opentofu/internal/getproviders/multi_source_test.go
Martin Atkins b9a93a0fe7 Move addrs/ to internal/addrs/
This is part of a general effort to move all of Terraform's non-library
package surface under internal in order to reinforce that these are for
internal use within Terraform only.

If you were previously importing packages under this prefix into an
external codebase, you could pin to an earlier release tag as an interim
solution until you've make a plan to achieve the same functionality some
other way.
2021-05-17 14:09:07 -07:00

550 lines
15 KiB
Go

package getproviders
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform/internal/addrs"
)
func TestMultiSourceAvailableVersions(t *testing.T) {
platform1 := Platform{OS: "amigaos", Arch: "m68k"}
platform2 := Platform{OS: "aros", Arch: "arm"}
t.Run("unfiltered merging", func(t *testing.T) {
s1 := NewMockSource([]PackageMeta{
FakePackageMeta(
addrs.NewDefaultProvider("foo"),
MustParseVersion("1.0.0"),
VersionList{MustParseVersion("5.0")},
platform1,
),
FakePackageMeta(
addrs.NewDefaultProvider("foo"),
MustParseVersion("1.0.0"),
VersionList{MustParseVersion("5.0")},
platform2,
),
FakePackageMeta(
addrs.NewDefaultProvider("bar"),
MustParseVersion("1.0.0"),
VersionList{MustParseVersion("5.0")},
platform2,
),
},
nil,
)
s2 := NewMockSource([]PackageMeta{
FakePackageMeta(
addrs.NewDefaultProvider("foo"),
MustParseVersion("1.0.0"),
VersionList{MustParseVersion("5.0")},
platform1,
),
FakePackageMeta(
addrs.NewDefaultProvider("foo"),
MustParseVersion("1.2.0"),
VersionList{MustParseVersion("5.0")},
platform1,
),
FakePackageMeta(
addrs.NewDefaultProvider("bar"),
MustParseVersion("1.0.0"),
VersionList{MustParseVersion("5.0")},
platform1,
),
},
nil,
)
multi := MultiSource{
{Source: s1},
{Source: s2},
}
// AvailableVersions produces the union of all versions available
// across all of the sources.
got, _, err := multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("foo"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
want := VersionList{
MustParseVersion("1.0.0"),
MustParseVersion("1.2.0"),
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong result\n%s", diff)
}
_, _, err = multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("baz"))
if want, ok := err.(ErrRegistryProviderNotKnown); !ok {
t.Fatalf("wrong error type:\ngot: %T\nwant: %T", err, want)
}
})
t.Run("merging with filters", func(t *testing.T) {
// This is just testing that filters are being honored at all, using a
// specific pair of filters. The different filter combinations
// themselves are tested in TestMultiSourceSelector.
s1 := NewMockSource([]PackageMeta{
FakePackageMeta(
addrs.NewDefaultProvider("foo"),
MustParseVersion("1.0.0"),
VersionList{MustParseVersion("5.0")},
platform1,
),
FakePackageMeta(
addrs.NewDefaultProvider("bar"),
MustParseVersion("1.0.0"),
VersionList{MustParseVersion("5.0")},
platform1,
),
},
nil,
)
s2 := NewMockSource([]PackageMeta{
FakePackageMeta(
addrs.NewDefaultProvider("foo"),
MustParseVersion("1.2.0"),
VersionList{MustParseVersion("5.0")},
platform1,
),
FakePackageMeta(
addrs.NewDefaultProvider("bar"),
MustParseVersion("1.2.0"),
VersionList{MustParseVersion("5.0")},
platform1,
),
},
nil,
)
multi := MultiSource{
{
Source: s1,
Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
},
{
Source: s2,
Include: mustParseMultiSourceMatchingPatterns("hashicorp/bar"),
},
}
got, _, err := multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("foo"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
want := VersionList{
MustParseVersion("1.0.0"),
// 1.2.0 isn't present because s3 doesn't include "foo"
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong result\n%s", diff)
}
got, _, err = multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("bar"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
want = VersionList{
MustParseVersion("1.0.0"),
MustParseVersion("1.2.0"), // included because s2 matches "bar"
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong result\n%s", diff)
}
_, _, err = multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("baz"))
if want, ok := err.(ErrRegistryProviderNotKnown); !ok {
t.Fatalf("wrong error type:\ngot: %T\nwant: %T", err, want)
}
})
t.Run("provider not found", func(t *testing.T) {
s1 := NewMockSource(nil, nil)
s2 := NewMockSource(nil, nil)
multi := MultiSource{
{Source: s1},
{Source: s2},
}
_, _, err := multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("foo"))
if err == nil {
t.Fatal("expected error, got success")
}
wantErr := `provider registry registry.terraform.io does not have a provider named registry.terraform.io/hashicorp/foo`
if err.Error() != wantErr {
t.Fatalf("wrong error.\ngot: %s\nwant: %s\n", err, wantErr)
}
})
t.Run("merging with warnings", func(t *testing.T) {
platform1 := Platform{OS: "amigaos", Arch: "m68k"}
platform2 := Platform{OS: "aros", Arch: "arm"}
s1 := NewMockSource([]PackageMeta{
FakePackageMeta(
addrs.NewDefaultProvider("bar"),
MustParseVersion("1.0.0"),
VersionList{MustParseVersion("5.0")},
platform2,
),
},
map[addrs.Provider]Warnings{
addrs.NewDefaultProvider("bar"): {"WARNING!"},
},
)
s2 := NewMockSource([]PackageMeta{
FakePackageMeta(
addrs.NewDefaultProvider("bar"),
MustParseVersion("1.0.0"),
VersionList{MustParseVersion("5.0")},
platform1,
),
},
nil,
)
multi := MultiSource{
{Source: s1},
{Source: s2},
}
// AvailableVersions produces the union of all versions available
// across all of the sources.
got, warns, err := multi.AvailableVersions(context.Background(), addrs.NewDefaultProvider("bar"))
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
want := VersionList{
MustParseVersion("1.0.0"),
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong result\n%s", diff)
}
if len(warns) != 1 {
t.Fatalf("wrong number of warnings. Got %d, wanted 1", len(warns))
}
if warns[0] != "WARNING!" {
t.Fatalf("wrong warnings. Got %s, wanted \"WARNING!\"", warns[0])
}
})
}
func TestMultiSourcePackageMeta(t *testing.T) {
platform1 := Platform{OS: "amigaos", Arch: "m68k"}
platform2 := Platform{OS: "aros", Arch: "arm"}
// We'll use the Filename field of the fake PackageMetas we created above
// to create a difference between the packages in s1 and the ones in s2,
// so we can test where individual packages came from below.
fakeFilename := func(fn string, meta PackageMeta) PackageMeta {
meta.Filename = fn
return meta
}
onlyInS1 := fakeFilename("s1", FakePackageMeta(
addrs.NewDefaultProvider("foo"),
MustParseVersion("1.0.0"),
VersionList{MustParseVersion("5.0")},
platform2,
))
onlyInS2 := fakeFilename("s2", FakePackageMeta(
addrs.NewDefaultProvider("foo"),
MustParseVersion("1.2.0"),
VersionList{MustParseVersion("5.0")},
platform1,
))
inBothS1 := fakeFilename("s1", FakePackageMeta(
addrs.NewDefaultProvider("foo"),
MustParseVersion("1.0.0"),
VersionList{MustParseVersion("5.0")},
platform1,
))
inBothS2 := fakeFilename("s2", inBothS1)
s1 := NewMockSource([]PackageMeta{
inBothS1,
onlyInS1,
fakeFilename("s1", FakePackageMeta(
addrs.NewDefaultProvider("bar"),
MustParseVersion("1.0.0"),
VersionList{MustParseVersion("5.0")},
platform2,
)),
},
nil,
)
s2 := NewMockSource([]PackageMeta{
inBothS2,
onlyInS2,
fakeFilename("s2", FakePackageMeta(
addrs.NewDefaultProvider("bar"),
MustParseVersion("1.0.0"),
VersionList{MustParseVersion("5.0")},
platform1,
)),
}, nil)
multi := MultiSource{
{Source: s1},
{Source: s2},
}
t.Run("only in s1", func(t *testing.T) {
got, err := multi.PackageMeta(
context.Background(),
addrs.NewDefaultProvider("foo"),
MustParseVersion("1.0.0"),
platform2,
)
want := onlyInS1
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong result\n%s", diff)
}
})
t.Run("only in s2", func(t *testing.T) {
got, err := multi.PackageMeta(
context.Background(),
addrs.NewDefaultProvider("foo"),
MustParseVersion("1.2.0"),
platform1,
)
want := onlyInS2
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong result\n%s", diff)
}
})
t.Run("in both", func(t *testing.T) {
got, err := multi.PackageMeta(
context.Background(),
addrs.NewDefaultProvider("foo"),
MustParseVersion("1.0.0"),
platform1,
)
want := inBothS1 // S1 "wins" because it's earlier in the MultiSource
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong result\n%s", diff)
}
// Make sure inBothS1 and inBothS2 really are different; if not then
// that's a test bug which we'd rather catch than have this test
// accidentally passing without actually checking anything.
if diff := cmp.Diff(inBothS1, inBothS2); diff == "" {
t.Fatalf("test bug: inBothS1 and inBothS2 are indistinguishable")
}
})
t.Run("in neither", func(t *testing.T) {
_, err := multi.PackageMeta(
context.Background(),
addrs.NewDefaultProvider("nonexist"),
MustParseVersion("1.0.0"),
platform1,
)
// This case reports "platform not supported" because it assumes that
// a caller would only pass to it package versions that were returned
// by a previousc all to AvailableVersions, and therefore a missing
// object ought to be valid provider/version but an unsupported
// platform.
if want, ok := err.(ErrPlatformNotSupported); !ok {
t.Fatalf("wrong error type:\ngot: %T\nwant: %T", err, want)
}
})
}
func TestMultiSourceSelector(t *testing.T) {
emptySource := NewMockSource(nil, nil)
tests := map[string]struct {
Selector MultiSourceSelector
Provider addrs.Provider
WantMatch bool
}{
"default provider with no constraints": {
MultiSourceSelector{
Source: emptySource,
},
addrs.NewDefaultProvider("foo"),
true,
},
"built-in provider with no constraints": {
MultiSourceSelector{
Source: emptySource,
},
addrs.NewBuiltInProvider("bar"),
true,
},
// Include constraints
"default provider with include constraint that matches it exactly": {
MultiSourceSelector{
Source: emptySource,
Include: mustParseMultiSourceMatchingPatterns("hashicorp/foo"),
},
addrs.NewDefaultProvider("foo"),
true,
},
"default provider with include constraint that matches it via type wildcard": {
MultiSourceSelector{
Source: emptySource,
Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
},
addrs.NewDefaultProvider("foo"),
true,
},
"default provider with include constraint that matches it via namespace wildcard": {
MultiSourceSelector{
Source: emptySource,
Include: mustParseMultiSourceMatchingPatterns("*/*"),
},
addrs.NewDefaultProvider("foo"),
true,
},
"default provider with non-normalized include constraint that matches it via type wildcard": {
MultiSourceSelector{
Source: emptySource,
Include: mustParseMultiSourceMatchingPatterns("HashiCorp/*"),
},
addrs.NewDefaultProvider("foo"),
true,
},
"built-in provider with exact include constraint that does not match it": {
MultiSourceSelector{
Source: emptySource,
Include: mustParseMultiSourceMatchingPatterns("hashicorp/foo"),
},
addrs.NewBuiltInProvider("bar"),
false,
},
"built-in provider with type-wild include constraint that does not match it": {
MultiSourceSelector{
Source: emptySource,
Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
},
addrs.NewBuiltInProvider("bar"),
false,
},
"built-in provider with namespace-wild include constraint that does not match it": {
MultiSourceSelector{
Source: emptySource,
Include: mustParseMultiSourceMatchingPatterns("*/*"),
},
// Doesn't match because builtin providers are in "terraform.io",
// but a pattern with no hostname is for registry.terraform.io.
addrs.NewBuiltInProvider("bar"),
false,
},
"built-in provider with include constraint that matches it via type wildcard": {
MultiSourceSelector{
Source: emptySource,
Include: mustParseMultiSourceMatchingPatterns("terraform.io/builtin/*"),
},
addrs.NewBuiltInProvider("bar"),
true,
},
// Exclude constraints
"default provider with exclude constraint that matches it exactly": {
MultiSourceSelector{
Source: emptySource,
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/foo"),
},
addrs.NewDefaultProvider("foo"),
false,
},
"default provider with exclude constraint that matches it via type wildcard": {
MultiSourceSelector{
Source: emptySource,
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
},
addrs.NewDefaultProvider("foo"),
false,
},
"default provider with exact exclude constraint that doesn't match it": {
MultiSourceSelector{
Source: emptySource,
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/bar"),
},
addrs.NewDefaultProvider("foo"),
true,
},
"default provider with non-normalized exclude constraint that matches it via type wildcard": {
MultiSourceSelector{
Source: emptySource,
Exclude: mustParseMultiSourceMatchingPatterns("HashiCorp/*"),
},
addrs.NewDefaultProvider("foo"),
false,
},
// Both include and exclude in a single selector
"default provider with exclude wildcard overriding include exact": {
MultiSourceSelector{
Source: emptySource,
Include: mustParseMultiSourceMatchingPatterns("hashicorp/foo"),
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
},
addrs.NewDefaultProvider("foo"),
false,
},
"default provider with exclude wildcard overriding irrelevant include exact": {
MultiSourceSelector{
Source: emptySource,
Include: mustParseMultiSourceMatchingPatterns("hashicorp/bar"),
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
},
addrs.NewDefaultProvider("foo"),
false,
},
"default provider with exclude exact overriding include wildcard": {
MultiSourceSelector{
Source: emptySource,
Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/foo"),
},
addrs.NewDefaultProvider("foo"),
false,
},
"default provider with irrelevant exclude exact overriding include wildcard": {
MultiSourceSelector{
Source: emptySource,
Include: mustParseMultiSourceMatchingPatterns("hashicorp/*"),
Exclude: mustParseMultiSourceMatchingPatterns("hashicorp/bar"),
},
addrs.NewDefaultProvider("foo"),
true,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
t.Logf("include: %s", test.Selector.Include)
t.Logf("exclude: %s", test.Selector.Exclude)
t.Logf("provider: %s", test.Provider)
got := test.Selector.CanHandleProvider(test.Provider)
want := test.WantMatch
if got != want {
t.Errorf("wrong result %t; want %t", got, want)
}
})
}
}
func mustParseMultiSourceMatchingPatterns(strs ...string) MultiSourceMatchingPatterns {
ret, err := ParseMultiSourceMatchingPatterns(strs)
if err != nil {
panic(err)
}
return ret
}