github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/jsonplan/plan_test.go (about) 1 package jsonplan 2 3 import ( 4 "encoding/json" 5 "reflect" 6 "testing" 7 8 "github.com/google/go-cmp/cmp" 9 "github.com/zclconf/go-cty/cty" 10 11 "github.com/hashicorp/terraform/internal/addrs" 12 "github.com/hashicorp/terraform/internal/plans" 13 ) 14 15 func TestOmitUnknowns(t *testing.T) { 16 tests := []struct { 17 Input cty.Value 18 Want cty.Value 19 }{ 20 { 21 cty.StringVal("hello"), 22 cty.StringVal("hello"), 23 }, 24 { 25 cty.NullVal(cty.String), 26 cty.NullVal(cty.String), 27 }, 28 { 29 cty.UnknownVal(cty.String), 30 cty.NilVal, 31 }, 32 { 33 cty.ListValEmpty(cty.String), 34 cty.EmptyTupleVal, 35 }, 36 { 37 cty.ListVal([]cty.Value{cty.StringVal("hello")}), 38 cty.TupleVal([]cty.Value{cty.StringVal("hello")}), 39 }, 40 { 41 cty.ListVal([]cty.Value{cty.NullVal(cty.String)}), 42 cty.TupleVal([]cty.Value{cty.NullVal(cty.String)}), 43 }, 44 { 45 cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}), 46 cty.TupleVal([]cty.Value{cty.NullVal(cty.String)}), 47 }, 48 { 49 cty.ListVal([]cty.Value{cty.StringVal("hello")}), 50 cty.TupleVal([]cty.Value{cty.StringVal("hello")}), 51 }, 52 // 53 { 54 cty.ListVal([]cty.Value{ 55 cty.StringVal("hello"), 56 cty.UnknownVal(cty.String)}), 57 cty.TupleVal([]cty.Value{ 58 cty.StringVal("hello"), 59 cty.NullVal(cty.String), 60 }), 61 }, 62 { 63 cty.MapVal(map[string]cty.Value{ 64 "hello": cty.True, 65 "world": cty.UnknownVal(cty.Bool), 66 }), 67 cty.ObjectVal(map[string]cty.Value{ 68 "hello": cty.True, 69 }), 70 }, 71 { 72 cty.TupleVal([]cty.Value{ 73 cty.StringVal("alpha"), 74 cty.UnknownVal(cty.String), 75 cty.StringVal("charlie"), 76 }), 77 cty.TupleVal([]cty.Value{ 78 cty.StringVal("alpha"), 79 cty.NullVal(cty.String), 80 cty.StringVal("charlie"), 81 }), 82 }, 83 { 84 cty.SetVal([]cty.Value{ 85 cty.StringVal("dev"), 86 cty.StringVal("foo"), 87 cty.StringVal("stg"), 88 cty.UnknownVal(cty.String), 89 }), 90 cty.TupleVal([]cty.Value{ 91 cty.StringVal("dev"), 92 cty.StringVal("foo"), 93 cty.StringVal("stg"), 94 cty.NullVal(cty.String), 95 }), 96 }, 97 { 98 cty.SetVal([]cty.Value{ 99 cty.ObjectVal(map[string]cty.Value{ 100 "a": cty.UnknownVal(cty.String), 101 }), 102 cty.ObjectVal(map[string]cty.Value{ 103 "a": cty.StringVal("known"), 104 }), 105 }), 106 cty.TupleVal([]cty.Value{ 107 cty.ObjectVal(map[string]cty.Value{ 108 "a": cty.StringVal("known"), 109 }), 110 cty.EmptyObjectVal, 111 }), 112 }, 113 } 114 115 for _, test := range tests { 116 got := omitUnknowns(test.Input) 117 if !reflect.DeepEqual(got, test.Want) { 118 t.Errorf( 119 "wrong result\ninput: %#v\ngot: %#v\nwant: %#v", 120 test.Input, got, test.Want, 121 ) 122 } 123 } 124 } 125 126 func TestUnknownAsBool(t *testing.T) { 127 tests := []struct { 128 Input cty.Value 129 Want cty.Value 130 }{ 131 { 132 cty.StringVal("hello"), 133 cty.False, 134 }, 135 { 136 cty.NullVal(cty.String), 137 cty.False, 138 }, 139 { 140 cty.UnknownVal(cty.String), 141 cty.True, 142 }, 143 144 { 145 cty.NullVal(cty.DynamicPseudoType), 146 cty.False, 147 }, 148 { 149 cty.NullVal(cty.Object(map[string]cty.Type{"test": cty.String})), 150 cty.False, 151 }, 152 { 153 cty.DynamicVal, 154 cty.True, 155 }, 156 157 { 158 cty.ListValEmpty(cty.String), 159 cty.EmptyTupleVal, 160 }, 161 { 162 cty.ListVal([]cty.Value{cty.StringVal("hello")}), 163 cty.TupleVal([]cty.Value{cty.False}), 164 }, 165 { 166 cty.ListVal([]cty.Value{cty.NullVal(cty.String)}), 167 cty.TupleVal([]cty.Value{cty.False}), 168 }, 169 { 170 cty.ListVal([]cty.Value{cty.UnknownVal(cty.String)}), 171 cty.TupleVal([]cty.Value{cty.True}), 172 }, 173 { 174 cty.SetValEmpty(cty.String), 175 cty.EmptyTupleVal, 176 }, 177 { 178 cty.SetVal([]cty.Value{cty.StringVal("hello")}), 179 cty.TupleVal([]cty.Value{cty.False}), 180 }, 181 { 182 cty.SetVal([]cty.Value{cty.NullVal(cty.String)}), 183 cty.TupleVal([]cty.Value{cty.False}), 184 }, 185 { 186 cty.SetVal([]cty.Value{cty.UnknownVal(cty.String)}), 187 cty.TupleVal([]cty.Value{cty.True}), 188 }, 189 { 190 cty.EmptyTupleVal, 191 cty.EmptyTupleVal, 192 }, 193 { 194 cty.TupleVal([]cty.Value{cty.StringVal("hello")}), 195 cty.TupleVal([]cty.Value{cty.False}), 196 }, 197 { 198 cty.TupleVal([]cty.Value{cty.NullVal(cty.String)}), 199 cty.TupleVal([]cty.Value{cty.False}), 200 }, 201 { 202 cty.TupleVal([]cty.Value{cty.UnknownVal(cty.String)}), 203 cty.TupleVal([]cty.Value{cty.True}), 204 }, 205 { 206 cty.MapValEmpty(cty.String), 207 cty.EmptyObjectVal, 208 }, 209 { 210 cty.MapVal(map[string]cty.Value{"greeting": cty.StringVal("hello")}), 211 cty.EmptyObjectVal, 212 }, 213 { 214 cty.MapVal(map[string]cty.Value{"greeting": cty.NullVal(cty.String)}), 215 cty.EmptyObjectVal, 216 }, 217 { 218 cty.MapVal(map[string]cty.Value{"greeting": cty.UnknownVal(cty.String)}), 219 cty.ObjectVal(map[string]cty.Value{"greeting": cty.True}), 220 }, 221 { 222 cty.EmptyObjectVal, 223 cty.EmptyObjectVal, 224 }, 225 { 226 cty.ObjectVal(map[string]cty.Value{"greeting": cty.StringVal("hello")}), 227 cty.EmptyObjectVal, 228 }, 229 { 230 cty.ObjectVal(map[string]cty.Value{"greeting": cty.NullVal(cty.String)}), 231 cty.EmptyObjectVal, 232 }, 233 { 234 cty.ObjectVal(map[string]cty.Value{"greeting": cty.UnknownVal(cty.String)}), 235 cty.ObjectVal(map[string]cty.Value{"greeting": cty.True}), 236 }, 237 { 238 cty.SetVal([]cty.Value{ 239 cty.ObjectVal(map[string]cty.Value{ 240 "a": cty.UnknownVal(cty.String), 241 }), 242 cty.ObjectVal(map[string]cty.Value{ 243 "a": cty.StringVal("known"), 244 }), 245 }), 246 cty.TupleVal([]cty.Value{ 247 cty.EmptyObjectVal, 248 cty.ObjectVal(map[string]cty.Value{ 249 "a": cty.True, 250 }), 251 }), 252 }, 253 { 254 cty.SetVal([]cty.Value{ 255 cty.MapValEmpty(cty.String), 256 cty.MapVal(map[string]cty.Value{ 257 "a": cty.StringVal("known"), 258 }), 259 cty.MapVal(map[string]cty.Value{ 260 "a": cty.UnknownVal(cty.String), 261 }), 262 }), 263 cty.TupleVal([]cty.Value{ 264 cty.EmptyObjectVal, 265 cty.ObjectVal(map[string]cty.Value{ 266 "a": cty.True, 267 }), 268 cty.EmptyObjectVal, 269 }), 270 }, 271 } 272 273 for _, test := range tests { 274 got := unknownAsBool(test.Input) 275 if !reflect.DeepEqual(got, test.Want) { 276 t.Errorf( 277 "wrong result\ninput: %#v\ngot: %#v\nwant: %#v", 278 test.Input, got, test.Want, 279 ) 280 } 281 } 282 } 283 284 func TestEncodePaths(t *testing.T) { 285 tests := map[string]struct { 286 Input cty.PathSet 287 Want json.RawMessage 288 }{ 289 "empty set": { 290 cty.NewPathSet(), 291 json.RawMessage(nil), 292 }, 293 "index path with string and int steps": { 294 cty.NewPathSet(cty.IndexStringPath("boop").IndexInt(0)), 295 json.RawMessage(`[["boop",0]]`), 296 }, 297 "get attr path with one step": { 298 cty.NewPathSet(cty.GetAttrPath("triggers")), 299 json.RawMessage(`[["triggers"]]`), 300 }, 301 "multiple paths of different types": { 302 cty.NewPathSet( 303 cty.GetAttrPath("alpha").GetAttr("beta").GetAttr("gamma"), 304 cty.GetAttrPath("triggers").IndexString("name"), 305 cty.IndexIntPath(0).IndexInt(1).IndexInt(2).IndexInt(3), 306 ), 307 json.RawMessage(`[["alpha","beta","gamma"],["triggers","name"],[0,1,2,3]]`), 308 }, 309 } 310 311 for name, test := range tests { 312 t.Run(name, func(t *testing.T) { 313 got, err := encodePaths(test.Input) 314 if err != nil { 315 t.Fatalf("unexpected error: %s", err) 316 } 317 if !cmp.Equal(got, test.Want) { 318 t.Errorf("wrong result:\n %v\n", cmp.Diff(got, test.Want)) 319 } 320 }) 321 } 322 } 323 324 func TestOutputs(t *testing.T) { 325 root := addrs.RootModuleInstance 326 327 child, diags := addrs.ParseModuleInstanceStr("module.child") 328 if diags.HasErrors() { 329 t.Fatalf("unexpected errors: %s", diags.Err()) 330 } 331 332 tests := map[string]struct { 333 changes *plans.Changes 334 expected map[string]Change 335 }{ 336 "copies all outputs": { 337 changes: &plans.Changes{ 338 Outputs: []*plans.OutputChangeSrc{ 339 { 340 Addr: root.OutputValue("first"), 341 ChangeSrc: plans.ChangeSrc{ 342 Action: plans.Create, 343 }, 344 }, 345 { 346 Addr: root.OutputValue("second"), 347 ChangeSrc: plans.ChangeSrc{ 348 Action: plans.Create, 349 }, 350 }, 351 }, 352 }, 353 expected: map[string]Change{ 354 "first": { 355 Actions: []string{"create"}, 356 Before: json.RawMessage("null"), 357 After: json.RawMessage("null"), 358 AfterUnknown: json.RawMessage("false"), 359 BeforeSensitive: json.RawMessage("false"), 360 AfterSensitive: json.RawMessage("false"), 361 }, 362 "second": { 363 Actions: []string{"create"}, 364 Before: json.RawMessage("null"), 365 After: json.RawMessage("null"), 366 AfterUnknown: json.RawMessage("false"), 367 BeforeSensitive: json.RawMessage("false"), 368 AfterSensitive: json.RawMessage("false"), 369 }, 370 }, 371 }, 372 "skips non root modules": { 373 changes: &plans.Changes{ 374 Outputs: []*plans.OutputChangeSrc{ 375 { 376 Addr: root.OutputValue("first"), 377 ChangeSrc: plans.ChangeSrc{ 378 Action: plans.Create, 379 }, 380 }, 381 { 382 Addr: child.OutputValue("second"), 383 ChangeSrc: plans.ChangeSrc{ 384 Action: plans.Create, 385 }, 386 }, 387 }, 388 }, 389 expected: map[string]Change{ 390 "first": { 391 Actions: []string{"create"}, 392 Before: json.RawMessage("null"), 393 After: json.RawMessage("null"), 394 AfterUnknown: json.RawMessage("false"), 395 BeforeSensitive: json.RawMessage("false"), 396 AfterSensitive: json.RawMessage("false"), 397 }, 398 }, 399 }, 400 } 401 for name, test := range tests { 402 t.Run(name, func(t *testing.T) { 403 changes, err := MarshalOutputChanges(test.changes) 404 if err != nil { 405 t.Fatalf("unexpected err: %s", err) 406 } 407 408 if !cmp.Equal(changes, test.expected) { 409 t.Errorf("wrong result:\n %v\n", cmp.Diff(changes, test.expected)) 410 } 411 }) 412 } 413 } 414 415 func deepObjectValue(depth int) cty.Value { 416 v := cty.ObjectVal(map[string]cty.Value{ 417 "a": cty.StringVal("a"), 418 "b": cty.NumberIntVal(2), 419 "c": cty.True, 420 "d": cty.UnknownVal(cty.String), 421 }) 422 423 result := v 424 425 for i := 0; i < depth; i++ { 426 result = cty.ObjectVal(map[string]cty.Value{ 427 "a": result, 428 "b": result, 429 "c": result, 430 }) 431 } 432 433 return result 434 } 435 436 func BenchmarkUnknownAsBool_2(b *testing.B) { 437 value := deepObjectValue(2) 438 for n := 0; n < b.N; n++ { 439 unknownAsBool(value) 440 } 441 } 442 443 func BenchmarkUnknownAsBool_3(b *testing.B) { 444 value := deepObjectValue(3) 445 for n := 0; n < b.N; n++ { 446 unknownAsBool(value) 447 } 448 } 449 450 func BenchmarkUnknownAsBool_5(b *testing.B) { 451 value := deepObjectValue(5) 452 for n := 0; n < b.N; n++ { 453 unknownAsBool(value) 454 } 455 } 456 457 func BenchmarkUnknownAsBool_7(b *testing.B) { 458 value := deepObjectValue(7) 459 for n := 0; n < b.N; n++ { 460 unknownAsBool(value) 461 } 462 } 463 464 func BenchmarkUnknownAsBool_9(b *testing.B) { 465 value := deepObjectValue(9) 466 for n := 0; n < b.N; n++ { 467 unknownAsBool(value) 468 } 469 }