opentofu/addrs/provider_test.go
Kristin Laemmert 041f4dd8ca
configs: require normalized provider local names (#24945)
* addrs: replace NewLegacyProvider with NewDefaultProvider in ParseProviderSourceString

ParseProviderSourceString was still defaulting to NewLegacyProvider when
encountering single-part strings. This has been fixed.

This commit also adds a new function, IsProviderPartNormalized, which
returns a bool indicating if the string given is the same as a
normalized version (as normalized by ParseProviderPart) or an error.
This is intended for use by the configs package when decoding provider
configurations.

* terraform: fix provider local names in tests

* configs: validate that all provider names are normalized

The addrs package normalizes all source strings, but not the local
names. This caused very odd behavior if for e.g. a provider local name
was capitalized in one place and not another. We considered enabling
case-sensitivity for provider local names, but decided that since this
was not something that worked in previous versions of terraform (and we
have yet to encounter any use cases for this feature) we could generate
an error if the provider local name is not normalized. This error also
provides instructions on how to fix it.

* configs: refactor decodeProviderRequirements to consistently not set an FQN when there are errors
2020-05-14 09:00:58 -04:00

516 lines
9.3 KiB
Go

package addrs
import (
"testing"
"github.com/go-test/deep"
svchost "github.com/hashicorp/terraform-svchost"
)
func TestProviderString(t *testing.T) {
tests := []struct {
Input Provider
Want string
}{
{
Provider{
Type: "test",
Hostname: DefaultRegistryHost,
Namespace: "hashicorp",
},
NewDefaultProvider("test").String(),
},
{
Provider{
Type: "test-beta",
Hostname: DefaultRegistryHost,
Namespace: "hashicorp",
},
NewDefaultProvider("test-beta").String(),
},
{
Provider{
Type: "test",
Hostname: "registry.terraform.com",
Namespace: "hashicorp",
},
"registry.terraform.com/hashicorp/test",
},
{
Provider{
Type: "test",
Hostname: DefaultRegistryHost,
Namespace: "othercorp",
},
DefaultRegistryHost.ForDisplay() + "/othercorp/test",
},
}
for _, test := range tests {
got := test.Input.String()
if got != test.Want {
t.Errorf("wrong result for %s\n", test.Input.String())
}
}
}
func TestProviderDisplay(t *testing.T) {
tests := []struct {
Input Provider
Want string
}{
{
Provider{
Type: "test",
Hostname: DefaultRegistryHost,
Namespace: "hashicorp",
},
"hashicorp/test",
},
{
Provider{
Type: "test",
Hostname: "registry.terraform.com",
Namespace: "hashicorp",
},
"registry.terraform.com/hashicorp/test",
},
{
Provider{
Type: "test",
Hostname: DefaultRegistryHost,
Namespace: "othercorp",
},
"othercorp/test",
},
}
for _, test := range tests {
got := test.Input.ForDisplay()
if got != test.Want {
t.Errorf("wrong result for %s\n", test.Input.String())
}
}
}
func TestProviderIsDefault(t *testing.T) {
tests := []struct {
Input Provider
Want bool
}{
{
Provider{
Type: "test",
Hostname: DefaultRegistryHost,
Namespace: "hashicorp",
},
true,
},
{
Provider{
Type: "test",
Hostname: "registry.terraform.com",
Namespace: "hashicorp",
},
false,
},
{
Provider{
Type: "test",
Hostname: DefaultRegistryHost,
Namespace: "othercorp",
},
false,
},
}
for _, test := range tests {
got := test.Input.IsDefault()
if got != test.Want {
t.Errorf("wrong result for %s\n", test.Input.String())
}
}
}
func TestProviderIsBuiltIn(t *testing.T) {
tests := []struct {
Input Provider
Want bool
}{
{
Provider{
Type: "test",
Hostname: BuiltInProviderHost,
Namespace: BuiltInProviderNamespace,
},
true,
},
{
Provider{
Type: "terraform",
Hostname: BuiltInProviderHost,
Namespace: BuiltInProviderNamespace,
},
true,
},
{
Provider{
Type: "test",
Hostname: BuiltInProviderHost,
Namespace: "boop",
},
false,
},
{
Provider{
Type: "test",
Hostname: DefaultRegistryHost,
Namespace: BuiltInProviderNamespace,
},
false,
},
{
Provider{
Type: "test",
Hostname: DefaultRegistryHost,
Namespace: "hashicorp",
},
false,
},
{
Provider{
Type: "test",
Hostname: "registry.terraform.com",
Namespace: "hashicorp",
},
false,
},
{
Provider{
Type: "test",
Hostname: DefaultRegistryHost,
Namespace: "othercorp",
},
false,
},
}
for _, test := range tests {
got := test.Input.IsBuiltIn()
if got != test.Want {
t.Errorf("wrong result for %s\ngot: %#v\nwant: %#v", test.Input.String(), got, test.Want)
}
}
}
func TestProviderIsLegacy(t *testing.T) {
tests := []struct {
Input Provider
Want bool
}{
{
Provider{
Type: "test",
Hostname: DefaultRegistryHost,
Namespace: LegacyProviderNamespace,
},
true,
},
{
Provider{
Type: "test",
Hostname: "registry.terraform.com",
Namespace: LegacyProviderNamespace,
},
false,
},
{
Provider{
Type: "test",
Hostname: DefaultRegistryHost,
Namespace: "hashicorp",
},
false,
},
}
for _, test := range tests {
got := test.Input.IsLegacy()
if got != test.Want {
t.Errorf("wrong result for %s\n", test.Input.String())
}
}
}
func TestParseProviderSourceStr(t *testing.T) {
tests := map[string]struct {
Want Provider
Err bool
}{
"registry.terraform.io/hashicorp/aws": {
Provider{
Type: "aws",
Namespace: "hashicorp",
Hostname: DefaultRegistryHost,
},
false,
},
"registry.Terraform.io/HashiCorp/AWS": {
Provider{
Type: "aws",
Namespace: "hashicorp",
Hostname: DefaultRegistryHost,
},
false,
},
"hashicorp/aws": {
Provider{
Type: "aws",
Namespace: "hashicorp",
Hostname: DefaultRegistryHost,
},
false,
},
"HashiCorp/AWS": {
Provider{
Type: "aws",
Namespace: "hashicorp",
Hostname: DefaultRegistryHost,
},
false,
},
"aws": {
Provider{
Type: "aws",
Namespace: "hashicorp",
Hostname: DefaultRegistryHost,
},
false,
},
"AWS": {
Provider{
Type: "aws",
Namespace: "hashicorp",
Hostname: DefaultRegistryHost,
},
false,
},
"example.com/foo-bar/baz-boop": {
Provider{
Type: "baz-boop",
Namespace: "foo-bar",
Hostname: svchost.Hostname("example.com"),
},
false,
},
"foo-bar/baz-boop": {
Provider{
Type: "baz-boop",
Namespace: "foo-bar",
Hostname: DefaultRegistryHost,
},
false,
},
"localhost:8080/foo/bar": {
Provider{
Type: "bar",
Namespace: "foo",
Hostname: svchost.Hostname("localhost:8080"),
},
false,
},
"example.com/too/many/parts/here": {
Provider{},
true,
},
"/too///many//slashes": {
Provider{},
true,
},
"///": {
Provider{},
true,
},
"/ / /": { // empty strings
Provider{},
true,
},
"badhost!/hashicorp/aws": {
Provider{},
true,
},
"example.com/badnamespace!/aws": {
Provider{},
true,
},
"example.com/bad--namespace/aws": {
Provider{},
true,
},
"example.com/-badnamespace/aws": {
Provider{},
true,
},
"example.com/badnamespace-/aws": {
Provider{},
true,
},
"example.com/bad.namespace/aws": {
Provider{},
true,
},
"example.com/hashicorp/badtype!": {
Provider{},
true,
},
"example.com/hashicorp/bad--type": {
Provider{},
true,
},
"example.com/hashicorp/-badtype": {
Provider{},
true,
},
"example.com/hashicorp/badtype-": {
Provider{},
true,
},
"example.com/hashicorp/bad.type": {
Provider{},
true,
},
}
for name, test := range tests {
got, diags := ParseProviderSourceString(name)
for _, problem := range deep.Equal(got, test.Want) {
t.Errorf(problem)
}
if len(diags) > 0 {
if test.Err == false {
t.Errorf("got error, expected success")
}
} else {
if test.Err {
t.Errorf("got success, expected error")
}
}
}
}
func TestParseProviderPart(t *testing.T) {
tests := map[string]struct {
Want string
Error string
}{
`foo`: {
`foo`,
``,
},
`FOO`: {
`foo`,
``,
},
`Foo`: {
`foo`,
``,
},
`abc-123`: {
`abc-123`,
``,
},
`Испытание`: {
`испытание`,
``,
},
`münchen`: { // this is a precomposed u with diaeresis
`münchen`, // this is a precomposed u with diaeresis
``,
},
`münchen`: { // this is a separate u and combining diaeresis
`münchen`, // this is a precomposed u with diaeresis
``,
},
`abc--123`: {
``,
`cannot use multiple consecutive dashes`,
},
`xn--80akhbyknj4f`: { // this is the punycode form of "испытание", but we don't accept punycode here
``,
`cannot use multiple consecutive dashes`,
},
`abc.123`: {
``,
`dots are not allowed`,
},
`-abc123`: {
``,
`must contain only letters, digits, and dashes, and may not use leading or trailing dashes`,
},
`abc123-`: {
``,
`must contain only letters, digits, and dashes, and may not use leading or trailing dashes`,
},
``: {
``,
`must have at least one character`,
},
}
for given, test := range tests {
t.Run(given, func(t *testing.T) {
got, err := ParseProviderPart(given)
if test.Error != "" {
if err == nil {
t.Errorf("unexpected success\ngot: %s\nwant: %s", err, test.Error)
} else if got := err.Error(); got != test.Error {
t.Errorf("wrong error\ngot: %s\nwant: %s", got, test.Error)
}
} else {
if err != nil {
t.Errorf("unexpected error\ngot: %s\nwant: <nil>", err)
} else if got != test.Want {
t.Errorf("wrong result\ngot: %s\nwant: %s", got, test.Want)
}
}
})
}
}
func TestProviderEquals(t *testing.T) {
tests := []struct {
InputP Provider
OtherP Provider
Want bool
}{
{
NewProvider(DefaultRegistryHost, "foo", "test"),
NewProvider(DefaultRegistryHost, "foo", "test"),
true,
},
{
NewProvider(DefaultRegistryHost, "foo", "test"),
NewProvider(DefaultRegistryHost, "bar", "test"),
false,
},
{
NewProvider(DefaultRegistryHost, "foo", "test"),
NewProvider(DefaultRegistryHost, "foo", "my-test"),
false,
},
{
NewProvider(DefaultRegistryHost, "foo", "test"),
NewProvider("example.com", "foo", "test"),
false,
},
}
for _, test := range tests {
t.Run(test.InputP.String(), func(t *testing.T) {
got := test.InputP.Equals(test.OtherP)
if got != test.Want {
t.Errorf("wrong result\ngot: %v\nwant: %v", got, test.Want)
}
})
}
}