Files
opentf/internal/addrs/provider_config_test.go
Martin Atkins 5b5a285066 Replace github.com/go-test/deep with go-cmp
My original intention was just to reduce our number of dependencies by
standardizing on a single comparison library, but in the process of doing
so I found various examples of the kinds of problems that caused this
codebase to begin adopting go-cmp instead of go-test/deep in the first
place, which make it easy to accidentally write a false-positive test that
doesn't actually check what the author thinks is being checked:

- deep.Equal silently ignores unexported fields, so comparing two values
  that differ only in data in unexported fields succeeds even when it ought
  not to.

  TestContext2Apply_multiVarComprehensive in package tofu was an excellent
  example of this problem: it had various test assertions that were
  actually checking absolutely nothing, despite appearing to compare
  pairs of cty.Value.

- deep.Equal also silently ignores anything below a certain level of
  nesting, and so comparison of deep data structures can appear to succeed
  even though they don't actually match.

  There were a few examples where that problem had already been found and
  fixed by temporarily overriding the package deep global settings, but
  with go-cmp the default behavior already visits everything, or panics
  if it cannot.

This does mean that in a few cases this needed some more elaborate options
to cmp.Diff to align with the previous behavior, which is a little annoying
but overall I think better to be explicit about what each test is relying
on. Perhaps we can rework these tests to need fewer unusual cmp options
in future, but for this commit I want to keep focused on the smallest
possible changes to remove our dependency on github.com/go-test/deep .

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
2025-10-13 08:17:40 -07:00

