github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/plans/objchange/normalize_obj_test.go (about) 1 package objchange 2 3 import ( 4 "testing" 5 6 "github.com/apparentlymart/go-dump/dump" 7 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" 8 "github.com/zclconf/go-cty/cty" 9 ) 10 11 func TestNormalizeObjectFromLegacySDK(t *testing.T) { 12 tests := map[string]struct { 13 Schema *configschema.Block 14 Input cty.Value 15 Want cty.Value 16 }{ 17 "empty": { 18 &configschema.Block{}, 19 cty.EmptyObjectVal, 20 cty.EmptyObjectVal, 21 }, 22 "attributes only": { 23 &configschema.Block{ 24 Attributes: map[string]*configschema.Attribute{ 25 "a": {Type: cty.String, Required: true}, 26 "b": {Type: cty.String, Optional: true}, 27 }, 28 }, 29 cty.ObjectVal(map[string]cty.Value{ 30 "a": cty.StringVal("a value"), 31 "b": cty.StringVal("b value"), 32 }), 33 cty.ObjectVal(map[string]cty.Value{ 34 "a": cty.StringVal("a value"), 35 "b": cty.StringVal("b value"), 36 }), 37 }, 38 "null block single": { 39 &configschema.Block{ 40 BlockTypes: map[string]*configschema.NestedBlock{ 41 "a": { 42 Nesting: configschema.NestingSingle, 43 Block: configschema.Block{ 44 Attributes: map[string]*configschema.Attribute{ 45 "b": {Type: cty.String, Optional: true}, 46 }, 47 }, 48 }, 49 }, 50 }, 51 cty.ObjectVal(map[string]cty.Value{ 52 "a": cty.NullVal(cty.Object(map[string]cty.Type{ 53 "b": cty.String, 54 })), 55 }), 56 cty.ObjectVal(map[string]cty.Value{ 57 "a": cty.NullVal(cty.Object(map[string]cty.Type{ 58 "b": cty.String, 59 })), 60 }), 61 }, 62 "unknown block single": { 63 &configschema.Block{ 64 BlockTypes: map[string]*configschema.NestedBlock{ 65 "a": { 66 Nesting: configschema.NestingSingle, 67 Block: configschema.Block{ 68 Attributes: map[string]*configschema.Attribute{ 69 "b": {Type: cty.String, Optional: true}, 70 }, 71 BlockTypes: map[string]*configschema.NestedBlock{ 72 "c": {Nesting: configschema.NestingSingle}, 73 }, 74 }, 75 }, 76 }, 77 }, 78 cty.ObjectVal(map[string]cty.Value{ 79 "a": cty.UnknownVal(cty.Object(map[string]cty.Type{ 80 "b": cty.String, 81 "c": cty.EmptyObject, 82 })), 83 }), 84 cty.ObjectVal(map[string]cty.Value{ 85 "a": cty.ObjectVal(map[string]cty.Value{ 86 "b": cty.UnknownVal(cty.String), 87 "c": cty.EmptyObjectVal, 88 }), 89 }), 90 }, 91 "null block list": { 92 &configschema.Block{ 93 BlockTypes: map[string]*configschema.NestedBlock{ 94 "a": { 95 Nesting: configschema.NestingList, 96 Block: configschema.Block{ 97 Attributes: map[string]*configschema.Attribute{ 98 "b": {Type: cty.String, Optional: true}, 99 }, 100 BlockTypes: map[string]*configschema.NestedBlock{ 101 "c": {Nesting: configschema.NestingSingle}, 102 }, 103 }, 104 }, 105 }, 106 }, 107 cty.ObjectVal(map[string]cty.Value{ 108 "a": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ 109 "b": cty.String, 110 "c": cty.EmptyObject, 111 }))), 112 }), 113 cty.ObjectVal(map[string]cty.Value{ 114 "a": cty.ListValEmpty(cty.Object(map[string]cty.Type{ 115 "b": cty.String, 116 "c": cty.EmptyObject, 117 })), 118 }), 119 }, 120 "unknown block list": { 121 &configschema.Block{ 122 BlockTypes: map[string]*configschema.NestedBlock{ 123 "a": { 124 Nesting: configschema.NestingList, 125 Block: configschema.Block{ 126 Attributes: map[string]*configschema.Attribute{ 127 "b": {Type: cty.String, Optional: true}, 128 }, 129 }, 130 }, 131 }, 132 }, 133 cty.ObjectVal(map[string]cty.Value{ 134 "a": cty.UnknownVal(cty.List(cty.Object(map[string]cty.Type{ 135 "b": cty.String, 136 }))), 137 }), 138 cty.ObjectVal(map[string]cty.Value{ 139 "a": cty.ListVal([]cty.Value{ 140 cty.ObjectVal(map[string]cty.Value{ 141 "b": cty.UnknownVal(cty.String), 142 }), 143 }), 144 }), 145 }, 146 "null block set": { 147 &configschema.Block{ 148 BlockTypes: map[string]*configschema.NestedBlock{ 149 "a": { 150 Nesting: configschema.NestingSet, 151 Block: configschema.Block{ 152 Attributes: map[string]*configschema.Attribute{ 153 "b": {Type: cty.String, Optional: true}, 154 }, 155 }, 156 }, 157 }, 158 }, 159 cty.ObjectVal(map[string]cty.Value{ 160 "a": cty.NullVal(cty.Set(cty.Object(map[string]cty.Type{ 161 "b": cty.String, 162 }))), 163 }), 164 cty.ObjectVal(map[string]cty.Value{ 165 "a": cty.SetValEmpty(cty.Object(map[string]cty.Type{ 166 "b": cty.String, 167 })), 168 }), 169 }, 170 "unknown block set": { 171 &configschema.Block{ 172 BlockTypes: map[string]*configschema.NestedBlock{ 173 "a": { 174 Nesting: configschema.NestingSet, 175 Block: configschema.Block{ 176 Attributes: map[string]*configschema.Attribute{ 177 "b": {Type: cty.String, Optional: true}, 178 }, 179 }, 180 }, 181 }, 182 }, 183 cty.ObjectVal(map[string]cty.Value{ 184 "a": cty.UnknownVal(cty.Set(cty.Object(map[string]cty.Type{ 185 "b": cty.String, 186 }))), 187 }), 188 cty.ObjectVal(map[string]cty.Value{ 189 "a": cty.SetVal([]cty.Value{ 190 cty.ObjectVal(map[string]cty.Value{ 191 "b": cty.UnknownVal(cty.String), 192 }), 193 }), 194 }), 195 }, 196 "map block passes through": { 197 // Legacy SDK doesn't use NestingMap, so we don't do any transforms 198 // related to it but we still need to verify that map blocks pass 199 // through unscathed. 200 &configschema.Block{ 201 BlockTypes: map[string]*configschema.NestedBlock{ 202 "a": { 203 Nesting: configschema.NestingMap, 204 Block: configschema.Block{ 205 Attributes: map[string]*configschema.Attribute{ 206 "b": {Type: cty.String, Optional: true}, 207 }, 208 }, 209 }, 210 }, 211 }, 212 cty.ObjectVal(map[string]cty.Value{ 213 "a": cty.MapVal(map[string]cty.Value{ 214 "foo": cty.ObjectVal(map[string]cty.Value{ 215 "b": cty.StringVal("b value"), 216 }), 217 }), 218 }), 219 cty.ObjectVal(map[string]cty.Value{ 220 "a": cty.MapVal(map[string]cty.Value{ 221 "foo": cty.ObjectVal(map[string]cty.Value{ 222 "b": cty.StringVal("b value"), 223 }), 224 }), 225 }), 226 }, 227 "block list with dynamic type": { 228 &configschema.Block{ 229 BlockTypes: map[string]*configschema.NestedBlock{ 230 "a": { 231 Nesting: configschema.NestingList, 232 Block: configschema.Block{ 233 Attributes: map[string]*configschema.Attribute{ 234 "b": {Type: cty.DynamicPseudoType, Optional: true}, 235 }, 236 }, 237 }, 238 }, 239 }, 240 cty.ObjectVal(map[string]cty.Value{ 241 "a": cty.TupleVal([]cty.Value{ 242 cty.ObjectVal(map[string]cty.Value{ 243 "b": cty.StringVal("hello"), 244 }), 245 cty.ObjectVal(map[string]cty.Value{ 246 "b": cty.True, 247 }), 248 }), 249 }), 250 cty.ObjectVal(map[string]cty.Value{ 251 "a": cty.TupleVal([]cty.Value{ 252 cty.ObjectVal(map[string]cty.Value{ 253 "b": cty.StringVal("hello"), 254 }), 255 cty.ObjectVal(map[string]cty.Value{ 256 "b": cty.True, 257 }), 258 }), 259 }), 260 }, 261 "block map with dynamic type": { 262 &configschema.Block{ 263 BlockTypes: map[string]*configschema.NestedBlock{ 264 "a": { 265 Nesting: configschema.NestingMap, 266 Block: configschema.Block{ 267 Attributes: map[string]*configschema.Attribute{ 268 "b": {Type: cty.DynamicPseudoType, Optional: true}, 269 }, 270 }, 271 }, 272 }, 273 }, 274 cty.ObjectVal(map[string]cty.Value{ 275 "a": cty.ObjectVal(map[string]cty.Value{ 276 "one": cty.ObjectVal(map[string]cty.Value{ 277 "b": cty.StringVal("hello"), 278 }), 279 "another": cty.ObjectVal(map[string]cty.Value{ 280 "b": cty.True, 281 }), 282 }), 283 }), 284 cty.ObjectVal(map[string]cty.Value{ 285 "a": cty.ObjectVal(map[string]cty.Value{ 286 "one": cty.ObjectVal(map[string]cty.Value{ 287 "b": cty.StringVal("hello"), 288 }), 289 "another": cty.ObjectVal(map[string]cty.Value{ 290 "b": cty.True, 291 }), 292 }), 293 }), 294 }, 295 } 296 297 for name, test := range tests { 298 t.Run(name, func(t *testing.T) { 299 got := NormalizeObjectFromLegacySDK(test.Input, test.Schema) 300 if !got.RawEquals(test.Want) { 301 t.Errorf( 302 "wrong result\ngot: %s\nwant: %s", 303 dump.Value(got), dump.Value(test.Want), 304 ) 305 } 306 }) 307 } 308 }