opentofu/addrs/parse_target_test.go
Martin Atkins 02b25e7057 addrs: flesh out functionality ready to replace terraform.ResourceAddress
This "kitchen sink" commit is mainly focused on supporting "targets" as
a new sub-category of addresses, for use-case like the -target CLI option,
but also includes some other functionality to get closer to replacing
terraform.ResourceAddress and fill out some missing parts for representing
various other address types that are currently represented as strings
in the "terraform" package.
2018-10-16 18:44:26 -07:00

344 lines
7.3 KiB
Go

package addrs
import (
"testing"
"github.com/go-test/deep"
"github.com/hashicorp/hcl2/hcl"
"github.com/hashicorp/hcl2/hcl/hclsyntax"
"github.com/hashicorp/terraform/tfdiags"
)
func TestParseTarget(t *testing.T) {
tests := []struct {
Input string
Want *Target
WantErr string
}{
{
`module.foo`,
&Target{
Subject: ModuleInstance{
{
Name: "foo",
},
},
SourceRange: tfdiags.SourceRange{
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 11, Byte: 10},
},
},
``,
},
{
`module.foo[2]`,
&Target{
Subject: ModuleInstance{
{
Name: "foo",
InstanceKey: IntKey(2),
},
},
SourceRange: tfdiags.SourceRange{
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 14, Byte: 13},
},
},
``,
},
{
`module.foo[2].module.bar`,
&Target{
Subject: ModuleInstance{
{
Name: "foo",
InstanceKey: IntKey(2),
},
{
Name: "bar",
},
},
SourceRange: tfdiags.SourceRange{
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 25, Byte: 24},
},
},
``,
},
{
`aws_instance.foo`,
&Target{
Subject: AbsResource{
Resource: Resource{
Mode: ManagedResourceMode,
Type: "aws_instance",
Name: "foo",
},
Module: RootModuleInstance,
},
SourceRange: tfdiags.SourceRange{
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 17, Byte: 16},
},
},
``,
},
{
`aws_instance.foo[1]`,
&Target{
Subject: AbsResourceInstance{
Resource: ResourceInstance{
Resource: Resource{
Mode: ManagedResourceMode,
Type: "aws_instance",
Name: "foo",
},
Key: IntKey(1),
},
Module: RootModuleInstance,
},
SourceRange: tfdiags.SourceRange{
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 20, Byte: 19},
},
},
``,
},
{
`data.aws_instance.foo`,
&Target{
Subject: AbsResource{
Resource: Resource{
Mode: DataResourceMode,
Type: "aws_instance",
Name: "foo",
},
Module: RootModuleInstance,
},
SourceRange: tfdiags.SourceRange{
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 22, Byte: 21},
},
},
``,
},
{
`data.aws_instance.foo[1]`,
&Target{
Subject: AbsResourceInstance{
Resource: ResourceInstance{
Resource: Resource{
Mode: DataResourceMode,
Type: "aws_instance",
Name: "foo",
},
Key: IntKey(1),
},
Module: RootModuleInstance,
},
SourceRange: tfdiags.SourceRange{
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 25, Byte: 24},
},
},
``,
},
{
`module.foo.aws_instance.bar`,
&Target{
Subject: AbsResource{
Resource: Resource{
Mode: ManagedResourceMode,
Type: "aws_instance",
Name: "bar",
},
Module: ModuleInstance{
{Name: "foo"},
},
},
SourceRange: tfdiags.SourceRange{
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 28, Byte: 27},
},
},
``,
},
{
`module.foo.module.bar.aws_instance.baz`,
&Target{
Subject: AbsResource{
Resource: Resource{
Mode: ManagedResourceMode,
Type: "aws_instance",
Name: "baz",
},
Module: ModuleInstance{
{Name: "foo"},
{Name: "bar"},
},
},
SourceRange: tfdiags.SourceRange{
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 39, Byte: 38},
},
},
``,
},
{
`module.foo.module.bar.aws_instance.baz["hello"]`,
&Target{
Subject: AbsResourceInstance{
Resource: ResourceInstance{
Resource: Resource{
Mode: ManagedResourceMode,
Type: "aws_instance",
Name: "baz",
},
Key: StringKey("hello"),
},
Module: ModuleInstance{
{Name: "foo"},
{Name: "bar"},
},
},
SourceRange: tfdiags.SourceRange{
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 48, Byte: 47},
},
},
``,
},
{
`module.foo.data.aws_instance.bar`,
&Target{
Subject: AbsResource{
Resource: Resource{
Mode: DataResourceMode,
Type: "aws_instance",
Name: "bar",
},
Module: ModuleInstance{
{Name: "foo"},
},
},
SourceRange: tfdiags.SourceRange{
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 33, Byte: 32},
},
},
``,
},
{
`module.foo.module.bar.data.aws_instance.baz`,
&Target{
Subject: AbsResource{
Resource: Resource{
Mode: DataResourceMode,
Type: "aws_instance",
Name: "baz",
},
Module: ModuleInstance{
{Name: "foo"},
{Name: "bar"},
},
},
SourceRange: tfdiags.SourceRange{
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 44, Byte: 43},
},
},
``,
},
{
`module.foo.module.bar.data.aws_instance.baz["hello"]`,
&Target{
Subject: AbsResourceInstance{
Resource: ResourceInstance{
Resource: Resource{
Mode: DataResourceMode,
Type: "aws_instance",
Name: "baz",
},
Key: StringKey("hello"),
},
Module: ModuleInstance{
{Name: "foo"},
{Name: "bar"},
},
},
SourceRange: tfdiags.SourceRange{
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
End: tfdiags.SourcePos{Line: 1, Column: 53, Byte: 52},
},
},
``,
},
{
`aws_instance`,
nil,
`Resource specification must include a resource type and name.`,
},
{
`module`,
nil,
`Prefix "module." must be followed by a module name.`,
},
{
`module["baz"]`,
nil,
`Prefix "module." must be followed by a module name.`,
},
{
`module.baz.bar`,
nil,
`Resource specification must include a resource type and name.`,
},
{
`aws_instance.foo.bar`,
nil,
`Resource instance key must be given in square brackets.`,
},
{
`aws_instance.foo[1].baz`,
nil,
`Unexpected extra operators after address.`,
},
}
for _, test := range tests {
t.Run(test.Input, func(t *testing.T) {
traversal, travDiags := hclsyntax.ParseTraversalAbs([]byte(test.Input), "", hcl.Pos{Line: 1, Column: 1})
if travDiags.HasErrors() {
t.Fatal(travDiags.Error())
}
got, diags := ParseTarget(traversal)
switch len(diags) {
case 0:
if test.WantErr != "" {
t.Fatalf("succeeded; want error: %s", test.WantErr)
}
case 1:
if test.WantErr == "" {
t.Fatalf("unexpected diagnostics: %s", diags.Err())
}
if got, want := diags[0].Description().Detail, test.WantErr; got != want {
t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want)
}
default:
t.Fatalf("too many diagnostics: %s", diags.Err())
}
if diags.HasErrors() {
return
}
for _, problem := range deep.Equal(got, test.Want) {
t.Errorf(problem)
}
})
}
}