318 lines
7.1 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 addrs
import (
"reflect"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
)
func TestParseAbsProviderConfigInstances(t *testing.T) {
tests := []struct {
Input string
Want AbsProviderConfig
WantKey InstanceKey
WantDiag string
}{
{
`provider["registry.opentofu.org/hashicorp/aws"]`,
AbsProviderConfig{
Module: RootModule,
Provider: Provider{
Type: "aws",
Namespace: "hashicorp",
Hostname: "registry.opentofu.org",
},
},
NoKey,
``,
},
{
`provider["registry.opentofu.org/hashicorp/aws"].foo`,
AbsProviderConfig{
Module: RootModule,
Provider: Provider{
Type: "aws",
Namespace: "hashicorp",
Hostname: "registry.opentofu.org",
},
Alias: "foo",
},
NoKey,
``,
},
{
`module.baz.provider["registry.opentofu.org/hashicorp/aws"]`,
AbsProviderConfig{
Module: Module{"baz"},
Provider: Provider{
Type: "aws",
Namespace: "hashicorp",
Hostname: "registry.opentofu.org",
},
},
NoKey,
``,
},
{
`module.baz.provider["registry.opentofu.org/hashicorp/aws"].foo`,
AbsProviderConfig{
Module: Module{"baz"},
Provider: Provider{
Type: "aws",
Namespace: "hashicorp",
Hostname: "registry.opentofu.org",
},
Alias: "foo",
},
NoKey,
``,
},
{
`module.baz.provider["registry.opentofu.org/hashicorp/aws"].foo["keystr"]`,
AbsProviderConfig{
Module: Module{"baz"},
Provider: Provider{
Type: "aws",
Namespace: "hashicorp",
Hostname: "registry.opentofu.org",
},
Alias: "foo",
},
StringKey("keystr"),
``,
},
{
`module.baz["foo"].provider["registry.opentofu.org/hashicorp/aws"]`,
AbsProviderConfig{},
NoKey,
`A provider configuration must not appear in a module instance that uses count or for_each.`,
},
{
`module.baz[1].provider["registry.opentofu.org/hashicorp/aws"]`,
AbsProviderConfig{},
NoKey,
`A provider configuration must not appear in a module instance that uses count or for_each.`,
},
{
`module.baz[1].module.bar.provider["registry.opentofu.org/hashicorp/aws"]`,
AbsProviderConfig{},
NoKey,
`A provider configuration must not appear in a module instance that uses count or for_each.`,
},
{
`aws`,
AbsProviderConfig{},
NoKey,
`Provider address must begin with "provider.", followed by a provider type name.`,
},
{
`aws.foo`,
AbsProviderConfig{},
NoKey,
`Provider address must begin with "provider.", followed by a provider type name.`,
},
{
`provider`,
AbsProviderConfig{},
NoKey,
`Provider address must begin with "provider.", followed by a provider type name.`,
},
{
`provider.aws.foo["bar"].baz`,
AbsProviderConfig{},
NoKey,
`Extraneous operators after provider configuration reference.`,
},
{
`provider["aws"]["foo"]`,
AbsProviderConfig{},
NoKey,
`Provider type name must be followed by a configuration alias name.`,
},
{
`module.foo`,
AbsProviderConfig{},
NoKey,
`Provider address must begin with "provider.", followed by a provider type name.`,
},
{
`provider[0]`,
AbsProviderConfig{},
NoKey,
`The prefix "provider." must be followed by a provider type name.`,
},
}
for _, test := range tests {
t.Run(test.Input, func(t *testing.T) {
traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(test.Input), "", hcl.Pos{})
if len(parseDiags) != 0 {
t.Errorf("unexpected diagnostics during parse")
for _, diag := range parseDiags {
t.Logf("- %s", diag)
}
return
}
got, key, diags := ParseAbsProviderConfigInstance(traversal)
if test.WantDiag != "" {
if len(diags) != 1 {
t.Fatalf("got %d diagnostics; want 1", len(diags))
}
gotDetail := diags[0].Description().Detail
if gotDetail != test.WantDiag {
t.Fatalf("wrong diagnostic detail\ngot: %s\nwant: %s", gotDetail, test.WantDiag)
}
return
} else {
if len(diags) != 0 {
t.Fatalf("got %d diagnostics; want 0", len(diags))
}
}
if diff := cmp.Diff(test.Want, got, CmpOptionsForTesting); diff != "" {
t.Error("wrong result:\n" + diff)
}
if test.WantKey != key {
t.Errorf("Wanted key %s, got key %s", test.WantKey, key)
}
})
}
}
func TestAbsProviderConfigString(t *testing.T) {
tests := []struct {
Config AbsProviderConfig
Want string
}{
{
AbsProviderConfig{
Module: RootModule,
Provider: NewLegacyProvider("foo"),
},
`provider["registry.opentofu.org/-/foo"]`,
},
{
AbsProviderConfig{
Module: RootModule.Child("child_module"),
Provider: NewDefaultProvider("foo"),
},
`module.child_module.provider["registry.opentofu.org/hashicorp/foo"]`,
},
{
AbsProviderConfig{
Module: RootModule,
Alias: "bar",
Provider: NewDefaultProvider("foo"),
},
`provider["registry.opentofu.org/hashicorp/foo"].bar`,
},
{
AbsProviderConfig{
Module: RootModule.Child("child_module"),
Alias: "bar",
Provider: NewDefaultProvider("foo"),
},
`module.child_module.provider["registry.opentofu.org/hashicorp/foo"].bar`,
},
}
for _, test := range tests {
got := test.Config.String()
if got != test.Want {
t.Errorf("wrong result. Got %s, want %s\n", got, test.Want)
}
}
}
func TestAbsProviderConfigLegacyString(t *testing.T) {
tests := []struct {
Config AbsProviderConfig
Want string
}{
{
AbsProviderConfig{
Module: RootModule,
Provider: NewLegacyProvider("foo"),
},
`provider.foo`,
},
{
AbsProviderConfig{
Module: RootModule.Child("child_module"),
Provider: NewLegacyProvider("foo"),
},
`module.child_module.provider.foo`,
},
{
AbsProviderConfig{
Module: RootModule,
Alias: "bar",
Provider: NewLegacyProvider("foo"),
},
`provider.foo.bar`,
},
{
AbsProviderConfig{
Module: RootModule.Child("child_module"),
Alias: "bar",
Provider: NewLegacyProvider("foo"),
},
`module.child_module.provider.foo.bar`,
},
}
for _, test := range tests {
got := test.Config.LegacyString()
if got != test.Want {
t.Errorf("wrong result. Got %s, want %s\n", got, test.Want)
}
}
}
func TestParseLegacyAbsProviderConfigStr(t *testing.T) {
tests := []struct {
Config string
Want AbsProviderConfig
}{
{
`provider.foo`,
AbsProviderConfig{
Module: RootModule,
Provider: NewLegacyProvider("foo"),
},
},
{
`module.child_module.provider.foo`,
AbsProviderConfig{
Module: RootModule.Child("child_module"),
Provider: NewLegacyProvider("foo"),
},
},
{
`provider.terraform`,
AbsProviderConfig{
Module: RootModule,
Provider: NewBuiltInProvider("terraform"),
},
},
}
for _, test := range tests {
got, _ := ParseLegacyAbsProviderConfigStr(test.Config)
if !reflect.DeepEqual(got, test.Want) {
t.Errorf("wrong result. Got %s, want %s\n", got, test.Want)
}
}
}