terraform/internal/addrs/move_endpoint_module_test.go

1358 lines
30 KiB
Go

package addrs
import (
"fmt"
"strings"
"testing"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/terraform/internal/tfdiags"
)
func TestModuleInstanceMoveDestination(t *testing.T) {
tests := []struct {
DeclModule string
StmtFrom, StmtTo string
Receiver string
WantMatch bool
WantResult string
}{
{
``,
`module.foo`,
`module.bar`,
`module.foo`,
true,
`module.bar`,
},
{
``,
`module.foo`,
`module.bar`,
`module.foo[1]`,
true,
`module.bar[1]`,
},
{
``,
`module.foo`,
`module.bar`,
`module.foo["a"]`,
true,
`module.bar["a"]`,
},
{
``,
`module.foo`,
`module.bar.module.foo`,
`module.foo`,
true,
`module.bar.module.foo`,
},
{
``,
`module.foo.module.bar`,
`module.bar`,
`module.foo.module.bar`,
true,
`module.bar`,
},
{
``,
`module.foo[1]`,
`module.foo[2]`,
`module.foo[1]`,
true,
`module.foo[2]`,
},
{
``,
`module.foo[1]`,
`module.foo`,
`module.foo[1]`,
true,
`module.foo`,
},
{
``,
`module.foo`,
`module.foo[1]`,
`module.foo`,
true,
`module.foo[1]`,
},
{
``,
`module.foo`,
`module.foo[1]`,
`module.foo.module.bar`,
true,
`module.foo[1].module.bar`,
},
{
``,
`module.foo`,
`module.foo[1]`,
`module.foo.module.bar[0]`,
true,
`module.foo[1].module.bar[0]`,
},
{
``,
`module.foo`,
`module.bar.module.foo`,
`module.foo[0]`,
true,
`module.bar.module.foo[0]`,
},
{
``,
`module.foo.module.bar`,
`module.bar`,
`module.foo.module.bar[0]`,
true,
`module.bar[0]`,
},
{
`foo`,
`module.bar`,
`module.baz`,
`module.foo.module.bar`,
true,
`module.foo.module.baz`,
},
{
`foo`,
`module.bar`,
`module.baz`,
`module.foo[1].module.bar`,
true,
`module.foo[1].module.baz`,
},
{
`foo`,
`module.bar`,
`module.bar[1]`,
`module.foo[1].module.bar`,
true,
`module.foo[1].module.bar[1]`,
},
{
``,
`module.foo[1]`,
`module.foo[2]`,
`module.foo`,
false, // the receiver has a non-matching instance key (NoKey)
``,
},
{
``,
`module.foo[1]`,
`module.foo[2]`,
`module.foo[2]`,
false, // the receiver is already the "to" address
``,
},
{
``,
`module.foo`,
`module.bar`,
``,
false, // the root module can never be moved
``,
},
{
`foo`,
`module.bar`,
`module.bar[1]`,
`module.boz`,
false, // the receiver is outside the declaration module
``,
},
{
`foo.bar`,
`module.bar`,
`module.bar[1]`,
`module.boz`,
false, // the receiver is outside the declaration module
``,
},
{
`foo.bar`,
`module.a`,
`module.b`,
`module.boz`,
false, // the receiver is outside the declaration module
``,
},
{
``,
`module.a1.module.a2`,
`module.b1.module.b2`,
`module.c`,
false, // the receiver is outside the declaration module
``,
},
{
``,
`module.a1.module.a2[0]`,
`module.b1.module.b2[1]`,
`module.c`,
false, // the receiver is outside the declaration module
``,
},
{
``,
`module.a1.module.a2`,
`module.b1.module.b2`,
`module.a1.module.b2`,
false, // the receiver is outside the declaration module
``,
},
{
``,
`module.a1.module.a2`,
`module.b1.module.b2`,
`module.b1.module.a2`,
false, // the receiver is outside the declaration module
``,
},
{
``,
`module.a1.module.a2[0]`,
`module.b1.module.b2[1]`,
`module.a1.module.b2[0]`,
false, // the receiver is outside the declaration module
``,
},
{
``,
`foo_instance.bar`,
`foo_instance.baz`,
`module.foo`,
false, // a resource address can never match a module instance
``,
},
}
for _, test := range tests {
t.Run(
fmt.Sprintf(
"%s: %s to %s with %s",
test.DeclModule,
test.StmtFrom, test.StmtTo,
test.Receiver,
),
func(t *testing.T) {
parseStmtEP := func(t *testing.T, input string) *MoveEndpoint {
t.Helper()
traversal, hclDiags := hclsyntax.ParseTraversalAbs([]byte(input), "", hcl.InitialPos)
if hclDiags.HasErrors() {
// We're not trying to test the HCL parser here, so any
// failures at this point are likely to be bugs in the
// test case itself.
t.Fatalf("syntax error: %s", hclDiags.Error())
}
moveEp, diags := ParseMoveEndpoint(traversal)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err().Error())
}
return moveEp
}
fromEPLocal := parseStmtEP(t, test.StmtFrom)
toEPLocal := parseStmtEP(t, test.StmtTo)
declModule := RootModule
if test.DeclModule != "" {
declModule = strings.Split(test.DeclModule, ".")
}
fromEP, toEP := UnifyMoveEndpoints(declModule, fromEPLocal, toEPLocal)
if fromEP == nil || toEP == nil {
t.Fatalf("invalid test case: non-unifyable endpoints\nfrom: %s\nto: %s", fromEPLocal, toEPLocal)
}
receiverAddr := RootModuleInstance
if test.Receiver != "" {
var diags tfdiags.Diagnostics
receiverAddr, diags = ParseModuleInstanceStr(test.Receiver)
if diags.HasErrors() {
t.Fatalf("invalid reciever address: %s", diags.Err().Error())
}
}
gotAddr, gotMatch := receiverAddr.MoveDestination(fromEP, toEP)
if !test.WantMatch {
if gotMatch {
t.Errorf("unexpected match\nreceiver: %s\nfrom: %s\nto: %s\nresult: %s", test.Receiver, fromEP, toEP, gotAddr)
}
return
}
if !gotMatch {
t.Errorf("unexpected non-match\nreceiver: %s\nfrom: %s\nto: %s", test.Receiver, fromEP, toEP)
}
if gotStr, wantStr := gotAddr.String(), test.WantResult; gotStr != wantStr {
t.Errorf("wrong result\ngot: %s\nwant: %s", gotStr, wantStr)
}
},
)
}
}
func TestAbsResourceInstanceMoveDestination(t *testing.T) {
tests := []struct {
DeclModule string
StmtFrom, StmtTo string
Receiver string
WantMatch bool
WantResult string
}{
{
``,
`test_object.beep`,
`test_object.boop`,
`test_object.beep`,
true,
`test_object.boop`,
},
{
``,
`test_object.beep`,
`test_object.beep[2]`,
`test_object.beep`,
true,
`test_object.beep[2]`,
},
{
``,
`test_object.beep`,
`module.foo.test_object.beep`,
`test_object.beep`,
true,
`module.foo.test_object.beep`,
},
{
``,
`test_object.beep[2]`,
`module.foo.test_object.beep["a"]`,
`test_object.beep[2]`,
true,
`module.foo.test_object.beep["a"]`,
},
{
``,
`test_object.beep`,
`module.foo[0].test_object.beep`,
`test_object.beep`,
true,
`module.foo[0].test_object.beep`,
},
{
``,
`module.foo.test_object.beep`,
`test_object.beep`,
`module.foo.test_object.beep`,
true,
`test_object.beep`,
},
{
``,
`module.foo[0].test_object.beep`,
`test_object.beep`,
`module.foo[0].test_object.beep`,
true,
`test_object.beep`,
},
{
`foo`,
`test_object.beep`,
`test_object.boop`,
`module.foo[0].test_object.beep`,
true,
`module.foo[0].test_object.boop`,
},
{
`foo`,
`test_object.beep`,
`test_object.beep[1]`,
`module.foo[0].test_object.beep`,
true,
`module.foo[0].test_object.beep[1]`,
},
{
``,
`test_object.beep`,
`test_object.boop`,
`test_object.boop`,
false, // the reciever is already the "to" address
``,
},
{
``,
`test_object.beep[1]`,
`test_object.beep[2]`,
`test_object.beep[5]`,
false, // the receiver has a non-matching instance key
``,
},
{
`foo`,
`test_object.beep`,
`test_object.boop`,
`test_object.beep`,
false, // the receiver is not inside an instance of module "foo"
``,
},
{
`foo.bar`,
`test_object.beep`,
`test_object.boop`,
`test_object.beep`,
false, // the receiver is not inside an instance of module "foo.bar"
``,
},
{
``,
`module.foo[0].test_object.beep`,
`test_object.beep`,
`module.foo[1].test_object.beep`,
false, // receiver is in a different instance of module.foo
``,
},
// Moving a module also moves all of the resources declared within it.
// The following tests all cover variations of that rule.
{
``,
`module.foo`,
`module.bar`,
`module.foo.test_object.beep`,
true,
`module.bar.test_object.beep`,
},
{
``,
`module.foo`,
`module.bar`,
`module.foo[1].test_object.beep`,
true,
`module.bar[1].test_object.beep`,
},
{
``,
`module.foo`,
`module.bar`,
`module.foo["a"].test_object.beep`,
true,
`module.bar["a"].test_object.beep`,
},
{
``,
`module.foo`,
`module.bar.module.foo`,
`module.foo.test_object.beep`,
true,
`module.bar.module.foo.test_object.beep`,
},
{
``,
`module.foo.module.bar`,
`module.bar`,
`module.foo.module.bar.test_object.beep`,
true,
`module.bar.test_object.beep`,
},
{
``,
`module.foo[1]`,
`module.foo[2]`,
`module.foo[1].test_object.beep`,
true,
`module.foo[2].test_object.beep`,
},
{
``,
`module.foo[1]`,
`module.foo`,
`module.foo[1].test_object.beep`,
true,
`module.foo.test_object.beep`,
},
{
``,
`module.foo`,
`module.foo[1]`,
`module.foo.test_object.beep`,
true,
`module.foo[1].test_object.beep`,
},
{
``,
`module.foo`,
`module.foo[1]`,
`module.foo.module.bar.test_object.beep`,
true,
`module.foo[1].module.bar.test_object.beep`,
},
{
``,
`module.foo`,
`module.foo[1]`,
`module.foo.module.bar[0].test_object.beep`,
true,
`module.foo[1].module.bar[0].test_object.beep`,
},
{
``,
`module.foo`,
`module.bar.module.foo`,
`module.foo[0].test_object.beep`,
true,
`module.bar.module.foo[0].test_object.beep`,
},
{
``,
`module.foo.module.bar`,
`module.bar`,
`module.foo.module.bar[0].test_object.beep`,
true,
`module.bar[0].test_object.beep`,
},
{
`foo`,
`module.bar`,
`module.baz`,
`module.foo.module.bar.test_object.beep`,
true,
`module.foo.module.baz.test_object.beep`,
},
{
`foo`,
`module.bar`,
`module.baz`,
`module.foo[1].module.bar.test_object.beep`,
true,
`module.foo[1].module.baz.test_object.beep`,
},
{
`foo`,
`module.bar`,
`module.bar[1]`,
`module.foo[1].module.bar.test_object.beep`,
true,
`module.foo[1].module.bar[1].test_object.beep`,
},
{
``,
`module.foo[1]`,
`module.foo[2]`,
`module.foo.test_object.beep`,
false, // the receiver module has a non-matching instance key (NoKey)
``,
},
{
``,
`module.foo[1]`,
`module.foo[2]`,
`module.foo[2].test_object.beep`,
false, // the receiver is already at the "to" address
``,
},
{
`foo`,
`module.bar`,
`module.bar[1]`,
`module.boz.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
`foo.bar`,
`module.bar`,
`module.bar[1]`,
`module.boz.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
`foo.bar`,
`module.a`,
`module.b`,
`module.boz.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
``,
`module.a1.module.a2`,
`module.b1.module.b2`,
`module.c.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
``,
`module.a1.module.a2[0]`,
`module.b1.module.b2[1]`,
`module.c.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
``,
`module.a1.module.a2`,
`module.b1.module.b2`,
`module.a1.module.b2.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
``,
`module.a1.module.a2`,
`module.b1.module.b2`,
`module.b1.module.a2.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
``,
`module.a1.module.a2[0]`,
`module.b1.module.b2[1]`,
`module.a1.module.b2[0].test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
``,
`foo_instance.bar`,
`foo_instance.baz`,
`module.foo.test_object.beep`,
false, // the resource address is unrelated to the move statements
``,
},
}
for _, test := range tests {
t.Run(
fmt.Sprintf(
"%s: %s to %s with %s",
test.DeclModule,
test.StmtFrom, test.StmtTo,
test.Receiver,
),
func(t *testing.T) {
parseStmtEP := func(t *testing.T, input string) *MoveEndpoint {
t.Helper()
traversal, hclDiags := hclsyntax.ParseTraversalAbs([]byte(input), "", hcl.InitialPos)
if hclDiags.HasErrors() {
// We're not trying to test the HCL parser here, so any
// failures at this point are likely to be bugs in the
// test case itself.
t.Fatalf("syntax error: %s", hclDiags.Error())
}
moveEp, diags := ParseMoveEndpoint(traversal)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err().Error())
}
return moveEp
}
fromEPLocal := parseStmtEP(t, test.StmtFrom)
toEPLocal := parseStmtEP(t, test.StmtTo)
declModule := RootModule
if test.DeclModule != "" {
declModule = strings.Split(test.DeclModule, ".")
}
fromEP, toEP := UnifyMoveEndpoints(declModule, fromEPLocal, toEPLocal)
if fromEP == nil || toEP == nil {
t.Fatalf("invalid test case: non-unifyable endpoints\nfrom: %s\nto: %s", fromEPLocal, toEPLocal)
}
receiverAddr, diags := ParseAbsResourceInstanceStr(test.Receiver)
if diags.HasErrors() {
t.Fatalf("invalid reciever address: %s", diags.Err().Error())
}
gotAddr, gotMatch := receiverAddr.MoveDestination(fromEP, toEP)
if !test.WantMatch {
if gotMatch {
t.Errorf("unexpected match\nreceiver: %s\nfrom: %s\nto: %s\nresult: %s", test.Receiver, fromEP, toEP, gotAddr)
}
return
}
if !gotMatch {
t.Fatalf("unexpected non-match\nreceiver: %s (%T)\nfrom: %s\nto: %s\ngot: (no match)\nwant: %s", test.Receiver, receiverAddr, fromEP, toEP, test.WantResult)
}
if gotStr, wantStr := gotAddr.String(), test.WantResult; gotStr != wantStr {
t.Errorf("wrong result\ngot: %s\nwant: %s", gotStr, wantStr)
}
},
)
}
}
func TestAbsResourceMoveDestination(t *testing.T) {
tests := []struct {
DeclModule string
StmtFrom, StmtTo string
Receiver string
WantMatch bool
WantResult string
}{
{
``,
`test_object.beep`,
`test_object.boop`,
`test_object.beep`,
true,
`test_object.boop`,
},
{
``,
`test_object.beep`,
`module.foo.test_object.beep`,
`test_object.beep`,
true,
`module.foo.test_object.beep`,
},
{
``,
`test_object.beep`,
`module.foo[0].test_object.beep`,
`test_object.beep`,
true,
`module.foo[0].test_object.beep`,
},
{
``,
`module.foo.test_object.beep`,
`test_object.beep`,
`module.foo.test_object.beep`,
true,
`test_object.beep`,
},
{
``,
`module.foo[0].test_object.beep`,
`test_object.beep`,
`module.foo[0].test_object.beep`,
true,
`test_object.beep`,
},
{
`foo`,
`test_object.beep`,
`test_object.boop`,
`module.foo[0].test_object.beep`,
true,
`module.foo[0].test_object.boop`,
},
{
``,
`test_object.beep`,
`test_object.boop`,
`test_object.boop`,
false, // the reciever is already the "to" address
``,
},
{
`foo`,
`test_object.beep`,
`test_object.boop`,
`test_object.beep`,
false, // the receiver is not inside an instance of module "foo"
``,
},
{
`foo.bar`,
`test_object.beep`,
`test_object.boop`,
`test_object.beep`,
false, // the receiver is not inside an instance of module "foo.bar"
``,
},
{
``,
`module.foo[0].test_object.beep`,
`test_object.beep`,
`module.foo[1].test_object.beep`,
false, // receiver is in a different instance of module.foo
``,
},
// Moving a module also moves all of the resources declared within it.
// The following tests all cover variations of that rule.
{
``,
`module.foo`,
`module.bar`,
`module.foo.test_object.beep`,
true,
`module.bar.test_object.beep`,
},
{
``,
`module.foo`,
`module.bar`,
`module.foo[1].test_object.beep`,
true,
`module.bar[1].test_object.beep`,
},
{
``,
`module.foo`,
`module.bar`,
`module.foo["a"].test_object.beep`,
true,
`module.bar["a"].test_object.beep`,
},
{
``,
`module.foo`,
`module.bar.module.foo`,
`module.foo.test_object.beep`,
true,
`module.bar.module.foo.test_object.beep`,
},
{
``,
`module.foo.module.bar`,
`module.bar`,
`module.foo.module.bar.test_object.beep`,
true,
`module.bar.test_object.beep`,
},
{
``,
`module.foo[1]`,
`module.foo[2]`,
`module.foo[1].test_object.beep`,
true,
`module.foo[2].test_object.beep`,
},
{
``,
`module.foo[1]`,
`module.foo`,
`module.foo[1].test_object.beep`,
true,
`module.foo.test_object.beep`,
},
{
``,
`module.foo`,
`module.foo[1]`,
`module.foo.test_object.beep`,
true,
`module.foo[1].test_object.beep`,
},
{
``,
`module.foo`,
`module.foo[1]`,
`module.foo.module.bar.test_object.beep`,
true,
`module.foo[1].module.bar.test_object.beep`,
},
{
``,
`module.foo`,
`module.foo[1]`,
`module.foo.module.bar[0].test_object.beep`,
true,
`module.foo[1].module.bar[0].test_object.beep`,
},
{
``,
`module.foo`,
`module.bar.module.foo`,
`module.foo[0].test_object.beep`,
true,
`module.bar.module.foo[0].test_object.beep`,
},
{
``,
`module.foo.module.bar`,
`module.bar`,
`module.foo.module.bar[0].test_object.beep`,
true,
`module.bar[0].test_object.beep`,
},
{
`foo`,
`module.bar`,
`module.baz`,
`module.foo.module.bar.test_object.beep`,
true,
`module.foo.module.baz.test_object.beep`,
},
{
`foo`,
`module.bar`,
`module.baz`,
`module.foo[1].module.bar.test_object.beep`,
true,
`module.foo[1].module.baz.test_object.beep`,
},
{
`foo`,
`module.bar`,
`module.bar[1]`,
`module.foo[1].module.bar.test_object.beep`,
true,
`module.foo[1].module.bar[1].test_object.beep`,
},
{
``,
`module.foo[1]`,
`module.foo[2]`,
`module.foo.test_object.beep`,
false, // the receiver module has a non-matching instance key (NoKey)
``,
},
{
``,
`module.foo[1]`,
`module.foo[2]`,
`module.foo[2].test_object.beep`,
false, // the receiver is already at the "to" address
``,
},
{
`foo`,
`module.bar`,
`module.bar[1]`,
`module.boz.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
`foo.bar`,
`module.bar`,
`module.bar[1]`,
`module.boz.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
`foo.bar`,
`module.a`,
`module.b`,
`module.boz.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
``,
`module.a1.module.a2`,
`module.b1.module.b2`,
`module.c.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
``,
`module.a1.module.a2[0]`,
`module.b1.module.b2[1]`,
`module.c.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
``,
`module.a1.module.a2`,
`module.b1.module.b2`,
`module.a1.module.b2.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
``,
`module.a1.module.a2`,
`module.b1.module.b2`,
`module.b1.module.a2.test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
``,
`module.a1.module.a2[0]`,
`module.b1.module.b2[1]`,
`module.a1.module.b2[0].test_object.beep`,
false, // the receiver module is outside the declaration module
``,
},
{
``,
`foo_instance.bar`,
`foo_instance.baz`,
`module.foo.test_object.beep`,
false, // the resource address is unrelated to the move statements
``,
},
}
for i, test := range tests {
t.Run(
fmt.Sprintf(
"[%02d] %s: %s to %s with %s",
i,
test.DeclModule,
test.StmtFrom, test.StmtTo,
test.Receiver,
),
func(t *testing.T) {
parseStmtEP := func(t *testing.T, input string) *MoveEndpoint {
t.Helper()
traversal, hclDiags := hclsyntax.ParseTraversalAbs([]byte(input), "", hcl.InitialPos)
if hclDiags.HasErrors() {
// We're not trying to test the HCL parser here, so any
// failures at this point are likely to be bugs in the
// test case itself.
t.Fatalf("syntax error: %s", hclDiags.Error())
}
moveEp, diags := ParseMoveEndpoint(traversal)
if diags.HasErrors() {
t.Fatalf("unexpected error: %s", diags.Err().Error())
}
return moveEp
}
fromEPLocal := parseStmtEP(t, test.StmtFrom)
toEPLocal := parseStmtEP(t, test.StmtTo)
declModule := RootModule
if test.DeclModule != "" {
declModule = strings.Split(test.DeclModule, ".")
}
fromEP, toEP := UnifyMoveEndpoints(declModule, fromEPLocal, toEPLocal)
if fromEP == nil || toEP == nil {
t.Fatalf("invalid test case: non-unifyable endpoints\nfrom: %s\nto: %s", fromEPLocal, toEPLocal)
}
// We only have an AbsResourceInstance parser, not an
// AbsResourceParser, and so we'll just cheat and parse this
// as a resource instance but fail if it includes an instance
// key.
receiverInstanceAddr, diags := ParseAbsResourceInstanceStr(test.Receiver)
if diags.HasErrors() {
t.Fatalf("invalid reciever address: %s", diags.Err().Error())
}
if receiverInstanceAddr.Resource.Key != NoKey {
t.Fatalf("invalid reciever address: must be a resource, not a resource instance")
}
receiverAddr := receiverInstanceAddr.ContainingResource()
gotAddr, gotMatch := receiverAddr.MoveDestination(fromEP, toEP)
if !test.WantMatch {
if gotMatch {
t.Errorf("unexpected match\nreceiver: %s (%T)\nfrom: %s\nto: %s\nresult: %s", test.Receiver, receiverAddr, fromEP, toEP, gotAddr)
}
return
}
if !gotMatch {
t.Fatalf("unexpected non-match\nreceiver: %s (%T)\nfrom: %s\nto: %s\ngot: no match\nwant: %s", test.Receiver, receiverAddr, fromEP, toEP, test.WantResult)
}
if gotStr, wantStr := gotAddr.String(), test.WantResult; gotStr != wantStr {
t.Errorf("wrong result\ngot: %s\nwant: %s", gotStr, wantStr)
}
},
)
}
}
func TestMoveEndpointChainAndNested(t *testing.T) {
tests := []struct {
Endpoint, Other AbsMoveable
CanChainFrom, NestedWithin bool
}{
{
Endpoint: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
Other: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
CanChainFrom: true,
NestedWithin: false,
},
{
Endpoint: mustParseModuleInstanceStr("module.foo[2]"),
Other: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
CanChainFrom: false,
NestedWithin: false,
},
{
Endpoint: mustParseModuleInstanceStr("module.foo[2].module.bar[2]"),
Other: AbsModuleCall{
Module: RootModuleInstance,
Call: ModuleCall{Name: "foo"},
},
CanChainFrom: false,
NestedWithin: true,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].module.bar.resource.baz").ContainingResource(),
Other: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
CanChainFrom: false,
NestedWithin: true,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].module.bar[3].resource.baz[2]"),
Other: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
CanChainFrom: false,
NestedWithin: true,
},
{
Endpoint: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
Other: mustParseModuleInstanceStr("module.foo[2]"),
CanChainFrom: false,
NestedWithin: false,
},
{
Endpoint: mustParseModuleInstanceStr("module.foo[2]"),
Other: mustParseModuleInstanceStr("module.foo[2]"),
CanChainFrom: true,
NestedWithin: false,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
Other: mustParseModuleInstanceStr("module.foo[2]"),
CanChainFrom: false,
NestedWithin: true,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].module.bar.resource.baz"),
Other: mustParseModuleInstanceStr("module.foo[2]"),
CanChainFrom: false,
NestedWithin: true,
},
{
Endpoint: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
CanChainFrom: false,
NestedWithin: false,
},
{
Endpoint: mustParseModuleInstanceStr("module.foo[2]"),
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
CanChainFrom: false,
NestedWithin: false,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
CanChainFrom: true,
NestedWithin: false,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz[2]").ContainingResource(),
CanChainFrom: false,
NestedWithin: true,
},
{
Endpoint: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
CanChainFrom: false,
},
{
Endpoint: mustParseModuleInstanceStr("module.foo[2]"),
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
CanChainFrom: false,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz").ContainingResource(),
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
CanChainFrom: false,
},
{
Endpoint: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
Other: mustParseAbsResourceInstanceStr("module.foo[2].resource.baz"),
CanChainFrom: true,
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("[%02d]%s.CanChainFrom(%s)", i, test.Endpoint, test.Other),
func(t *testing.T) {
endpoint := &MoveEndpointInModule{
relSubject: test.Endpoint,
}
other := &MoveEndpointInModule{
relSubject: test.Other,
}
if endpoint.CanChainFrom(other) != test.CanChainFrom {
t.Errorf("expected %s CanChainFrom %s == %t", test.Endpoint, test.Other, test.CanChainFrom)
}
if endpoint.NestedWithin(other) != test.NestedWithin {
t.Errorf("expected %s NestedWithin %s == %t", test.Endpoint, test.Other, test.NestedWithin)
}
},
)
}
}
func TestSelectsModule(t *testing.T) {
tests := []struct {
Endpoint *MoveEndpointInModule
Addr ModuleInstance
Selects bool
}{
{
Endpoint: &MoveEndpointInModule{
relSubject: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.foo[2]"),
Call: ModuleCall{Name: "bar"},
},
},
Addr: mustParseModuleInstanceStr("module.foo[2].module.bar[1]"),
Selects: true,
},
{
Endpoint: &MoveEndpointInModule{
module: mustParseModuleInstanceStr("module.foo").Module(),
relSubject: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.bar[2]"),
Call: ModuleCall{Name: "baz"},
},
},
Addr: mustParseModuleInstanceStr("module.foo[2].module.bar[2].module.baz"),
Selects: true,
},
{
Endpoint: &MoveEndpointInModule{
module: mustParseModuleInstanceStr("module.foo").Module(),
relSubject: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.bar[2]"),
Call: ModuleCall{Name: "baz"},
},
},
Addr: mustParseModuleInstanceStr("module.foo[2].module.bar[1].module.baz"),
Selects: false,
},
{
Endpoint: &MoveEndpointInModule{
relSubject: AbsModuleCall{
Module: mustParseModuleInstanceStr("module.bar"),
Call: ModuleCall{Name: "baz"},
},
},
Addr: mustParseModuleInstanceStr("module.bar[1].module.baz"),
Selects: false,
},
{
Endpoint: &MoveEndpointInModule{
module: mustParseModuleInstanceStr("module.foo").Module(),
relSubject: mustParseAbsResourceInstanceStr(`module.bar.resource.name["key"]`),
},
Addr: mustParseModuleInstanceStr(`module.foo[1].module.bar`),
Selects: true,
},
{
Endpoint: &MoveEndpointInModule{
relSubject: mustParseModuleInstanceStr(`module.bar.module.baz["key"]`),
},
Addr: mustParseModuleInstanceStr(`module.bar.module.baz["key"]`),
Selects: true,
},
{
Endpoint: &MoveEndpointInModule{
relSubject: mustParseAbsResourceInstanceStr(`module.bar.module.baz["key"].resource.name`).ContainingResource(),
},
Addr: mustParseModuleInstanceStr(`module.bar.module.baz["key"]`),
Selects: true,
},
{
Endpoint: &MoveEndpointInModule{
module: mustParseModuleInstanceStr("module.nope").Module(),
relSubject: mustParseAbsResourceInstanceStr(`module.bar.resource.name["key"]`),
},
Addr: mustParseModuleInstanceStr(`module.foo[1].module.bar`),
Selects: false,
},
{
Endpoint: &MoveEndpointInModule{
relSubject: mustParseModuleInstanceStr(`module.bar.module.baz["key"]`),
},
Addr: mustParseModuleInstanceStr(`module.bar.module.baz["nope"]`),
Selects: false,
},
{
Endpoint: &MoveEndpointInModule{
relSubject: mustParseAbsResourceInstanceStr(`module.nope.module.baz["key"].resource.name`).ContainingResource(),
},
Addr: mustParseModuleInstanceStr(`module.bar.module.baz["key"]`),
Selects: false,
},
}
for i, test := range tests {
t.Run(fmt.Sprintf("[%02d]%s.SelectsModule(%s)", i, test.Endpoint, test.Addr),
func(t *testing.T) {
if test.Endpoint.SelectsModule(test.Addr) != test.Selects {
t.Errorf("expected %s SelectsModule %s == %t", test.Endpoint, test.Addr, test.Selects)
}
},
)
}
}
func mustParseAbsResourceInstanceStr(s string) AbsResourceInstance {
r, diags := ParseAbsResourceInstanceStr(s)
if diags.HasErrors() {
panic(diags.ErrWithWarnings().Error())
}
return r
}