github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/helper/plugin/unknown_test.go (about) 1 package plugin 2 3 import ( 4 "testing" 5 6 "github.com/hashicorp/terraform-plugin-sdk/internal/configs/configschema" 7 "github.com/zclconf/go-cty/cty" 8 ) 9 10 func TestSetUnknowns(t *testing.T) { 11 for n, tc := range map[string]struct { 12 Schema *configschema.Block 13 Val cty.Value 14 Expected cty.Value 15 }{ 16 "empty": { 17 &configschema.Block{}, 18 cty.EmptyObjectVal, 19 cty.EmptyObjectVal, 20 }, 21 "no prior": { 22 &configschema.Block{ 23 Attributes: map[string]*configschema.Attribute{ 24 "foo": { 25 Type: cty.String, 26 Optional: true, 27 }, 28 "bar": { 29 Type: cty.String, 30 Computed: true, 31 }, 32 }, 33 BlockTypes: map[string]*configschema.NestedBlock{ 34 "baz": { 35 Nesting: configschema.NestingSingle, 36 Block: configschema.Block{ 37 Attributes: map[string]*configschema.Attribute{ 38 "boz": { 39 Type: cty.String, 40 Optional: true, 41 Computed: true, 42 }, 43 "biz": { 44 Type: cty.String, 45 Optional: true, 46 Computed: true, 47 }, 48 }, 49 }, 50 }, 51 }, 52 }, 53 cty.NullVal(cty.Object(map[string]cty.Type{ 54 "foo": cty.String, 55 "bar": cty.String, 56 "baz": cty.Object(map[string]cty.Type{ 57 "boz": cty.String, 58 "biz": cty.String, 59 }), 60 })), 61 cty.ObjectVal(map[string]cty.Value{ 62 "foo": cty.NullVal(cty.String), 63 "bar": cty.UnknownVal(cty.String), 64 }), 65 }, 66 "null stays null": { 67 // if the object has no computed attributes, it should stay null 68 &configschema.Block{ 69 Attributes: map[string]*configschema.Attribute{ 70 "foo": { 71 Type: cty.String, 72 }, 73 }, 74 BlockTypes: map[string]*configschema.NestedBlock{ 75 "baz": { 76 Nesting: configschema.NestingSet, 77 Block: configschema.Block{ 78 Attributes: map[string]*configschema.Attribute{ 79 "boz": { 80 Type: cty.String, 81 Optional: true, 82 Computed: true, 83 }, 84 }, 85 }, 86 }, 87 }, 88 }, 89 cty.NullVal(cty.Object(map[string]cty.Type{ 90 "foo": cty.String, 91 "baz": cty.Set(cty.Object(map[string]cty.Type{ 92 "boz": cty.String, 93 })), 94 })), 95 cty.NullVal(cty.Object(map[string]cty.Type{ 96 "foo": cty.String, 97 "baz": cty.Set(cty.Object(map[string]cty.Type{ 98 "boz": cty.String, 99 })), 100 })), 101 }, 102 "no prior with set": { 103 // the set value should remain null 104 &configschema.Block{ 105 Attributes: map[string]*configschema.Attribute{ 106 "foo": { 107 Type: cty.String, 108 Computed: true, 109 }, 110 }, 111 BlockTypes: map[string]*configschema.NestedBlock{ 112 "baz": { 113 Nesting: configschema.NestingSet, 114 Block: configschema.Block{ 115 Attributes: map[string]*configschema.Attribute{ 116 "boz": { 117 Type: cty.String, 118 Optional: true, 119 Computed: true, 120 }, 121 }, 122 }, 123 }, 124 }, 125 }, 126 cty.NullVal(cty.Object(map[string]cty.Type{ 127 "foo": cty.String, 128 "baz": cty.Set(cty.Object(map[string]cty.Type{ 129 "boz": cty.String, 130 })), 131 })), 132 cty.ObjectVal(map[string]cty.Value{ 133 "foo": cty.UnknownVal(cty.String), 134 }), 135 }, 136 "prior attributes": { 137 &configschema.Block{ 138 Attributes: map[string]*configschema.Attribute{ 139 "foo": { 140 Type: cty.String, 141 Optional: true, 142 }, 143 "bar": { 144 Type: cty.String, 145 Computed: true, 146 }, 147 "baz": { 148 Type: cty.String, 149 Optional: true, 150 Computed: true, 151 }, 152 "boz": { 153 Type: cty.String, 154 Optional: true, 155 Computed: true, 156 }, 157 }, 158 }, 159 cty.ObjectVal(map[string]cty.Value{ 160 "foo": cty.StringVal("bonjour"), 161 "bar": cty.StringVal("petit dejeuner"), 162 "baz": cty.StringVal("grande dejeuner"), 163 }), 164 cty.ObjectVal(map[string]cty.Value{ 165 "foo": cty.StringVal("bonjour"), 166 "bar": cty.StringVal("petit dejeuner"), 167 "baz": cty.StringVal("grande dejeuner"), 168 "boz": cty.UnknownVal(cty.String), 169 }), 170 }, 171 "prior nested single": { 172 &configschema.Block{ 173 BlockTypes: map[string]*configschema.NestedBlock{ 174 "foo": { 175 Nesting: configschema.NestingSingle, 176 Block: configschema.Block{ 177 Attributes: map[string]*configschema.Attribute{ 178 "bar": { 179 Type: cty.String, 180 Optional: true, 181 Computed: true, 182 }, 183 "baz": { 184 Type: cty.String, 185 Optional: true, 186 Computed: true, 187 }, 188 }, 189 }, 190 }, 191 }, 192 }, 193 cty.ObjectVal(map[string]cty.Value{ 194 "foo": cty.ObjectVal(map[string]cty.Value{ 195 "bar": cty.StringVal("beep"), 196 }), 197 }), 198 cty.ObjectVal(map[string]cty.Value{ 199 "foo": cty.ObjectVal(map[string]cty.Value{ 200 "bar": cty.StringVal("beep"), 201 "baz": cty.UnknownVal(cty.String), 202 }), 203 }), 204 }, 205 "prior nested list": { 206 &configschema.Block{ 207 BlockTypes: map[string]*configschema.NestedBlock{ 208 "foo": { 209 Nesting: configschema.NestingList, 210 Block: configschema.Block{ 211 Attributes: map[string]*configschema.Attribute{ 212 "bar": { 213 Type: cty.String, 214 Optional: true, 215 Computed: true, 216 }, 217 "baz": { 218 Type: cty.String, 219 Optional: true, 220 Computed: true, 221 }, 222 }, 223 }, 224 }, 225 }, 226 }, 227 cty.ObjectVal(map[string]cty.Value{ 228 "foo": cty.ListVal([]cty.Value{ 229 cty.ObjectVal(map[string]cty.Value{ 230 "bar": cty.StringVal("bap"), 231 }), 232 cty.ObjectVal(map[string]cty.Value{ 233 "bar": cty.StringVal("blep"), 234 }), 235 }), 236 }), 237 cty.ObjectVal(map[string]cty.Value{ 238 "foo": cty.ListVal([]cty.Value{ 239 cty.ObjectVal(map[string]cty.Value{ 240 "bar": cty.StringVal("bap"), 241 "baz": cty.UnknownVal(cty.String), 242 }), 243 cty.ObjectVal(map[string]cty.Value{ 244 "bar": cty.StringVal("blep"), 245 "baz": cty.UnknownVal(cty.String), 246 }), 247 }), 248 }), 249 }, 250 "prior nested map": { 251 &configschema.Block{ 252 BlockTypes: map[string]*configschema.NestedBlock{ 253 "foo": { 254 Nesting: configschema.NestingMap, 255 Block: configschema.Block{ 256 Attributes: map[string]*configschema.Attribute{ 257 "bar": { 258 Type: cty.String, 259 Optional: true, 260 Computed: true, 261 }, 262 "baz": { 263 Type: cty.String, 264 Optional: true, 265 Computed: true, 266 }, 267 }, 268 }, 269 }, 270 }, 271 }, 272 cty.ObjectVal(map[string]cty.Value{ 273 "foo": cty.MapVal(map[string]cty.Value{ 274 "a": cty.ObjectVal(map[string]cty.Value{ 275 "bar": cty.NullVal(cty.String), 276 "baz": cty.StringVal("boop"), 277 }), 278 "b": cty.ObjectVal(map[string]cty.Value{ 279 "bar": cty.StringVal("blep"), 280 "baz": cty.NullVal(cty.String), 281 }), 282 }), 283 }), 284 cty.ObjectVal(map[string]cty.Value{ 285 "foo": cty.MapVal(map[string]cty.Value{ 286 "a": cty.ObjectVal(map[string]cty.Value{ 287 "bar": cty.UnknownVal(cty.String), 288 "baz": cty.StringVal("boop"), 289 }), 290 "b": cty.ObjectVal(map[string]cty.Value{ 291 "bar": cty.StringVal("blep"), 292 "baz": cty.UnknownVal(cty.String), 293 }), 294 }), 295 }), 296 }, 297 "prior nested set": { 298 &configschema.Block{ 299 BlockTypes: map[string]*configschema.NestedBlock{ 300 "foo": { 301 Nesting: configschema.NestingSet, 302 Block: configschema.Block{ 303 Attributes: map[string]*configschema.Attribute{ 304 "bar": { 305 Type: cty.String, 306 Optional: true, 307 }, 308 "baz": { 309 Type: cty.String, 310 Optional: true, 311 Computed: true, 312 }, 313 }, 314 }, 315 }, 316 }, 317 }, 318 cty.ObjectVal(map[string]cty.Value{ 319 "foo": cty.SetVal([]cty.Value{ 320 cty.ObjectVal(map[string]cty.Value{ 321 "bar": cty.StringVal("blep"), 322 "baz": cty.NullVal(cty.String), 323 }), 324 cty.ObjectVal(map[string]cty.Value{ 325 "bar": cty.StringVal("boop"), 326 "baz": cty.NullVal(cty.String), 327 }), 328 }), 329 }), 330 cty.ObjectVal(map[string]cty.Value{ 331 "foo": cty.SetVal([]cty.Value{ 332 cty.ObjectVal(map[string]cty.Value{ 333 "bar": cty.StringVal("blep"), 334 "baz": cty.UnknownVal(cty.String), 335 }), 336 cty.ObjectVal(map[string]cty.Value{ 337 "bar": cty.StringVal("boop"), 338 "baz": cty.UnknownVal(cty.String), 339 }), 340 }), 341 }), 342 }, 343 "sets differing only by unknown": { 344 &configschema.Block{ 345 BlockTypes: map[string]*configschema.NestedBlock{ 346 "foo": { 347 Nesting: configschema.NestingSet, 348 Block: configschema.Block{ 349 Attributes: map[string]*configschema.Attribute{ 350 "bar": { 351 Type: cty.String, 352 Optional: true, 353 }, 354 "baz": { 355 Type: cty.String, 356 Optional: true, 357 Computed: true, 358 }, 359 }, 360 }, 361 }, 362 }, 363 }, 364 cty.ObjectVal(map[string]cty.Value{ 365 "foo": cty.SetVal([]cty.Value{ 366 cty.ObjectVal(map[string]cty.Value{ 367 "bar": cty.StringVal("boop"), 368 "baz": cty.NullVal(cty.String), 369 }), 370 cty.ObjectVal(map[string]cty.Value{ 371 "bar": cty.StringVal("boop"), 372 "baz": cty.UnknownVal(cty.String), 373 }), 374 }), 375 }), 376 cty.ObjectVal(map[string]cty.Value{ 377 "foo": cty.SetVal([]cty.Value{ 378 cty.ObjectVal(map[string]cty.Value{ 379 "bar": cty.StringVal("boop"), 380 "baz": cty.UnknownVal(cty.String), 381 }), 382 cty.ObjectVal(map[string]cty.Value{ 383 "bar": cty.StringVal("boop"), 384 "baz": cty.UnknownVal(cty.String), 385 }), 386 }), 387 }), 388 }, 389 "prior nested list with dynamic": { 390 &configschema.Block{ 391 BlockTypes: map[string]*configschema.NestedBlock{ 392 "foo": { 393 Nesting: configschema.NestingList, 394 Block: configschema.Block{ 395 Attributes: map[string]*configschema.Attribute{ 396 "bar": { 397 Type: cty.String, 398 Optional: true, 399 Computed: true, 400 }, 401 "baz": { 402 Type: cty.DynamicPseudoType, 403 Optional: true, 404 Computed: true, 405 }, 406 }, 407 }, 408 }, 409 }, 410 }, 411 cty.ObjectVal(map[string]cty.Value{ 412 "foo": cty.TupleVal([]cty.Value{ 413 cty.ObjectVal(map[string]cty.Value{ 414 "bar": cty.NullVal(cty.String), 415 "baz": cty.NumberIntVal(8), 416 }), 417 }), 418 }), 419 cty.ObjectVal(map[string]cty.Value{ 420 "foo": cty.TupleVal([]cty.Value{ 421 cty.ObjectVal(map[string]cty.Value{ 422 "bar": cty.UnknownVal(cty.String), 423 "baz": cty.NumberIntVal(8), 424 }), 425 }), 426 }), 427 }, 428 "prior nested map with dynamic": { 429 &configschema.Block{ 430 BlockTypes: map[string]*configschema.NestedBlock{ 431 "foo": { 432 Nesting: configschema.NestingMap, 433 Block: configschema.Block{ 434 Attributes: map[string]*configschema.Attribute{ 435 "bar": { 436 Type: cty.String, 437 Optional: true, 438 Computed: true, 439 }, 440 "baz": { 441 Type: cty.DynamicPseudoType, 442 Optional: true, 443 Computed: true, 444 }, 445 }, 446 }, 447 }, 448 }, 449 }, 450 cty.ObjectVal(map[string]cty.Value{ 451 "foo": cty.ObjectVal(map[string]cty.Value{ 452 "a": cty.ObjectVal(map[string]cty.Value{ 453 "bar": cty.StringVal("beep"), 454 "baz": cty.NullVal(cty.DynamicPseudoType), 455 }), 456 "b": cty.ObjectVal(map[string]cty.Value{ 457 "bar": cty.StringVal("boop"), 458 "baz": cty.NumberIntVal(8), 459 }), 460 }), 461 }), 462 cty.ObjectVal(map[string]cty.Value{ 463 "foo": cty.ObjectVal(map[string]cty.Value{ 464 "a": cty.ObjectVal(map[string]cty.Value{ 465 "bar": cty.StringVal("beep"), 466 "baz": cty.UnknownVal(cty.DynamicPseudoType), 467 }), 468 "b": cty.ObjectVal(map[string]cty.Value{ 469 "bar": cty.StringVal("boop"), 470 "baz": cty.NumberIntVal(8), 471 }), 472 }), 473 }), 474 }, 475 } { 476 t.Run(n, func(t *testing.T) { 477 got := SetUnknowns(tc.Val, tc.Schema) 478 if !got.RawEquals(tc.Expected) { 479 t.Fatalf("\nexpected: %#v\ngot: %#v\n", tc.Expected, got) 480 } 481 }) 482 } 483 }