mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-19 17:59:05 -05:00
417 lines
14 KiB
Go
417 lines
14 KiB
Go
// Copyright (c) The OpenTofu Authors
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
// Copyright (c) 2023 HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package depsfile
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/opentofu/opentofu/internal/addrs"
|
|
"github.com/opentofu/opentofu/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)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestProviderLockContainsAll(t *testing.T) {
|
|
provider := addrs.NewDefaultProvider("provider")
|
|
v2 := getproviders.MustParseVersion("2.0.0")
|
|
v2EqConstraints := getproviders.MustParseVersionConstraints("2.0.0")
|
|
|
|
t.Run("non-symmetric", func(t *testing.T) {
|
|
target := NewProviderLock(provider, v2, v2EqConstraints, []getproviders.Hash{
|
|
"9r3i9a9QmASqMnQM",
|
|
"K43RHM2klOoywtyW",
|
|
"swJPXfuCNhJsTM5c",
|
|
})
|
|
|
|
original := NewProviderLock(provider, v2, v2EqConstraints, []getproviders.Hash{
|
|
"9r3i9a9QmASqMnQM",
|
|
"1ZAChGWUMWn4zmIk",
|
|
"K43RHM2klOoywtyW",
|
|
"HWjRvIuWZ1LVatnc",
|
|
"swJPXfuCNhJsTM5c",
|
|
"KwhJK4p/U2dqbKhI",
|
|
})
|
|
|
|
if !original.ContainsAll(target) {
|
|
t.Errorf("original should contain all hashes in target")
|
|
}
|
|
if target.ContainsAll(original) {
|
|
t.Errorf("target should not contain all hashes in original")
|
|
}
|
|
})
|
|
|
|
t.Run("symmetric", func(t *testing.T) {
|
|
target := NewProviderLock(provider, v2, v2EqConstraints, []getproviders.Hash{
|
|
"9r3i9a9QmASqMnQM",
|
|
"K43RHM2klOoywtyW",
|
|
"swJPXfuCNhJsTM5c",
|
|
})
|
|
|
|
original := NewProviderLock(provider, v2, v2EqConstraints, []getproviders.Hash{
|
|
"9r3i9a9QmASqMnQM",
|
|
"K43RHM2klOoywtyW",
|
|
"swJPXfuCNhJsTM5c",
|
|
})
|
|
|
|
if !original.ContainsAll(target) {
|
|
t.Errorf("original should contain all hashes in target")
|
|
}
|
|
if !target.ContainsAll(original) {
|
|
t.Errorf("target should not contain all hashes in original")
|
|
}
|
|
})
|
|
|
|
t.Run("edge case - null", func(t *testing.T) {
|
|
original := NewProviderLock(provider, v2, v2EqConstraints, []getproviders.Hash{
|
|
"9r3i9a9QmASqMnQM",
|
|
"K43RHM2klOoywtyW",
|
|
"swJPXfuCNhJsTM5c",
|
|
})
|
|
|
|
if !original.ContainsAll(nil) {
|
|
t.Fatalf("original should report true on nil")
|
|
}
|
|
})
|
|
|
|
t.Run("edge case - empty", func(t *testing.T) {
|
|
original := NewProviderLock(provider, v2, v2EqConstraints, []getproviders.Hash{
|
|
"9r3i9a9QmASqMnQM",
|
|
"K43RHM2klOoywtyW",
|
|
"swJPXfuCNhJsTM5c",
|
|
})
|
|
|
|
target := NewProviderLock(provider, v2, v2EqConstraints, []getproviders.Hash{})
|
|
|
|
if !original.ContainsAll(target) {
|
|
t.Fatalf("original should report true on empty")
|
|
}
|
|
})
|
|
|
|
t.Run("edge case - original empty", func(t *testing.T) {
|
|
original := NewProviderLock(provider, v2, v2EqConstraints, []getproviders.Hash{})
|
|
|
|
target := NewProviderLock(provider, v2, v2EqConstraints, []getproviders.Hash{
|
|
"9r3i9a9QmASqMnQM",
|
|
"K43RHM2klOoywtyW",
|
|
"swJPXfuCNhJsTM5c",
|
|
})
|
|
|
|
if original.ContainsAll(target) {
|
|
t.Fatalf("original should report false when empty")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestLocksUpgradeFromPredecessorProject(t *testing.T) {
|
|
locks, diags := LoadLocksFromBytes([]byte(`
|
|
provider "registry.terraform.io/hashicorp/a" {
|
|
version = "1.0.0"
|
|
constraints = ">= 1.0.0"
|
|
hashes = [
|
|
"h1:jsKjBiLb+v3OIC3xuDiY4sR0r1OHUMSWPYKult9MhT0=",
|
|
]
|
|
}
|
|
|
|
provider "registry.terraform.io/anything-else/b" {
|
|
version = "2.0.0"
|
|
hashes = [
|
|
"h1:jsKjBiLb+v3OIC3xuDiY4sR0r1OHUMSWPYKult9MhT0=",
|
|
]
|
|
}
|
|
|
|
provider "registry.terraform.io/hashicorp/c" {
|
|
version = "3.0.0"
|
|
hashes = [
|
|
"h1:jsKjBiLb+v3OIC3xuDiY4sR0r1OHUMSWPYKult9MhT0=",
|
|
]
|
|
}
|
|
provider "registry.opentofu.org/hashicorp/c" {
|
|
version = "4.0.0"
|
|
hashes = [
|
|
"h1:jsKjBiLb+v3OIC3xuDiY4sR0r1OHUMSWPYKult9T0Mh=",
|
|
]
|
|
}
|
|
`), "")
|
|
if diags.HasErrors() {
|
|
t.Fatalf("unexpected errors: %s", diags.Err().Error())
|
|
}
|
|
|
|
gotChanges := locks.UpgradeFromPredecessorProject()
|
|
wantChanges := map[addrs.Provider]addrs.Provider{
|
|
addrs.MustParseProviderSourceString("registry.terraform.io/hashicorp/a"): addrs.MustParseProviderSourceString("registry.opentofu.org/hashicorp/a"),
|
|
}
|
|
if diff := cmp.Diff(wantChanges, gotChanges); diff != "" {
|
|
t.Error("wrong reported changes\n" + diff)
|
|
}
|
|
|
|
gotLocks := locks.AllProviders()
|
|
wantLocks := map[addrs.Provider]*ProviderLock{
|
|
// This one is still included because we want to let the provider
|
|
// installer be the one to decide to remove it, once it's convinced
|
|
// itself that this provider is definitely no longer needed...
|
|
addrs.MustParseProviderSourceString("registry.terraform.io/hashicorp/a"): {
|
|
addr: addrs.MustParseProviderSourceString("registry.terraform.io/hashicorp/a"),
|
|
version: getproviders.MustParseVersion("1.0.0"),
|
|
versionConstraints: getproviders.MustParseVersionConstraints(">= 1.0.0"),
|
|
hashes: []getproviders.Hash{
|
|
getproviders.HashScheme1.New("jsKjBiLb+v3OIC3xuDiY4sR0r1OHUMSWPYKult9MhT0="),
|
|
},
|
|
},
|
|
|
|
// ...but we now have this new one that describes the OpenTofu equivalent
|
|
// of it, with the same version but not yet any hashes. The hashes
|
|
// must be determined by a subsequent installation step.
|
|
addrs.MustParseProviderSourceString("registry.opentofu.org/hashicorp/a"): {
|
|
addr: addrs.MustParseProviderSourceString("registry.opentofu.org/hashicorp/a"),
|
|
version: getproviders.MustParseVersion("1.0.0"),
|
|
versionConstraints: getproviders.MustParseVersionConstraints(">= 1.0.0"),
|
|
// intentionally no hashes here, but the version and versionConstraints
|
|
// must match the entry above.
|
|
},
|
|
|
|
// This one does not get any special treatment because it's not in
|
|
// the namespace where the OpenTofu project maintains
|
|
// directly-corresponding releases.
|
|
addrs.MustParseProviderSourceString("registry.terraform.io/anything-else/b"): {
|
|
addr: addrs.MustParseProviderSourceString("registry.terraform.io/anything-else/b"),
|
|
version: getproviders.MustParseVersion("2.0.0"),
|
|
hashes: []getproviders.Hash{
|
|
getproviders.HashScheme1.New("jsKjBiLb+v3OIC3xuDiY4sR0r1OHUMSWPYKult9MhT0="),
|
|
},
|
|
},
|
|
|
|
// The following two both survive unchanged because we don't want to
|
|
// destroy any existing lock file entry for a provider from the
|
|
// OpenTofu registry even if there's an entry that could potentially
|
|
// have been translated.
|
|
addrs.MustParseProviderSourceString("registry.terraform.io/hashicorp/c"): {
|
|
addr: addrs.MustParseProviderSourceString("registry.terraform.io/hashicorp/c"),
|
|
version: getproviders.MustParseVersion("3.0.0"),
|
|
hashes: []getproviders.Hash{
|
|
getproviders.HashScheme1.New("jsKjBiLb+v3OIC3xuDiY4sR0r1OHUMSWPYKult9MhT0="),
|
|
},
|
|
},
|
|
addrs.MustParseProviderSourceString("registry.opentofu.org/hashicorp/c"): {
|
|
addr: addrs.MustParseProviderSourceString("registry.opentofu.org/hashicorp/c"),
|
|
version: getproviders.MustParseVersion("4.0.0"),
|
|
hashes: []getproviders.Hash{
|
|
getproviders.HashScheme1.New("jsKjBiLb+v3OIC3xuDiY4sR0r1OHUMSWPYKult9T0Mh="),
|
|
},
|
|
},
|
|
}
|
|
if diff := cmp.Diff(wantLocks, gotLocks, ProviderLockComparer); diff != "" {
|
|
t.Error("wrong updated locks\n" + diff)
|
|
}
|
|
}
|