github.com/opentofu/opentofu@v1.7.1/internal/tofu/reduce_plan_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 16 func TestProcessIgnoreChangesIndividual(t *testing.T) { 17 tests := map[string]struct { 18 Old, New cty.Value 19 Ignore []string 20 Want cty.Value 21 }{ 22 "string": { 23 cty.ObjectVal(map[string]cty.Value{ 24 "a": cty.StringVal("a value"), 25 "b": cty.StringVal("b value"), 26 }), 27 cty.ObjectVal(map[string]cty.Value{ 28 "a": cty.StringVal("new a value"), 29 "b": cty.StringVal("new b value"), 30 }), 31 []string{"a"}, 32 cty.ObjectVal(map[string]cty.Value{ 33 "a": cty.StringVal("a value"), 34 "b": cty.StringVal("new b value"), 35 }), 36 }, 37 "changed type": { 38 cty.ObjectVal(map[string]cty.Value{ 39 "a": cty.StringVal("a value"), 40 "b": cty.StringVal("b value"), 41 }), 42 cty.ObjectVal(map[string]cty.Value{ 43 "a": cty.NumberIntVal(1), 44 "b": cty.StringVal("new b value"), 45 }), 46 []string{"a"}, 47 cty.ObjectVal(map[string]cty.Value{ 48 "a": cty.StringVal("a value"), 49 "b": cty.StringVal("new b value"), 50 }), 51 }, 52 "list": { 53 cty.ObjectVal(map[string]cty.Value{ 54 "a": cty.ListVal([]cty.Value{ 55 cty.StringVal("a0 value"), 56 cty.StringVal("a1 value"), 57 }), 58 "b": cty.StringVal("b value"), 59 }), 60 cty.ObjectVal(map[string]cty.Value{ 61 "a": cty.ListVal([]cty.Value{ 62 cty.StringVal("new a0 value"), 63 cty.StringVal("new a1 value"), 64 }), 65 "b": cty.StringVal("new b value"), 66 }), 67 []string{"a"}, 68 cty.ObjectVal(map[string]cty.Value{ 69 "a": cty.ListVal([]cty.Value{ 70 cty.StringVal("a0 value"), 71 cty.StringVal("a1 value"), 72 }), 73 "b": cty.StringVal("new b value"), 74 }), 75 }, 76 "list_index": { 77 cty.ObjectVal(map[string]cty.Value{ 78 "a": cty.ListVal([]cty.Value{ 79 cty.StringVal("a0 value"), 80 cty.StringVal("a1 value"), 81 }), 82 "b": cty.StringVal("b value"), 83 }), 84 cty.ObjectVal(map[string]cty.Value{ 85 "a": cty.ListVal([]cty.Value{ 86 cty.StringVal("new a0 value"), 87 cty.StringVal("new a1 value"), 88 }), 89 "b": cty.StringVal("new b value"), 90 }), 91 []string{"a[1]"}, 92 cty.ObjectVal(map[string]cty.Value{ 93 "a": cty.ListVal([]cty.Value{ 94 cty.StringVal("new a0 value"), 95 cty.StringVal("a1 value"), 96 }), 97 "b": cty.StringVal("new b value"), 98 }), 99 }, 100 "map": { 101 cty.ObjectVal(map[string]cty.Value{ 102 "a": cty.MapVal(map[string]cty.Value{ 103 "a0": cty.StringVal("a0 value"), 104 "a1": cty.StringVal("a1 value"), 105 }), 106 "b": cty.StringVal("b value"), 107 }), 108 cty.ObjectVal(map[string]cty.Value{ 109 "a": cty.MapVal(map[string]cty.Value{ 110 "a0": cty.StringVal("new a0 value"), 111 "a1": cty.UnknownVal(cty.String), 112 }), 113 "b": cty.StringVal("b value"), 114 }), 115 []string{`a`}, 116 cty.ObjectVal(map[string]cty.Value{ 117 "a": cty.MapVal(map[string]cty.Value{ 118 "a0": cty.StringVal("a0 value"), 119 "a1": cty.StringVal("a1 value"), 120 }), 121 "b": cty.StringVal("b value"), 122 }), 123 }, 124 "map_index": { 125 cty.ObjectVal(map[string]cty.Value{ 126 "a": cty.MapVal(map[string]cty.Value{ 127 "a0": cty.StringVal("a0 value"), 128 "a1": cty.StringVal("a1 value"), 129 }), 130 "b": cty.StringVal("b value"), 131 }), 132 cty.ObjectVal(map[string]cty.Value{ 133 "a": cty.MapVal(map[string]cty.Value{ 134 "a0": cty.StringVal("new a0 value"), 135 "a1": cty.StringVal("new a1 value"), 136 }), 137 "b": cty.StringVal("b value"), 138 }), 139 []string{`a["a1"]`}, 140 cty.ObjectVal(map[string]cty.Value{ 141 "a": cty.MapVal(map[string]cty.Value{ 142 "a0": cty.StringVal("new a0 value"), 143 "a1": cty.StringVal("a1 value"), 144 }), 145 "b": cty.StringVal("b value"), 146 }), 147 }, 148 "map_index_no_config": { 149 cty.ObjectVal(map[string]cty.Value{ 150 "a": cty.MapVal(map[string]cty.Value{ 151 "a0": cty.StringVal("a0 value"), 152 "a1": cty.StringVal("a1 value"), 153 }), 154 "b": cty.StringVal("b value"), 155 }), 156 cty.ObjectVal(map[string]cty.Value{ 157 "a": cty.NullVal(cty.Map(cty.String)), 158 "b": cty.StringVal("b value"), 159 }), 160 []string{`a["a1"]`}, 161 cty.ObjectVal(map[string]cty.Value{ 162 "a": cty.MapVal(map[string]cty.Value{ 163 "a1": cty.StringVal("a1 value"), 164 }), 165 "b": cty.StringVal("b value"), 166 }), 167 }, 168 "map_index_unknown_value": { 169 cty.ObjectVal(map[string]cty.Value{ 170 "a": cty.MapVal(map[string]cty.Value{ 171 "a0": cty.StringVal("a0 value"), 172 "a1": cty.StringVal("a1 value"), 173 }), 174 "b": cty.StringVal("b value"), 175 }), 176 cty.ObjectVal(map[string]cty.Value{ 177 "a": cty.MapVal(map[string]cty.Value{ 178 "a0": cty.StringVal("a0 value"), 179 "a1": cty.UnknownVal(cty.String), 180 }), 181 "b": cty.StringVal("b value"), 182 }), 183 []string{`a["a1"]`}, 184 cty.ObjectVal(map[string]cty.Value{ 185 "a": cty.MapVal(map[string]cty.Value{ 186 "a0": cty.StringVal("a0 value"), 187 "a1": cty.StringVal("a1 value"), 188 }), 189 "b": cty.StringVal("b value"), 190 }), 191 }, 192 "map_index_multiple_keys": { 193 cty.ObjectVal(map[string]cty.Value{ 194 "a": cty.MapVal(map[string]cty.Value{ 195 "a0": cty.StringVal("a0 value"), 196 "a1": cty.StringVal("a1 value"), 197 "a2": cty.StringVal("a2 value"), 198 "a3": cty.StringVal("a3 value"), 199 }), 200 "b": cty.StringVal("b value"), 201 }), 202 cty.ObjectVal(map[string]cty.Value{ 203 "a": cty.NullVal(cty.Map(cty.String)), 204 "b": cty.StringVal("new b value"), 205 }), 206 []string{`a["a1"]`, `a["a2"]`, `a["a3"]`, `b`}, 207 cty.ObjectVal(map[string]cty.Value{ 208 "a": cty.MapVal(map[string]cty.Value{ 209 "a1": cty.StringVal("a1 value"), 210 "a2": cty.StringVal("a2 value"), 211 "a3": cty.StringVal("a3 value"), 212 }), 213 "b": cty.StringVal("b value"), 214 }), 215 }, 216 "map_index_redundant": { 217 cty.ObjectVal(map[string]cty.Value{ 218 "a": cty.MapVal(map[string]cty.Value{ 219 "a0": cty.StringVal("a0 value"), 220 "a1": cty.StringVal("a1 value"), 221 "a2": cty.StringVal("a2 value"), 222 }), 223 "b": cty.StringVal("b value"), 224 }), 225 cty.ObjectVal(map[string]cty.Value{ 226 "a": cty.NullVal(cty.Map(cty.String)), 227 "b": cty.StringVal("new b value"), 228 }), 229 []string{`a["a1"]`, `a`, `b`}, 230 cty.ObjectVal(map[string]cty.Value{ 231 "a": cty.MapVal(map[string]cty.Value{ 232 "a0": cty.StringVal("a0 value"), 233 "a1": cty.StringVal("a1 value"), 234 "a2": cty.StringVal("a2 value"), 235 }), 236 "b": cty.StringVal("b value"), 237 }), 238 }, 239 "missing_map_index": { 240 cty.ObjectVal(map[string]cty.Value{ 241 "a": cty.MapVal(map[string]cty.Value{ 242 "a0": cty.StringVal("a0 value"), 243 "a1": cty.StringVal("a1 value"), 244 }), 245 "b": cty.StringVal("b value"), 246 }), 247 cty.ObjectVal(map[string]cty.Value{ 248 "a": cty.MapValEmpty(cty.String), 249 "b": cty.StringVal("b value"), 250 }), 251 []string{`a["a1"]`}, 252 cty.ObjectVal(map[string]cty.Value{ 253 "a": cty.MapVal(map[string]cty.Value{ 254 "a1": cty.StringVal("a1 value"), 255 }), 256 "b": cty.StringVal("b value"), 257 }), 258 }, 259 "missing_map_index_empty": { 260 cty.ObjectVal(map[string]cty.Value{ 261 "a": cty.MapValEmpty(cty.String), 262 }), 263 cty.ObjectVal(map[string]cty.Value{ 264 "a": cty.MapVal(map[string]cty.Value{ 265 "a": cty.StringVal("a0 value"), 266 }), 267 }), 268 []string{`a["a"]`}, 269 cty.ObjectVal(map[string]cty.Value{ 270 "a": cty.MapValEmpty(cty.String), 271 }), 272 }, 273 "missing_map_index_to_object": { 274 cty.ObjectVal(map[string]cty.Value{ 275 "a": cty.MapVal(map[string]cty.Value{ 276 "a": cty.ObjectVal(map[string]cty.Value{ 277 "a": cty.StringVal("aa0"), 278 "b": cty.StringVal("ab0"), 279 }), 280 "b": cty.ObjectVal(map[string]cty.Value{ 281 "a": cty.StringVal("ba0"), 282 "b": cty.StringVal("bb0"), 283 }), 284 }), 285 }), 286 cty.ObjectVal(map[string]cty.Value{ 287 "a": cty.MapValEmpty( 288 cty.Object(map[string]cty.Type{ 289 "a": cty.String, 290 "b": cty.String, 291 }), 292 ), 293 }), 294 // we expect the config to be used here, as the ignore changes was 295 // `a["a"].b`, but the change was larger than that removing 296 // `a["a"]` entirely. 297 []string{`a["a"].b`}, 298 cty.ObjectVal(map[string]cty.Value{ 299 "a": cty.MapValEmpty( 300 cty.Object(map[string]cty.Type{ 301 "a": cty.String, 302 "b": cty.String, 303 }), 304 ), 305 }), 306 }, 307 "missing_prior_map_index": { 308 cty.ObjectVal(map[string]cty.Value{ 309 "a": cty.MapVal(map[string]cty.Value{ 310 "a0": cty.StringVal("a0 value"), 311 }), 312 "b": cty.StringVal("b value"), 313 }), 314 cty.ObjectVal(map[string]cty.Value{ 315 "a": cty.MapVal(map[string]cty.Value{ 316 "a0": cty.StringVal("a0 value"), 317 "a1": cty.StringVal("new a1 value"), 318 }), 319 "b": cty.StringVal("b value"), 320 }), 321 []string{`a["a1"]`}, 322 cty.ObjectVal(map[string]cty.Value{ 323 "a": cty.MapVal(map[string]cty.Value{ 324 "a0": cty.StringVal("a0 value"), 325 }), 326 "b": cty.StringVal("b value"), 327 }), 328 }, 329 "object attribute": { 330 cty.ObjectVal(map[string]cty.Value{ 331 "a": cty.ObjectVal(map[string]cty.Value{ 332 "foo": cty.StringVal("a.foo value"), 333 "bar": cty.StringVal("a.bar value"), 334 }), 335 "b": cty.StringVal("b value"), 336 }), 337 cty.ObjectVal(map[string]cty.Value{ 338 "a": cty.ObjectVal(map[string]cty.Value{ 339 "foo": cty.StringVal("new a.foo value"), 340 "bar": cty.StringVal("new a.bar value"), 341 }), 342 "b": cty.StringVal("new b value"), 343 }), 344 []string{"a.bar"}, 345 cty.ObjectVal(map[string]cty.Value{ 346 "a": cty.ObjectVal(map[string]cty.Value{ 347 "foo": cty.StringVal("new a.foo value"), 348 "bar": cty.StringVal("a.bar value"), 349 }), 350 "b": cty.StringVal("new b value"), 351 }), 352 }, 353 "unknown_object_attribute": { 354 cty.ObjectVal(map[string]cty.Value{ 355 "a": cty.ObjectVal(map[string]cty.Value{ 356 "foo": cty.StringVal("a.foo value"), 357 "bar": cty.StringVal("a.bar value"), 358 }), 359 "b": cty.StringVal("b value"), 360 }), 361 cty.ObjectVal(map[string]cty.Value{ 362 "a": cty.ObjectVal(map[string]cty.Value{ 363 "foo": cty.StringVal("new a.foo value"), 364 "bar": cty.UnknownVal(cty.String), 365 }), 366 "b": cty.StringVal("new b value"), 367 }), 368 []string{"a.bar"}, 369 cty.ObjectVal(map[string]cty.Value{ 370 "a": cty.ObjectVal(map[string]cty.Value{ 371 "foo": cty.StringVal("new a.foo value"), 372 "bar": cty.StringVal("a.bar value"), 373 }), 374 "b": cty.StringVal("new b value"), 375 }), 376 }, 377 "null_map": { 378 cty.ObjectVal(map[string]cty.Value{ 379 "a": cty.StringVal("ok"), 380 "list": cty.ListVal([]cty.Value{ 381 cty.ObjectVal(map[string]cty.Value{ 382 "s": cty.StringVal("ok"), 383 "map": cty.NullVal(cty.Map(cty.String)), 384 }), 385 }), 386 }), 387 cty.ObjectVal(map[string]cty.Value{ 388 "a": cty.NullVal(cty.String), 389 "list": cty.ListVal([]cty.Value{ 390 cty.ObjectVal(map[string]cty.Value{ 391 "s": cty.StringVal("ok"), 392 "map": cty.NullVal(cty.Map(cty.String)), 393 }), 394 }), 395 }), 396 []string{"a"}, 397 cty.ObjectVal(map[string]cty.Value{ 398 "a": cty.StringVal("ok"), 399 "list": cty.ListVal([]cty.Value{ 400 cty.ObjectVal(map[string]cty.Value{ 401 "s": cty.StringVal("ok"), 402 "map": cty.NullVal(cty.Map(cty.String)), 403 }), 404 }), 405 }), 406 }, 407 "marked_map": { 408 cty.ObjectVal(map[string]cty.Value{ 409 "map": cty.MapVal(map[string]cty.Value{ 410 "key": cty.StringVal("val"), 411 }).Mark("marked"), 412 }), 413 cty.ObjectVal(map[string]cty.Value{ 414 "map": cty.MapVal(map[string]cty.Value{ 415 "key": cty.StringVal("new val"), 416 }).Mark("marked"), 417 }), 418 []string{`map["key"]`}, 419 cty.ObjectVal(map[string]cty.Value{ 420 "map": cty.MapVal(map[string]cty.Value{ 421 "key": cty.StringVal("val"), 422 }).Mark("marked"), 423 }), 424 }, 425 } 426 427 for name, test := range tests { 428 t.Run(name, func(t *testing.T) { 429 ignore := make([]hcl.Traversal, len(test.Ignore)) 430 for i, ignoreStr := range test.Ignore { 431 trav, diags := hclsyntax.ParseTraversalAbs([]byte(ignoreStr), "", hcl.Pos{Line: 1, Column: 1}) 432 if diags.HasErrors() { 433 t.Fatalf("failed to parse %q: %s", ignoreStr, diags.Error()) 434 } 435 ignore[i] = trav 436 } 437 438 ret, diags := processIgnoreChangesIndividual(test.Old, test.New, traversalsToPaths(ignore)) 439 if diags.HasErrors() { 440 t.Fatal(diags.Err()) 441 } 442 443 if got, want := ret, test.Want; !want.RawEquals(got) { 444 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, want) 445 } 446 }) 447 } 448 }