github.com/opentofu/opentofu@v1.7.1/internal/tofu/variables_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/opentofu/opentofu/internal/configs" 12 "github.com/zclconf/go-cty/cty" 13 ) 14 15 func TestCheckInputVariables(t *testing.T) { 16 c := testModule(t, "input-variables") 17 18 t.Run("No variables set", func(t *testing.T) { 19 // No variables set 20 diags := checkInputVariables(c.Module.Variables, nil) 21 if !diags.HasErrors() { 22 t.Fatal("check succeeded, but want errors") 23 } 24 25 // Required variables set, optional variables unset 26 // This is still an error at this layer, since it's the caller's 27 // responsibility to have already merged in any default values. 28 diags = checkInputVariables(c.Module.Variables, InputValues{ 29 "foo": &InputValue{ 30 Value: cty.StringVal("bar"), 31 SourceType: ValueFromCLIArg, 32 }, 33 }) 34 if !diags.HasErrors() { 35 t.Fatal("check succeeded, but want errors") 36 } 37 }) 38 39 t.Run("All variables set", func(t *testing.T) { 40 diags := checkInputVariables(c.Module.Variables, InputValues{ 41 "foo": &InputValue{ 42 Value: cty.StringVal("bar"), 43 SourceType: ValueFromCLIArg, 44 }, 45 "bar": &InputValue{ 46 Value: cty.StringVal("baz"), 47 SourceType: ValueFromCLIArg, 48 }, 49 "map": &InputValue{ 50 Value: cty.StringVal("baz"), // okay because config has no type constraint 51 SourceType: ValueFromCLIArg, 52 }, 53 "object_map": &InputValue{ 54 Value: cty.MapVal(map[string]cty.Value{ 55 "uno": cty.ObjectVal(map[string]cty.Value{ 56 "foo": cty.StringVal("baz"), 57 "bar": cty.NumberIntVal(2), // type = any 58 }), 59 "dos": cty.ObjectVal(map[string]cty.Value{ 60 "foo": cty.StringVal("bat"), 61 "bar": cty.NumberIntVal(99), // type = any 62 }), 63 }), 64 SourceType: ValueFromCLIArg, 65 }, 66 "object_list": &InputValue{ 67 Value: cty.ListVal([]cty.Value{ 68 cty.ObjectVal(map[string]cty.Value{ 69 "foo": cty.StringVal("baz"), 70 "bar": cty.NumberIntVal(2), // type = any 71 }), 72 cty.ObjectVal(map[string]cty.Value{ 73 "foo": cty.StringVal("bang"), 74 "bar": cty.NumberIntVal(42), // type = any 75 }), 76 }), 77 SourceType: ValueFromCLIArg, 78 }, 79 }) 80 if diags.HasErrors() { 81 t.Fatalf("unexpected errors: %s", diags.Err()) 82 } 83 }) 84 85 t.Run("Invalid Complex Types", func(t *testing.T) { 86 diags := checkInputVariables(c.Module.Variables, InputValues{ 87 "foo": &InputValue{ 88 Value: cty.StringVal("bar"), 89 SourceType: ValueFromCLIArg, 90 }, 91 "bar": &InputValue{ 92 Value: cty.StringVal("baz"), 93 SourceType: ValueFromCLIArg, 94 }, 95 "map": &InputValue{ 96 Value: cty.StringVal("baz"), // okay because config has no type constraint 97 SourceType: ValueFromCLIArg, 98 }, 99 "object_map": &InputValue{ 100 Value: cty.MapVal(map[string]cty.Value{ 101 "uno": cty.ObjectVal(map[string]cty.Value{ 102 "foo": cty.StringVal("baz"), 103 "bar": cty.NumberIntVal(2), // type = any 104 }), 105 "dos": cty.ObjectVal(map[string]cty.Value{ 106 "foo": cty.StringVal("bat"), 107 "bar": cty.NumberIntVal(99), // type = any 108 }), 109 }), 110 SourceType: ValueFromCLIArg, 111 }, 112 "object_list": &InputValue{ 113 Value: cty.TupleVal([]cty.Value{ 114 cty.ObjectVal(map[string]cty.Value{ 115 "foo": cty.StringVal("baz"), 116 "bar": cty.NumberIntVal(2), // type = any 117 }), 118 cty.ObjectVal(map[string]cty.Value{ 119 "foo": cty.StringVal("bang"), 120 "bar": cty.StringVal("42"), // type = any, but mismatch with the first list item 121 }), 122 }), 123 SourceType: ValueFromCLIArg, 124 }, 125 }) 126 127 if diags.HasErrors() { 128 t.Fatalf("unexpected errors: %s", diags.Err()) 129 } 130 }) 131 } 132 133 // testInputValuesUnset is a helper for constructing InputValues values for 134 // situations where all of the root module variables are optional and a 135 // test case intends to just use those default values and not override them 136 // at all. 137 // 138 // In other words, this constructs an InputValues with one entry per given 139 // input variable declaration where all of them are declared as unset. 140 func testInputValuesUnset(decls map[string]*configs.Variable) InputValues { 141 if len(decls) == 0 { 142 return nil 143 } 144 145 ret := make(InputValues, len(decls)) 146 for name := range decls { 147 ret[name] = &InputValue{ 148 Value: cty.NilVal, 149 SourceType: ValueFromUnknown, 150 } 151 } 152 return ret 153 }