mirror of
https://github.com/hashicorp/terraform.git
synced 2026-03-21 18:10:30 -04:00
407 lines
9.2 KiB
Go
407 lines
9.2 KiB
Go
// Copyright IBM Corp. 2014, 2026
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package addrs
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
func TestPartialExpandedResourceIsTargetedBy(t *testing.T) {
|
|
|
|
tcs := []struct {
|
|
per string
|
|
target string
|
|
want bool
|
|
}{
|
|
{
|
|
"test.a",
|
|
"test.a",
|
|
true,
|
|
},
|
|
{
|
|
"test.a",
|
|
"test.a[0]",
|
|
true,
|
|
},
|
|
{
|
|
"test.a[*]",
|
|
"test.a",
|
|
true,
|
|
},
|
|
{
|
|
"test.a[*]",
|
|
"test.a[0]",
|
|
true,
|
|
},
|
|
{
|
|
"test.a[*]",
|
|
"test.a[\"key\"]",
|
|
true,
|
|
},
|
|
{
|
|
"module.mod.test.a",
|
|
"module.mod.test.a",
|
|
true,
|
|
},
|
|
{
|
|
"module.mod[1].test.a",
|
|
"module.mod[0].test.a",
|
|
false,
|
|
},
|
|
{
|
|
"module.mod.test.a[*]",
|
|
"module.mod.test.a",
|
|
true,
|
|
},
|
|
{
|
|
"module.mod.test.a[*]",
|
|
"module.mod.test.a[0]",
|
|
true,
|
|
},
|
|
{
|
|
"module.mod.test.a[*]",
|
|
"module.mod.test.a[\"key\"]",
|
|
true,
|
|
},
|
|
{
|
|
"module.mod.test.a[*]",
|
|
"module.mod[0].test.a",
|
|
false,
|
|
},
|
|
{
|
|
"module.mod[1].test.a[*]",
|
|
"module.mod[\"key\"].test.a[0]",
|
|
false,
|
|
},
|
|
{
|
|
"module.mod[*].test.a",
|
|
"module.mod.test.a",
|
|
true,
|
|
},
|
|
{
|
|
"module.mod[*].test.a",
|
|
"module.mod.test.a[0]",
|
|
true,
|
|
},
|
|
{
|
|
"module.mod[*].test.a",
|
|
"module.mod[0].test.a",
|
|
true,
|
|
},
|
|
{
|
|
"module.mod[*].test.a",
|
|
"module.mod[\"key\"].test.a",
|
|
true,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tcs {
|
|
t.Run(fmt.Sprintf("PartialResource(%q).IsTargetedBy(%q)", tc.per, tc.target), func(t *testing.T) {
|
|
per := mustParsePartialResourceInstanceStr(tc.per).PartialResource()
|
|
target := mustParseTarget(tc.target)
|
|
|
|
got := per.IsTargetedBy(target)
|
|
if got != tc.want {
|
|
t.Errorf("PartialResource(%q).IsTargetedBy(%q): got %v; want %v", tc.per, tc.target, got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func TestParsePartialExpandedModule(t *testing.T) {
|
|
|
|
// these functions are a bit weird, as the normal parsing supported by
|
|
// HCL can't put unknown values into the instance keys. So we need to
|
|
// build the traversals in the same way the thing that is calling these
|
|
// functions does.
|
|
|
|
tcs := []struct {
|
|
traversal func(t *testing.T) (string, hcl.Traversal)
|
|
want PartialExpandedModule
|
|
remain int
|
|
}{
|
|
{
|
|
traversal: func(t *testing.T) (string, hcl.Traversal) {
|
|
addr := "module.mod"
|
|
traversal, diags := hclsyntax.ParseTraversalAbs([]byte(addr), "", hcl.InitialPos)
|
|
if len(diags) > 0 {
|
|
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
}
|
|
return addr, traversal
|
|
},
|
|
want: PartialExpandedModule{
|
|
expandedPrefix: ModuleInstance{
|
|
{
|
|
Name: "mod",
|
|
},
|
|
},
|
|
},
|
|
remain: 0,
|
|
},
|
|
{
|
|
traversal: func(t *testing.T) (string, hcl.Traversal) {
|
|
addr := "module.mod[0]"
|
|
traversal, diags := hclsyntax.ParseTraversalAbs([]byte(addr), "", hcl.InitialPos)
|
|
if len(diags) > 0 {
|
|
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
}
|
|
// Hack the key into an unknown value.
|
|
traversal[2] = hcl.TraverseIndex{
|
|
Key: cty.UnknownVal(cty.Number),
|
|
}
|
|
return "module.mod[*]", traversal
|
|
},
|
|
want: PartialExpandedModule{
|
|
unexpandedSuffix: Module{
|
|
"mod",
|
|
},
|
|
},
|
|
remain: 0,
|
|
},
|
|
{
|
|
traversal: func(t *testing.T) (string, hcl.Traversal) {
|
|
addr := "module.child.module.grandchild"
|
|
traversal, diags := hclsyntax.ParseTraversalAbs([]byte(addr), "", hcl.InitialPos)
|
|
if len(diags) > 0 {
|
|
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
}
|
|
return addr, traversal
|
|
},
|
|
want: PartialExpandedModule{
|
|
expandedPrefix: ModuleInstance{
|
|
{
|
|
Name: "child",
|
|
},
|
|
{
|
|
Name: "grandchild",
|
|
},
|
|
},
|
|
},
|
|
remain: 0,
|
|
},
|
|
{
|
|
traversal: func(t *testing.T) (string, hcl.Traversal) {
|
|
addr := "module.child[0].module.grandchild"
|
|
traversal, diags := hclsyntax.ParseTraversalAbs([]byte(addr), "", hcl.InitialPos)
|
|
if len(diags) > 0 {
|
|
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
}
|
|
return addr, traversal
|
|
},
|
|
want: PartialExpandedModule{
|
|
expandedPrefix: ModuleInstance{
|
|
{
|
|
Name: "child",
|
|
InstanceKey: IntKey(0),
|
|
},
|
|
{
|
|
Name: "grandchild",
|
|
},
|
|
},
|
|
},
|
|
remain: 0,
|
|
},
|
|
{
|
|
traversal: func(t *testing.T) (string, hcl.Traversal) {
|
|
addr := "module.child[0].module.grandchild"
|
|
traversal, diags := hclsyntax.ParseTraversalAbs([]byte(addr), "", hcl.InitialPos)
|
|
if len(diags) > 0 {
|
|
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
}
|
|
traversal[2] = hcl.TraverseIndex{
|
|
Key: cty.UnknownVal(cty.Number),
|
|
}
|
|
return "module.child[*].module.grandchild", traversal
|
|
},
|
|
want: PartialExpandedModule{
|
|
unexpandedSuffix: Module{
|
|
"child",
|
|
"grandchild",
|
|
},
|
|
},
|
|
remain: 0,
|
|
},
|
|
{
|
|
traversal: func(t *testing.T) (string, hcl.Traversal) {
|
|
addr := "module.child.module.grandchild[0]"
|
|
traversal, diags := hclsyntax.ParseTraversalAbs([]byte(addr), "", hcl.InitialPos)
|
|
if len(diags) > 0 {
|
|
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
}
|
|
traversal[4] = hcl.TraverseIndex{
|
|
Key: cty.UnknownVal(cty.Number),
|
|
}
|
|
return "module.child.module.grandchild[*]", traversal
|
|
},
|
|
want: PartialExpandedModule{
|
|
expandedPrefix: ModuleInstance{
|
|
{
|
|
Name: "child",
|
|
},
|
|
},
|
|
unexpandedSuffix: Module{
|
|
"grandchild",
|
|
},
|
|
},
|
|
remain: 0,
|
|
},
|
|
{
|
|
traversal: func(t *testing.T) (string, hcl.Traversal) {
|
|
addr := "module.child.module.grandchild[0].resource_type.resource_name"
|
|
traversal, diags := hclsyntax.ParseTraversalAbs([]byte(addr), "", hcl.InitialPos)
|
|
if len(diags) > 0 {
|
|
t.Fatalf("unexpected diagnostics: %v", diags)
|
|
}
|
|
traversal[4] = hcl.TraverseIndex{
|
|
Key: cty.UnknownVal(cty.Number),
|
|
}
|
|
return "module.child.module.grandchild[*].resource_type.resource_name", traversal
|
|
},
|
|
want: PartialExpandedModule{
|
|
expandedPrefix: ModuleInstance{
|
|
{
|
|
Name: "child",
|
|
},
|
|
},
|
|
unexpandedSuffix: Module{
|
|
"grandchild",
|
|
},
|
|
},
|
|
remain: 2,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tcs {
|
|
addr, traversal := tc.traversal(t)
|
|
t.Run(addr, func(t *testing.T) {
|
|
module, rest, diags := ParsePartialExpandedModule(traversal)
|
|
if len(diags) > 0 {
|
|
t.Fatalf("unexpected diagnostics: %s", diags)
|
|
}
|
|
|
|
if !module.expandedPrefix.Equal(tc.want.expandedPrefix) {
|
|
t.Errorf("got expandedPrefix %v; want %v", module.expandedPrefix, tc.want.expandedPrefix)
|
|
}
|
|
if !module.unexpandedSuffix.Equal(tc.want.unexpandedSuffix) {
|
|
t.Errorf("got unexpandedSuffix %v; want %v", module.unexpandedSuffix, tc.want.unexpandedSuffix)
|
|
}
|
|
if len(rest) != tc.remain {
|
|
t.Errorf("got %d remaining traversals; want %d", len(rest), tc.remain)
|
|
}
|
|
})
|
|
}
|
|
|
|
}
|
|
|
|
func TestParsePartialExpandedResource(t *testing.T) {
|
|
|
|
tcs := []struct {
|
|
addr string
|
|
want PartialExpandedResource
|
|
remain int
|
|
}{
|
|
{
|
|
addr: "resource_type.resource_name",
|
|
want: PartialExpandedResource{
|
|
resource: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "resource_type",
|
|
Name: "resource_name",
|
|
},
|
|
},
|
|
remain: 0,
|
|
},
|
|
{
|
|
addr: "module.mod.resource_type.resource_name",
|
|
want: PartialExpandedResource{
|
|
module: PartialExpandedModule{
|
|
expandedPrefix: ModuleInstance{
|
|
{
|
|
Name: "mod",
|
|
},
|
|
},
|
|
},
|
|
resource: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "resource_type",
|
|
Name: "resource_name",
|
|
},
|
|
},
|
|
},
|
|
{
|
|
addr: "resource_type.resource_name[0]",
|
|
want: PartialExpandedResource{
|
|
resource: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "resource_type",
|
|
Name: "resource_name",
|
|
},
|
|
},
|
|
remain: 0,
|
|
},
|
|
{
|
|
addr: "resource_type.resource_name[0].attr",
|
|
want: PartialExpandedResource{
|
|
resource: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "resource_type",
|
|
Name: "resource_name",
|
|
},
|
|
},
|
|
remain: 1,
|
|
},
|
|
{
|
|
addr: "resource.resource_type.resource_name",
|
|
want: PartialExpandedResource{
|
|
resource: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "resource_type",
|
|
Name: "resource_name",
|
|
},
|
|
},
|
|
remain: 0,
|
|
},
|
|
}
|
|
|
|
for _, tc := range tcs {
|
|
t.Run(tc.addr, func(t *testing.T) {
|
|
traversal, traversalDiags := hclsyntax.ParseTraversalAbs([]byte(tc.addr), "", hcl.InitialPos)
|
|
if len(traversalDiags) > 0 {
|
|
t.Fatalf("unexpected diagnostics: %v", traversalDiags)
|
|
}
|
|
|
|
partial, rest, diags := ParsePartialExpandedResource(traversal)
|
|
if len(diags) > 0 {
|
|
t.Fatalf("unexpected diagnostics: %s", diags)
|
|
}
|
|
|
|
if !partial.module.expandedPrefix.Equal(tc.want.module.expandedPrefix) {
|
|
t.Errorf("got expandedPrefix %v; want %v", partial.module.expandedPrefix, tc.want.module.expandedPrefix)
|
|
}
|
|
if !partial.module.unexpandedSuffix.Equal(tc.want.module.unexpandedSuffix) {
|
|
t.Errorf("got unexpandedSuffix %v; want %v", partial.module.unexpandedSuffix, tc.want.module.unexpandedSuffix)
|
|
}
|
|
if !partial.resource.Equal(tc.want.resource) {
|
|
t.Errorf("got resource %v; want %v", partial.resource, tc.want.resource)
|
|
}
|
|
if len(rest) != tc.remain {
|
|
t.Errorf("got %d remaining traversals; want %d", len(rest), tc.remain)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func mustParsePartialResourceInstanceStr(s string) AbsResourceInstance {
|
|
r, diags := ParsePartialResourceInstanceStr(s)
|
|
if diags.HasErrors() {
|
|
panic(diags.ErrWithWarnings().Error())
|
|
}
|
|
return r
|
|
}
|