github.com/opentofu/opentofu@v1.7.1/internal/tofu/evaluate_valid_test.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package tofu 7 8 import ( 9 "testing" 10 11 "github.com/hashicorp/hcl/v2" 12 "github.com/hashicorp/hcl/v2/hclsyntax" 13 "github.com/zclconf/go-cty/cty" 14 15 "github.com/opentofu/opentofu/internal/addrs" 16 "github.com/opentofu/opentofu/internal/configs/configschema" 17 "github.com/opentofu/opentofu/internal/lang" 18 "github.com/opentofu/opentofu/internal/providers" 19 ) 20 21 func TestStaticValidateReferences(t *testing.T) { 22 tests := []struct { 23 Ref string 24 Src addrs.Referenceable 25 WantErr string 26 }{ 27 { 28 Ref: "aws_instance.no_count", 29 WantErr: ``, 30 }, 31 { 32 Ref: "aws_instance.count", 33 WantErr: ``, 34 }, 35 { 36 Ref: "aws_instance.count[0]", 37 WantErr: ``, 38 }, 39 { 40 Ref: "aws_instance.nonexist", 41 WantErr: `Reference to undeclared resource: A managed resource "aws_instance" "nonexist" has not been declared in the root module.`, 42 }, 43 { 44 Ref: "beep.boop", 45 WantErr: `Reference to undeclared resource: A managed resource "beep" "boop" has not been declared in the root module. 46 47 Did you mean the data resource data.beep.boop?`, 48 }, 49 { 50 Ref: "aws_instance.no_count[0]", 51 WantErr: `Unexpected resource instance key: Because aws_instance.no_count does not have "count" or "for_each" set, references to it must not include an index key. Remove the bracketed index to refer to the single instance of this resource.`, 52 }, 53 { 54 Ref: "aws_instance.count.foo", 55 // In this case we return two errors that are somewhat redundant with 56 // one another, but we'll accept that because they both report the 57 // problem from different perspectives and so give the user more 58 // opportunity to understand what's going on here. 59 WantErr: `2 problems: 60 61 - Missing resource instance key: Because aws_instance.count has "count" set, its attributes must be accessed on specific instances. 62 63 For example, to correlate with indices of a referring resource, use: 64 aws_instance.count[count.index] 65 - Unsupported attribute: This object has no argument, nested block, or exported attribute named "foo".`, 66 }, 67 { 68 Ref: "boop_instance.yep", 69 WantErr: ``, 70 }, 71 { 72 Ref: "boop_whatever.nope", 73 WantErr: `Invalid resource type: A managed resource type "boop_whatever" is not supported by provider "registry.opentofu.org/foobar/beep".`, 74 }, 75 { 76 Ref: "data.boop_data.boop_nested", 77 WantErr: `Reference to scoped resource: The referenced data resource "boop_data" "boop_nested" is not available from this context.`, 78 }, 79 { 80 Ref: "data.boop_data.boop_nested", 81 WantErr: ``, 82 Src: addrs.Check{Name: "foo"}, 83 }, 84 } 85 86 cfg := testModule(t, "static-validate-refs") 87 evaluator := &Evaluator{ 88 Config: cfg, 89 Plugins: schemaOnlyProvidersForTesting(map[addrs.Provider]providers.ProviderSchema{ 90 addrs.NewDefaultProvider("aws"): { 91 ResourceTypes: map[string]providers.Schema{ 92 "aws_instance": { 93 Block: &configschema.Block{}, 94 }, 95 }, 96 }, 97 addrs.MustParseProviderSourceString("foobar/beep"): { 98 ResourceTypes: map[string]providers.Schema{ 99 // intentional mismatch between resource type prefix and provider type 100 "boop_instance": { 101 Block: &configschema.Block{}, 102 }, 103 }, 104 DataSources: map[string]providers.Schema{ 105 "boop_data": { 106 Block: &configschema.Block{ 107 Attributes: map[string]*configschema.Attribute{ 108 "id": { 109 Type: cty.String, 110 Optional: true, 111 }, 112 }, 113 }, 114 }, 115 }, 116 }, 117 }, t), 118 } 119 120 for _, test := range tests { 121 t.Run(test.Ref, func(t *testing.T) { 122 traversal, hclDiags := hclsyntax.ParseTraversalAbs([]byte(test.Ref), "", hcl.Pos{Line: 1, Column: 1}) 123 if hclDiags.HasErrors() { 124 t.Fatal(hclDiags.Error()) 125 } 126 127 refs, diags := lang.References(addrs.ParseRef, []hcl.Traversal{traversal}) 128 if diags.HasErrors() { 129 t.Fatal(diags.Err()) 130 } 131 132 data := &evaluationStateData{ 133 Evaluator: evaluator, 134 } 135 136 diags = data.StaticValidateReferences(refs, nil, test.Src) 137 if diags.HasErrors() { 138 if test.WantErr == "" { 139 t.Fatalf("Unexpected diagnostics: %s", diags.Err()) 140 } 141 142 gotErr := diags.Err().Error() 143 if gotErr != test.WantErr { 144 t.Fatalf("Wrong diagnostics\ngot: %s\nwant: %s", gotErr, test.WantErr) 145 } 146 return 147 } 148 149 if test.WantErr != "" { 150 t.Fatalf("Expected diagnostics, but got none\nwant: %s", test.WantErr) 151 } 152 }) 153 } 154 }