mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
This is part of a general effort to move all of Terraform's non-library package surface under internal in order to reinforce that these are for internal use within Terraform only. If you were previously importing packages under this prefix into an external codebase, you could pin to an earlier release tag as an interim solution until you've make a plan to achieve the same functionality some other way.
756 lines
17 KiB
Go
756 lines
17 KiB
Go
package addrs
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/go-test/deep"
|
|
"github.com/hashicorp/hcl/v2"
|
|
"github.com/hashicorp/hcl/v2/hclsyntax"
|
|
"github.com/hashicorp/terraform/internal/tfdiags"
|
|
"github.com/zclconf/go-cty/cty"
|
|
)
|
|
|
|
func TestParseRef(t *testing.T) {
|
|
tests := []struct {
|
|
Input string
|
|
Want *Reference
|
|
WantErr string
|
|
}{
|
|
|
|
// count
|
|
{
|
|
`count.index`,
|
|
&Reference{
|
|
Subject: CountAttr{
|
|
Name: "index",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 12, Byte: 11},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`count.index.blah`,
|
|
&Reference{
|
|
Subject: CountAttr{
|
|
Name: "index",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 12, Byte: 11},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseAttr{
|
|
Name: "blah",
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 12, Byte: 11},
|
|
End: hcl.Pos{Line: 1, Column: 17, Byte: 16},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``, // valid at this layer, but will fail during eval because "index" is a number
|
|
},
|
|
{
|
|
`count`,
|
|
nil,
|
|
`The "count" object cannot be accessed directly. Instead, access one of its attributes.`,
|
|
},
|
|
{
|
|
`count["hello"]`,
|
|
nil,
|
|
`The "count" object does not support this operation.`,
|
|
},
|
|
|
|
// each
|
|
{
|
|
`each.key`,
|
|
&Reference{
|
|
Subject: ForEachAttr{
|
|
Name: "key",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 9, Byte: 8},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`each.value.blah`,
|
|
&Reference{
|
|
Subject: ForEachAttr{
|
|
Name: "value",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 11, Byte: 10},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseAttr{
|
|
Name: "blah",
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 11, Byte: 10},
|
|
End: hcl.Pos{Line: 1, Column: 16, Byte: 15},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`each`,
|
|
nil,
|
|
`The "each" object cannot be accessed directly. Instead, access one of its attributes.`,
|
|
},
|
|
{
|
|
`each["hello"]`,
|
|
nil,
|
|
`The "each" object does not support this operation.`,
|
|
},
|
|
// data
|
|
{
|
|
`data.external.foo`,
|
|
&Reference{
|
|
Subject: Resource{
|
|
Mode: DataResourceMode,
|
|
Type: "external",
|
|
Name: "foo",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 18, Byte: 17},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`data.external.foo.bar`,
|
|
&Reference{
|
|
Subject: ResourceInstance{
|
|
Resource: Resource{
|
|
Mode: DataResourceMode,
|
|
Type: "external",
|
|
Name: "foo",
|
|
},
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 18, Byte: 17},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseAttr{
|
|
Name: "bar",
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 18, Byte: 17},
|
|
End: hcl.Pos{Line: 1, Column: 22, Byte: 21},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`data.external.foo["baz"].bar`,
|
|
&Reference{
|
|
Subject: ResourceInstance{
|
|
Resource: Resource{
|
|
Mode: DataResourceMode,
|
|
Type: "external",
|
|
Name: "foo",
|
|
},
|
|
Key: StringKey("baz"),
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 25, Byte: 24},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseAttr{
|
|
Name: "bar",
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 25, Byte: 24},
|
|
End: hcl.Pos{Line: 1, Column: 29, Byte: 28},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`data.external.foo["baz"]`,
|
|
&Reference{
|
|
Subject: ResourceInstance{
|
|
Resource: Resource{
|
|
Mode: DataResourceMode,
|
|
Type: "external",
|
|
Name: "foo",
|
|
},
|
|
Key: StringKey("baz"),
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 25, Byte: 24},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`data`,
|
|
nil,
|
|
`The "data" object must be followed by two attribute names: the data source type and the resource name.`,
|
|
},
|
|
{
|
|
`data.external`,
|
|
nil,
|
|
`The "data" object must be followed by two attribute names: the data source type and the resource name.`,
|
|
},
|
|
|
|
// local
|
|
{
|
|
`local.foo`,
|
|
&Reference{
|
|
Subject: LocalValue{
|
|
Name: "foo",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 10, Byte: 9},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`local.foo.blah`,
|
|
&Reference{
|
|
Subject: LocalValue{
|
|
Name: "foo",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 10, Byte: 9},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseAttr{
|
|
Name: "blah",
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 10, Byte: 9},
|
|
End: hcl.Pos{Line: 1, Column: 15, Byte: 14},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`local.foo["blah"]`,
|
|
&Reference{
|
|
Subject: LocalValue{
|
|
Name: "foo",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 10, Byte: 9},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseIndex{
|
|
Key: cty.StringVal("blah"),
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 10, Byte: 9},
|
|
End: hcl.Pos{Line: 1, Column: 18, Byte: 17},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`local`,
|
|
nil,
|
|
`The "local" object cannot be accessed directly. Instead, access one of its attributes.`,
|
|
},
|
|
{
|
|
`local["foo"]`,
|
|
nil,
|
|
`The "local" object does not support this operation.`,
|
|
},
|
|
|
|
// module
|
|
{
|
|
`module.foo`,
|
|
&Reference{
|
|
Subject: ModuleCall{
|
|
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.bar`,
|
|
&Reference{
|
|
Subject: AbsModuleCallOutput{
|
|
Call: ModuleCallInstance{
|
|
Call: ModuleCall{
|
|
Name: "foo",
|
|
},
|
|
},
|
|
Name: "bar",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 15, Byte: 14},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`module.foo.bar.baz`,
|
|
&Reference{
|
|
Subject: AbsModuleCallOutput{
|
|
Call: ModuleCallInstance{
|
|
Call: ModuleCall{
|
|
Name: "foo",
|
|
},
|
|
},
|
|
Name: "bar",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 15, Byte: 14},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseAttr{
|
|
Name: "baz",
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 15, Byte: 14},
|
|
End: hcl.Pos{Line: 1, Column: 19, Byte: 18},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`module.foo["baz"]`,
|
|
&Reference{
|
|
Subject: ModuleCallInstance{
|
|
Call: ModuleCall{
|
|
Name: "foo",
|
|
},
|
|
Key: StringKey("baz"),
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 18, Byte: 17},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`module.foo["baz"].bar`,
|
|
&Reference{
|
|
Subject: AbsModuleCallOutput{
|
|
Call: ModuleCallInstance{
|
|
Call: ModuleCall{
|
|
Name: "foo",
|
|
},
|
|
Key: StringKey("baz"),
|
|
},
|
|
Name: "bar",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 22, Byte: 21},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`module.foo["baz"].bar.boop`,
|
|
&Reference{
|
|
Subject: AbsModuleCallOutput{
|
|
Call: ModuleCallInstance{
|
|
Call: ModuleCall{
|
|
Name: "foo",
|
|
},
|
|
Key: StringKey("baz"),
|
|
},
|
|
Name: "bar",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 22, Byte: 21},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseAttr{
|
|
Name: "boop",
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 22, Byte: 21},
|
|
End: hcl.Pos{Line: 1, Column: 27, Byte: 26},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`module`,
|
|
nil,
|
|
`The "module" object cannot be accessed directly. Instead, access one of its attributes.`,
|
|
},
|
|
{
|
|
`module["foo"]`,
|
|
nil,
|
|
`The "module" object does not support this operation.`,
|
|
},
|
|
|
|
// path
|
|
{
|
|
`path.module`,
|
|
&Reference{
|
|
Subject: PathAttr{
|
|
Name: "module",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 12, Byte: 11},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`path.module.blah`,
|
|
&Reference{
|
|
Subject: PathAttr{
|
|
Name: "module",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 12, Byte: 11},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseAttr{
|
|
Name: "blah",
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 12, Byte: 11},
|
|
End: hcl.Pos{Line: 1, Column: 17, Byte: 16},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``, // valid at this layer, but will fail during eval because "module" is a string
|
|
},
|
|
{
|
|
`path`,
|
|
nil,
|
|
`The "path" object cannot be accessed directly. Instead, access one of its attributes.`,
|
|
},
|
|
{
|
|
`path["module"]`,
|
|
nil,
|
|
`The "path" object does not support this operation.`,
|
|
},
|
|
|
|
// self
|
|
{
|
|
`self`,
|
|
&Reference{
|
|
Subject: Self,
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 5, Byte: 4},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`self.blah`,
|
|
&Reference{
|
|
Subject: Self,
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 5, Byte: 4},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseAttr{
|
|
Name: "blah",
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 5, Byte: 4},
|
|
End: hcl.Pos{Line: 1, Column: 10, Byte: 9},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
|
|
// terraform
|
|
{
|
|
`terraform.workspace`,
|
|
&Reference{
|
|
Subject: TerraformAttr{
|
|
Name: "workspace",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 20, Byte: 19},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`terraform.workspace.blah`,
|
|
&Reference{
|
|
Subject: TerraformAttr{
|
|
Name: "workspace",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 20, Byte: 19},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseAttr{
|
|
Name: "blah",
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 20, Byte: 19},
|
|
End: hcl.Pos{Line: 1, Column: 25, Byte: 24},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``, // valid at this layer, but will fail during eval because "workspace" is a string
|
|
},
|
|
{
|
|
`terraform`,
|
|
nil,
|
|
`The "terraform" object cannot be accessed directly. Instead, access one of its attributes.`,
|
|
},
|
|
{
|
|
`terraform["workspace"]`,
|
|
nil,
|
|
`The "terraform" object does not support this operation.`,
|
|
},
|
|
|
|
// var
|
|
{
|
|
`var.foo`,
|
|
&Reference{
|
|
Subject: InputVariable{
|
|
Name: "foo",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 8, Byte: 7},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`var.foo.blah`,
|
|
&Reference{
|
|
Subject: InputVariable{
|
|
Name: "foo",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 8, Byte: 7},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseAttr{
|
|
Name: "blah",
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 8, Byte: 7},
|
|
End: hcl.Pos{Line: 1, Column: 13, Byte: 12},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``, // valid at this layer, but will fail during eval because "module" is a string
|
|
},
|
|
{
|
|
`var`,
|
|
nil,
|
|
`The "var" object cannot be accessed directly. Instead, access one of its attributes.`,
|
|
},
|
|
{
|
|
`var["foo"]`,
|
|
nil,
|
|
`The "var" object does not support this operation.`,
|
|
},
|
|
|
|
// the "resource" prefix forces interpreting the next name as a
|
|
// resource type name. This is an alias for just using a resource
|
|
// type name at the top level, to be used only if a later edition
|
|
// of the Terraform language introduces a new reserved word that
|
|
// overlaps with a resource type name.
|
|
{
|
|
`resource.boop_instance.foo`,
|
|
&Reference{
|
|
Subject: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "boop_instance",
|
|
Name: "foo",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 27, Byte: 26},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
|
|
// We have some names reserved which might be used by a
|
|
// still-under-discussion proposal for template values or lazy
|
|
// expressions.
|
|
{
|
|
`template.foo`,
|
|
nil,
|
|
`The symbol name "template" is reserved for use in a future Terraform version. If you are using a provider that already uses this as a resource type name, add the prefix "resource." to force interpretation as a resource type name.`,
|
|
},
|
|
{
|
|
`lazy.foo`,
|
|
nil,
|
|
`The symbol name "lazy" is reserved for use in a future Terraform version. If you are using a provider that already uses this as a resource type name, add the prefix "resource." to force interpretation as a resource type name.`,
|
|
},
|
|
{
|
|
`arg.foo`,
|
|
nil,
|
|
`The symbol name "arg" is reserved for use in a future Terraform version. If you are using a provider that already uses this as a resource type name, add the prefix "resource." to force interpretation as a resource type name.`,
|
|
},
|
|
|
|
// anything else, interpreted as a managed resource reference
|
|
{
|
|
`boop_instance.foo`,
|
|
&Reference{
|
|
Subject: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "boop_instance",
|
|
Name: "foo",
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 18, Byte: 17},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`boop_instance.foo.bar`,
|
|
&Reference{
|
|
Subject: ResourceInstance{
|
|
Resource: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "boop_instance",
|
|
Name: "foo",
|
|
},
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 18, Byte: 17},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseAttr{
|
|
Name: "bar",
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 18, Byte: 17},
|
|
End: hcl.Pos{Line: 1, Column: 22, Byte: 21},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`boop_instance.foo["baz"].bar`,
|
|
&Reference{
|
|
Subject: ResourceInstance{
|
|
Resource: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "boop_instance",
|
|
Name: "foo",
|
|
},
|
|
Key: StringKey("baz"),
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 25, Byte: 24},
|
|
},
|
|
Remaining: hcl.Traversal{
|
|
hcl.TraverseAttr{
|
|
Name: "bar",
|
|
SrcRange: hcl.Range{
|
|
Start: hcl.Pos{Line: 1, Column: 25, Byte: 24},
|
|
End: hcl.Pos{Line: 1, Column: 29, Byte: 28},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`boop_instance.foo["baz"]`,
|
|
&Reference{
|
|
Subject: ResourceInstance{
|
|
Resource: Resource{
|
|
Mode: ManagedResourceMode,
|
|
Type: "boop_instance",
|
|
Name: "foo",
|
|
},
|
|
Key: StringKey("baz"),
|
|
},
|
|
SourceRange: tfdiags.SourceRange{
|
|
Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
|
|
End: tfdiags.SourcePos{Line: 1, Column: 25, Byte: 24},
|
|
},
|
|
},
|
|
``,
|
|
},
|
|
{
|
|
`boop_instance`,
|
|
nil,
|
|
`A reference to a resource type must be followed by at least one attribute access, specifying the resource name.`,
|
|
},
|
|
}
|
|
|
|
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 := ParseRef(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)
|
|
}
|
|
})
|
|
}
|
|
}
|