mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-26 17:01:04 -06:00
ab350289ab
The previous name didn't fit with the naming scheme for addrs types: The "Abs" prefix typically means that it's an addrs.ModuleInstance combined with whatever type name appears after "Abs", but this is instead a ModuleCallOutput combined with an InstanceKey, albeit structured the other way around for convenience, and so the expected name for this would be the suffix "Instance". We don't have an "Abs" type corresponding with this one because it would represent no additional information than AbsOutputValue.
756 lines
18 KiB
Go
756 lines
18 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: ModuleCallInstanceOutput{
|
|
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: ModuleCallInstanceOutput{
|
|
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: ModuleCallInstanceOutput{
|
|
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: ModuleCallInstanceOutput{
|
|
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)
|
|
}
|
|
})
|
|
}
|
|
}
|