opentofu/internal/depsfile/locks_test.go
Martin Atkins 23395a1022 providercache: Discard lock entries for unused providers
Previously we would only ever add new lock entries or update existing
ones. However, it's possible that over time a module may _cease_ using
a particular provider, at which point we ought to remove it from the lock
file so that operations won't fail when seeing that the provider cache
directory is inconsistent with the lock file.

Now the provider installer (EnsureProviderVersions) will remove any lock
file entries that relate to providers not included in the given
requirements, which therefore makes the resulting lock file properly match
the set of packages the installer wrote into the cache.

This does potentially mean that someone could inadvertently defeat the
lock by removing a provider dependency, running "terraform init", then
undoing that removal, and finally running "terraform init" again. However,
that seems relatively unlikely compared to the likelihood of removing
a provider and keeping it removed, and in the event it _did_ happen the
changes to the lock entry for that provider would be visible in the diff
of the provider lock file as usual, and so could be noticed in code
review just as for any other change to dependencies.
2021-12-17 15:30:21 -08:00

219 lines
6.9 KiB
Go

package depsfile
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/getproviders"
)
func TestLocksEqual(t *testing.T) {
boopProvider := addrs.NewDefaultProvider("boop")
v2 := getproviders.MustParseVersion("2.0.0")
v2LocalBuild := getproviders.MustParseVersion("2.0.0+awesomecorp.1")
v2GtConstraints := getproviders.MustParseVersionConstraints(">= 2.0.0")
v2EqConstraints := getproviders.MustParseVersionConstraints("2.0.0")
hash1 := getproviders.HashScheme("test").New("1")
hash2 := getproviders.HashScheme("test").New("2")
hash3 := getproviders.HashScheme("test").New("3")
equalBothWays := func(t *testing.T, a, b *Locks) {
t.Helper()
if !a.Equal(b) {
t.Errorf("a should be equal to b")
}
if !b.Equal(a) {
t.Errorf("b should be equal to a")
}
}
nonEqualBothWays := func(t *testing.T, a, b *Locks) {
t.Helper()
if a.Equal(b) {
t.Errorf("a should be equal to b")
}
if b.Equal(a) {
t.Errorf("b should be equal to a")
}
}
t.Run("both empty", func(t *testing.T) {
a := NewLocks()
b := NewLocks()
equalBothWays(t, a, b)
})
t.Run("an extra provider lock", func(t *testing.T) {
a := NewLocks()
b := NewLocks()
b.SetProvider(boopProvider, v2, v2GtConstraints, nil)
nonEqualBothWays(t, a, b)
})
t.Run("both have boop provider with same version", func(t *testing.T) {
a := NewLocks()
b := NewLocks()
// Note: the constraints are not part of the definition of "Equal", so they can differ
a.SetProvider(boopProvider, v2, v2GtConstraints, nil)
b.SetProvider(boopProvider, v2, v2EqConstraints, nil)
equalBothWays(t, a, b)
})
t.Run("both have boop provider with different versions", func(t *testing.T) {
a := NewLocks()
b := NewLocks()
a.SetProvider(boopProvider, v2, v2EqConstraints, nil)
b.SetProvider(boopProvider, v2LocalBuild, v2EqConstraints, nil)
nonEqualBothWays(t, a, b)
})
t.Run("both have boop provider with same version and same hashes", func(t *testing.T) {
a := NewLocks()
b := NewLocks()
hashes := []getproviders.Hash{hash1, hash2, hash3}
a.SetProvider(boopProvider, v2, v2EqConstraints, hashes)
b.SetProvider(boopProvider, v2, v2EqConstraints, hashes)
equalBothWays(t, a, b)
})
t.Run("both have boop provider with same version but different hashes", func(t *testing.T) {
a := NewLocks()
b := NewLocks()
hashesA := []getproviders.Hash{hash1, hash2}
hashesB := []getproviders.Hash{hash1, hash3}
a.SetProvider(boopProvider, v2, v2EqConstraints, hashesA)
b.SetProvider(boopProvider, v2, v2EqConstraints, hashesB)
nonEqualBothWays(t, a, b)
})
}
func TestLocksEqualProviderAddress(t *testing.T) {
boopProvider := addrs.NewDefaultProvider("boop")
v2 := getproviders.MustParseVersion("2.0.0")
v2LocalBuild := getproviders.MustParseVersion("2.0.0+awesomecorp.1")
v2GtConstraints := getproviders.MustParseVersionConstraints(">= 2.0.0")
v2EqConstraints := getproviders.MustParseVersionConstraints("2.0.0")
hash1 := getproviders.HashScheme("test").New("1")
hash2 := getproviders.HashScheme("test").New("2")
hash3 := getproviders.HashScheme("test").New("3")
equalProviderAddressBothWays := func(t *testing.T, a, b *Locks) {
t.Helper()
if !a.EqualProviderAddress(b) {
t.Errorf("a should be equal to b")
}
if !b.EqualProviderAddress(a) {
t.Errorf("b should be equal to a")
}
}
nonEqualProviderAddressBothWays := func(t *testing.T, a, b *Locks) {
t.Helper()
if a.EqualProviderAddress(b) {
t.Errorf("a should be equal to b")
}
if b.EqualProviderAddress(a) {
t.Errorf("b should be equal to a")
}
}
t.Run("both empty", func(t *testing.T) {
a := NewLocks()
b := NewLocks()
equalProviderAddressBothWays(t, a, b)
})
t.Run("an extra provider lock", func(t *testing.T) {
a := NewLocks()
b := NewLocks()
b.SetProvider(boopProvider, v2, v2GtConstraints, nil)
nonEqualProviderAddressBothWays(t, a, b)
})
t.Run("both have boop provider with different versions", func(t *testing.T) {
a := NewLocks()
b := NewLocks()
a.SetProvider(boopProvider, v2, v2EqConstraints, nil)
b.SetProvider(boopProvider, v2LocalBuild, v2EqConstraints, nil)
equalProviderAddressBothWays(t, a, b)
})
t.Run("both have boop provider with same version but different hashes", func(t *testing.T) {
a := NewLocks()
b := NewLocks()
hashesA := []getproviders.Hash{hash1, hash2}
hashesB := []getproviders.Hash{hash1, hash3}
a.SetProvider(boopProvider, v2, v2EqConstraints, hashesA)
b.SetProvider(boopProvider, v2, v2EqConstraints, hashesB)
equalProviderAddressBothWays(t, a, b)
})
}
func TestLocksProviderSetRemove(t *testing.T) {
beepProvider := addrs.NewDefaultProvider("beep")
boopProvider := addrs.NewDefaultProvider("boop")
v2 := getproviders.MustParseVersion("2.0.0")
v2EqConstraints := getproviders.MustParseVersionConstraints("2.0.0")
v2GtConstraints := getproviders.MustParseVersionConstraints(">= 2.0.0")
hash := getproviders.HashScheme("test").New("1")
locks := NewLocks()
if got, want := len(locks.AllProviders()), 0; got != want {
t.Fatalf("fresh locks object already has providers")
}
locks.SetProvider(boopProvider, v2, v2EqConstraints, []getproviders.Hash{hash})
{
got := locks.AllProviders()
want := map[addrs.Provider]*ProviderLock{
boopProvider: {
addr: boopProvider,
version: v2,
versionConstraints: v2EqConstraints,
hashes: []getproviders.Hash{hash},
},
}
if diff := cmp.Diff(want, got, ProviderLockComparer); diff != "" {
t.Fatalf("wrong providers after SetProvider boop\n%s", diff)
}
}
locks.SetProvider(beepProvider, v2, v2GtConstraints, []getproviders.Hash{hash})
{
got := locks.AllProviders()
want := map[addrs.Provider]*ProviderLock{
boopProvider: {
addr: boopProvider,
version: v2,
versionConstraints: v2EqConstraints,
hashes: []getproviders.Hash{hash},
},
beepProvider: {
addr: beepProvider,
version: v2,
versionConstraints: v2GtConstraints,
hashes: []getproviders.Hash{hash},
},
}
if diff := cmp.Diff(want, got, ProviderLockComparer); diff != "" {
t.Fatalf("wrong providers after SetProvider beep\n%s", diff)
}
}
locks.RemoveProvider(boopProvider)
{
got := locks.AllProviders()
want := map[addrs.Provider]*ProviderLock{
beepProvider: {
addr: beepProvider,
version: v2,
versionConstraints: v2GtConstraints,
hashes: []getproviders.Hash{hash},
},
}
if diff := cmp.Diff(want, got, ProviderLockComparer); diff != "" {
t.Fatalf("wrong providers after RemoveProvider boop\n%s", diff)
}
}
locks.RemoveProvider(beepProvider)
{
got := locks.AllProviders()
want := map[addrs.Provider]*ProviderLock{}
if diff := cmp.Diff(want, got, ProviderLockComparer); diff != "" {
t.Fatalf("wrong providers after RemoveProvider beep\n%s", diff)
}
}
}