github.com/opentofu/opentofu@v1.7.1/internal/configs/configschema/empty_value_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 configschema 7 8 import ( 9 "fmt" 10 "testing" 11 12 "github.com/apparentlymart/go-dump/dump" 13 "github.com/davecgh/go-spew/spew" 14 "github.com/zclconf/go-cty/cty" 15 ) 16 17 func TestBlockEmptyValue(t *testing.T) { 18 tests := []struct { 19 Schema *Block 20 Want cty.Value 21 }{ 22 { 23 &Block{}, 24 cty.EmptyObjectVal, 25 }, 26 { 27 &Block{ 28 Attributes: map[string]*Attribute{ 29 "str": {Type: cty.String, Required: true}, 30 }, 31 }, 32 cty.ObjectVal(map[string]cty.Value{ 33 "str": cty.NullVal(cty.String), 34 }), 35 }, 36 { 37 &Block{ 38 BlockTypes: map[string]*NestedBlock{ 39 "single": { 40 Nesting: NestingSingle, 41 Block: Block{ 42 Attributes: map[string]*Attribute{ 43 "str": {Type: cty.String, Required: true}, 44 }, 45 }, 46 }, 47 }, 48 }, 49 cty.ObjectVal(map[string]cty.Value{ 50 "single": cty.NullVal(cty.Object(map[string]cty.Type{ 51 "str": cty.String, 52 })), 53 }), 54 }, 55 { 56 &Block{ 57 BlockTypes: map[string]*NestedBlock{ 58 "group": { 59 Nesting: NestingGroup, 60 Block: Block{ 61 Attributes: map[string]*Attribute{ 62 "str": {Type: cty.String, Required: true}, 63 }, 64 }, 65 }, 66 }, 67 }, 68 cty.ObjectVal(map[string]cty.Value{ 69 "group": cty.ObjectVal(map[string]cty.Value{ 70 "str": cty.NullVal(cty.String), 71 }), 72 }), 73 }, 74 { 75 &Block{ 76 BlockTypes: map[string]*NestedBlock{ 77 "list": { 78 Nesting: NestingList, 79 Block: Block{ 80 Attributes: map[string]*Attribute{ 81 "str": {Type: cty.String, Required: true}, 82 }, 83 }, 84 }, 85 }, 86 }, 87 cty.ObjectVal(map[string]cty.Value{ 88 "list": cty.ListValEmpty(cty.Object(map[string]cty.Type{ 89 "str": cty.String, 90 })), 91 }), 92 }, 93 { 94 &Block{ 95 BlockTypes: map[string]*NestedBlock{ 96 "list_dynamic": { 97 Nesting: NestingList, 98 Block: Block{ 99 Attributes: map[string]*Attribute{ 100 "str": {Type: cty.DynamicPseudoType, Required: true}, 101 }, 102 }, 103 }, 104 }, 105 }, 106 cty.ObjectVal(map[string]cty.Value{ 107 "list_dynamic": cty.EmptyTupleVal, 108 }), 109 }, 110 { 111 &Block{ 112 BlockTypes: map[string]*NestedBlock{ 113 "map": { 114 Nesting: NestingMap, 115 Block: Block{ 116 Attributes: map[string]*Attribute{ 117 "str": {Type: cty.String, Required: true}, 118 }, 119 }, 120 }, 121 }, 122 }, 123 cty.ObjectVal(map[string]cty.Value{ 124 "map": cty.MapValEmpty(cty.Object(map[string]cty.Type{ 125 "str": cty.String, 126 })), 127 }), 128 }, 129 { 130 &Block{ 131 BlockTypes: map[string]*NestedBlock{ 132 "map_dynamic": { 133 Nesting: NestingMap, 134 Block: Block{ 135 Attributes: map[string]*Attribute{ 136 "str": {Type: cty.DynamicPseudoType, Required: true}, 137 }, 138 }, 139 }, 140 }, 141 }, 142 cty.ObjectVal(map[string]cty.Value{ 143 "map_dynamic": cty.EmptyObjectVal, 144 }), 145 }, 146 { 147 &Block{ 148 BlockTypes: map[string]*NestedBlock{ 149 "set": { 150 Nesting: NestingSet, 151 Block: Block{ 152 Attributes: map[string]*Attribute{ 153 "str": {Type: cty.String, Required: true}, 154 }, 155 }, 156 }, 157 }, 158 }, 159 cty.ObjectVal(map[string]cty.Value{ 160 "set": cty.SetValEmpty(cty.Object(map[string]cty.Type{ 161 "str": cty.String, 162 })), 163 }), 164 }, 165 } 166 167 for _, test := range tests { 168 t.Run(fmt.Sprintf("%#v", test.Schema), func(t *testing.T) { 169 got := test.Schema.EmptyValue() 170 if !test.Want.RawEquals(got) { 171 t.Errorf("wrong result\nschema: %s\ngot: %s\nwant: %s", spew.Sdump(test.Schema), dump.Value(got), dump.Value(test.Want)) 172 } 173 }) 174 } 175 } 176 177 // Attribute EmptyValue() is well covered by the Block tests above; these tests 178 // focus on the behavior with NestedType field inside an Attribute 179 func TestAttributeEmptyValue(t *testing.T) { 180 tests := []struct { 181 Schema *Attribute 182 Want cty.Value 183 }{ 184 { 185 &Attribute{}, 186 cty.NilVal, 187 }, 188 { 189 &Attribute{ 190 Type: cty.String, 191 }, 192 cty.NullVal(cty.String), 193 }, 194 { 195 &Attribute{ 196 NestedType: &Object{ 197 Nesting: NestingSingle, 198 Attributes: map[string]*Attribute{ 199 "str": {Type: cty.String, Required: true}, 200 }, 201 }, 202 }, 203 cty.NullVal(cty.Object(map[string]cty.Type{ 204 "str": cty.String, 205 })), 206 }, 207 { 208 &Attribute{ 209 NestedType: &Object{ 210 Nesting: NestingList, 211 Attributes: map[string]*Attribute{ 212 "str": {Type: cty.String, Required: true}, 213 }, 214 }, 215 }, 216 cty.NullVal(cty.List( 217 cty.Object(map[string]cty.Type{ 218 "str": cty.String, 219 }), 220 )), 221 }, 222 { 223 &Attribute{ 224 NestedType: &Object{ 225 Nesting: NestingMap, 226 Attributes: map[string]*Attribute{ 227 "str": {Type: cty.String, Required: true}, 228 }, 229 }, 230 }, 231 cty.NullVal(cty.Map( 232 cty.Object(map[string]cty.Type{ 233 "str": cty.String, 234 }), 235 )), 236 }, 237 { 238 &Attribute{ 239 NestedType: &Object{ 240 Nesting: NestingSet, 241 Attributes: map[string]*Attribute{ 242 "str": {Type: cty.String, Required: true}, 243 }, 244 }, 245 }, 246 cty.NullVal(cty.Set( 247 cty.Object(map[string]cty.Type{ 248 "str": cty.String, 249 }), 250 )), 251 }, 252 } 253 254 for _, test := range tests { 255 t.Run(fmt.Sprintf("%#v", test.Schema), func(t *testing.T) { 256 got := test.Schema.EmptyValue() 257 if !test.Want.RawEquals(got) { 258 t.Errorf("wrong result\nschema: %s\ngot: %s\nwant: %s", spew.Sdump(test.Schema), dump.Value(got), dump.Value(test.Want)) 259 } 260 }) 261 } 262 }