550 lines
15 KiB
Go
550 lines
15 KiB
Go
package getproviders
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/hashicorp/terraform/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
|
|
}
|