github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/moduletest/run_test.go (about) 1 package moduletest 2 3 import ( 4 "testing" 5 6 "github.com/google/go-cmp/cmp" 7 "github.com/hashicorp/hcl/v2" 8 "github.com/hashicorp/hcl/v2/hclsyntax" 9 10 "github.com/terramate-io/tf/addrs" 11 "github.com/terramate-io/tf/configs" 12 "github.com/terramate-io/tf/tfdiags" 13 ) 14 15 func TestRun_ValidateExpectedFailures(t *testing.T) { 16 17 type output struct { 18 Description tfdiags.Description 19 Severity tfdiags.Severity 20 } 21 22 tcs := map[string]struct { 23 ExpectedFailures []string 24 Input tfdiags.Diagnostics 25 Output []output 26 }{ 27 "empty": { 28 ExpectedFailures: nil, 29 Input: nil, 30 Output: nil, 31 }, 32 "carries through simple diags": { 33 Input: createDiagnostics(func(diags tfdiags.Diagnostics) tfdiags.Diagnostics { 34 35 diags = diags.Append(&hcl.Diagnostic{ 36 Severity: hcl.DiagError, 37 Summary: "simple error", 38 Detail: "want to see this in the returned set", 39 }) 40 41 diags = diags.Append(&hcl.Diagnostic{ 42 Severity: hcl.DiagWarning, 43 Summary: "simple warning", 44 Detail: "want to see this in the returned set", 45 }) 46 47 return diags 48 }), 49 Output: []output{ 50 { 51 Description: tfdiags.Description{ 52 Summary: "simple error", 53 Detail: "want to see this in the returned set", 54 }, 55 Severity: tfdiags.Error, 56 }, 57 { 58 Description: tfdiags.Description{ 59 Summary: "simple warning", 60 Detail: "want to see this in the returned set", 61 }, 62 Severity: tfdiags.Warning, 63 }, 64 }, 65 }, 66 "expected failures did not fail": { 67 ExpectedFailures: []string{ 68 "check.example", 69 }, 70 Input: nil, 71 Output: []output{ 72 { 73 Description: tfdiags.Description{ 74 Summary: "Missing expected failure", 75 Detail: "The checkable object, check.example, was expected to report an error but did not.", 76 }, 77 Severity: tfdiags.Error, 78 }, 79 }, 80 }, 81 "outputs": { 82 ExpectedFailures: []string{ 83 "output.expected_one", 84 "output.expected_two", 85 }, 86 Input: createDiagnostics(func(diags tfdiags.Diagnostics) tfdiags.Diagnostics { 87 88 // First, let's create an output that failed that isn't 89 // expected. This should be unaffected by our function. 90 diags = diags.Append( 91 &hcl.Diagnostic{ 92 Severity: hcl.DiagError, 93 Summary: "unexpected failure", 94 Detail: "this should not be removed", 95 Extra: &addrs.CheckRuleDiagnosticExtra{ 96 CheckRule: addrs.NewCheckRule(addrs.AbsOutputValue{ 97 Module: addrs.RootModuleInstance, 98 OutputValue: addrs.OutputValue{Name: "unexpected"}, 99 }, addrs.OutputPrecondition, 0), 100 }, 101 }) 102 103 // Second, let's create an output that failed but is expected. 104 // Our function should remove this from the set of diags. 105 diags = diags.Append( 106 &hcl.Diagnostic{ 107 Severity: hcl.DiagError, 108 Summary: "expected failure", 109 Detail: "this should be removed", 110 Extra: &addrs.CheckRuleDiagnosticExtra{ 111 CheckRule: addrs.NewCheckRule(addrs.AbsOutputValue{ 112 Module: addrs.RootModuleInstance, 113 OutputValue: addrs.OutputValue{Name: "expected_one"}, 114 }, addrs.OutputPrecondition, 0), 115 }, 116 }) 117 118 diags = diags.Append( 119 &hcl.Diagnostic{ 120 Severity: hcl.DiagWarning, 121 Summary: "expected warning", 122 Detail: "this should not be removed", 123 Extra: &addrs.CheckRuleDiagnosticExtra{ 124 CheckRule: addrs.NewCheckRule(addrs.AbsOutputValue{ 125 Module: addrs.RootModuleInstance, 126 OutputValue: addrs.OutputValue{Name: "expected_one"}, 127 }, addrs.OutputPrecondition, 0), 128 }, 129 }) 130 131 // The error we are adding here is for expected_two but in a 132 // child module. We expect that this diagnostic shouldn't 133 // trigger our expected failure, and that an extra diagnostic 134 // should be created complaining that the output wasn't actually 135 // triggered. 136 137 diags = diags.Append( 138 &hcl.Diagnostic{ 139 Severity: hcl.DiagError, 140 Summary: "error in child module", 141 Detail: "this should not be removed", 142 Extra: &addrs.CheckRuleDiagnosticExtra{ 143 CheckRule: addrs.NewCheckRule(addrs.AbsOutputValue{ 144 Module: []addrs.ModuleInstanceStep{ 145 { 146 Name: "child_module", 147 }, 148 }, 149 OutputValue: addrs.OutputValue{Name: "expected_two"}, 150 }, addrs.OutputPrecondition, 0), 151 }, 152 }) 153 154 return diags 155 }), 156 Output: []output{ 157 { 158 Description: tfdiags.Description{ 159 Summary: "unexpected failure", 160 Detail: "this should not be removed", 161 }, 162 Severity: tfdiags.Error, 163 }, 164 { 165 Description: tfdiags.Description{ 166 Summary: "expected warning", 167 Detail: "this should not be removed", 168 }, 169 Severity: tfdiags.Warning, 170 }, 171 { 172 Description: tfdiags.Description{ 173 Summary: "error in child module", 174 Detail: "this should not be removed", 175 }, 176 Severity: tfdiags.Error, 177 }, 178 { 179 Description: tfdiags.Description{ 180 Summary: "Missing expected failure", 181 Detail: "The checkable object, output.expected_two, was expected to report an error but did not.", 182 }, 183 Severity: tfdiags.Error, 184 }, 185 }, 186 }, 187 "variables": { 188 ExpectedFailures: []string{ 189 "var.expected_one", 190 "var.expected_two", 191 }, 192 Input: createDiagnostics(func(diags tfdiags.Diagnostics) tfdiags.Diagnostics { 193 194 // First, let's create an input that failed that isn't 195 // expected. This should be unaffected by our function. 196 diags = diags.Append( 197 &hcl.Diagnostic{ 198 Severity: hcl.DiagError, 199 Summary: "unexpected failure", 200 Detail: "this should not be removed", 201 Extra: &addrs.CheckRuleDiagnosticExtra{ 202 CheckRule: addrs.NewCheckRule(addrs.AbsInputVariableInstance{ 203 Module: addrs.RootModuleInstance, 204 Variable: addrs.InputVariable{Name: "unexpected"}, 205 }, addrs.InputValidation, 0), 206 }, 207 }) 208 209 // Second, let's create an input that failed but is expected. 210 // Our function should remove this from the set of diags. 211 diags = diags.Append( 212 &hcl.Diagnostic{ 213 Severity: hcl.DiagError, 214 Summary: "expected failure", 215 Detail: "this should be removed", 216 Extra: &addrs.CheckRuleDiagnosticExtra{ 217 CheckRule: addrs.NewCheckRule(addrs.AbsInputVariableInstance{ 218 Module: addrs.RootModuleInstance, 219 Variable: addrs.InputVariable{Name: "expected_one"}, 220 }, addrs.InputValidation, 0), 221 }, 222 }) 223 224 diags = diags.Append( 225 &hcl.Diagnostic{ 226 Severity: hcl.DiagWarning, 227 Summary: "expected warning", 228 Detail: "this should not be removed", 229 Extra: &addrs.CheckRuleDiagnosticExtra{ 230 CheckRule: addrs.NewCheckRule(addrs.AbsInputVariableInstance{ 231 Module: addrs.RootModuleInstance, 232 Variable: addrs.InputVariable{Name: "expected_one"}, 233 }, addrs.InputValidation, 0), 234 }, 235 }) 236 237 // The error we are adding here is for expected_two but in a 238 // child module. We expect that this diagnostic shouldn't 239 // trigger our expected failure, and that an extra diagnostic 240 // should be created complaining that the output wasn't actually 241 // triggered. 242 243 diags = diags.Append( 244 &hcl.Diagnostic{ 245 Severity: hcl.DiagError, 246 Summary: "error in child module", 247 Detail: "this should not be removed", 248 Extra: &addrs.CheckRuleDiagnosticExtra{ 249 CheckRule: addrs.NewCheckRule(addrs.AbsInputVariableInstance{ 250 Module: []addrs.ModuleInstanceStep{ 251 { 252 Name: "child_module", 253 }, 254 }, 255 Variable: addrs.InputVariable{Name: "expected_two"}, 256 }, addrs.InputValidation, 0), 257 }, 258 }) 259 260 return diags 261 }), 262 Output: []output{ 263 { 264 Description: tfdiags.Description{ 265 Summary: "unexpected failure", 266 Detail: "this should not be removed", 267 }, 268 Severity: tfdiags.Error, 269 }, 270 { 271 Description: tfdiags.Description{ 272 Summary: "expected warning", 273 Detail: "this should not be removed", 274 }, 275 Severity: tfdiags.Warning, 276 }, 277 { 278 Description: tfdiags.Description{ 279 Summary: "error in child module", 280 Detail: "this should not be removed", 281 }, 282 Severity: tfdiags.Error, 283 }, 284 { 285 Description: tfdiags.Description{ 286 Summary: "Missing expected failure", 287 Detail: "The checkable object, var.expected_two, was expected to report an error but did not.", 288 }, 289 Severity: tfdiags.Error, 290 }, 291 }, 292 }, 293 "resources": { 294 ExpectedFailures: []string{ 295 "test_instance.single", 296 "test_instance.all_instances", 297 "test_instance.instance[0]", 298 "test_instance.instance[2]", 299 "test_instance.missing", 300 }, 301 Input: createDiagnostics(func(diags tfdiags.Diagnostics) tfdiags.Diagnostics { 302 // First, we'll create an unexpected failure that should be 303 // carried through untouched. 304 diags = diags.Append( 305 &hcl.Diagnostic{ 306 Severity: hcl.DiagError, 307 Summary: "unexpected failure", 308 Detail: "this should not be removed", 309 Extra: &addrs.CheckRuleDiagnosticExtra{ 310 CheckRule: addrs.NewCheckRule(addrs.AbsResourceInstance{ 311 Module: addrs.RootModuleInstance, 312 Resource: addrs.ResourceInstance{ 313 Resource: addrs.Resource{ 314 Mode: addrs.ManagedResourceMode, 315 Type: "test_instance", 316 Name: "unexpected", 317 }, 318 }, 319 }, addrs.ResourcePrecondition, 0), 320 }, 321 }) 322 323 // Second, we'll create a failure from our test_instance.single 324 // resource that should be removed. 325 diags = diags.Append( 326 &hcl.Diagnostic{ 327 Severity: hcl.DiagError, 328 Summary: "expected failure in test_instance.single", 329 Detail: "this should be removed", 330 Extra: &addrs.CheckRuleDiagnosticExtra{ 331 CheckRule: addrs.NewCheckRule(addrs.AbsResourceInstance{ 332 Module: addrs.RootModuleInstance, 333 Resource: addrs.ResourceInstance{ 334 Resource: addrs.Resource{ 335 Mode: addrs.ManagedResourceMode, 336 Type: "test_instance", 337 Name: "single", 338 }, 339 }, 340 }, addrs.ResourcePrecondition, 0), 341 }, 342 }) 343 344 // Third, we'll create a warning from our test_instance.single 345 // resource that should be propagated as it is only a warning. 346 diags = diags.Append( 347 &hcl.Diagnostic{ 348 Severity: hcl.DiagWarning, 349 Summary: "expected warning in test_instance.single", 350 Detail: "this should not be removed", 351 Extra: &addrs.CheckRuleDiagnosticExtra{ 352 CheckRule: addrs.NewCheckRule(addrs.AbsResourceInstance{ 353 Module: addrs.RootModuleInstance, 354 Resource: addrs.ResourceInstance{ 355 Resource: addrs.Resource{ 356 Mode: addrs.ManagedResourceMode, 357 Type: "test_instance", 358 Name: "single", 359 }, 360 }, 361 }, addrs.ResourcePrecondition, 0), 362 }, 363 }) 364 365 // Fourth, we'll create diagnostics from several instances of 366 // the test_instance.all_instances which should all be removed. 367 diags = diags.Append( 368 &hcl.Diagnostic{ 369 Severity: hcl.DiagError, 370 Summary: "expected failure in test_instance.all_instances[0]", 371 Detail: "this should be removed", 372 Extra: &addrs.CheckRuleDiagnosticExtra{ 373 CheckRule: addrs.NewCheckRule(addrs.AbsResourceInstance{ 374 Module: addrs.RootModuleInstance, 375 Resource: addrs.ResourceInstance{ 376 Resource: addrs.Resource{ 377 Mode: addrs.ManagedResourceMode, 378 Type: "test_instance", 379 Name: "all_instances", 380 }, 381 Key: addrs.IntKey(0), 382 }, 383 }, addrs.ResourcePrecondition, 0), 384 }, 385 }) 386 diags = diags.Append( 387 &hcl.Diagnostic{ 388 Severity: hcl.DiagError, 389 Summary: "expected failure in test_instance.all_instances[1]", 390 Detail: "this should be removed", 391 Extra: &addrs.CheckRuleDiagnosticExtra{ 392 CheckRule: addrs.NewCheckRule(addrs.AbsResourceInstance{ 393 Module: addrs.RootModuleInstance, 394 Resource: addrs.ResourceInstance{ 395 Resource: addrs.Resource{ 396 Mode: addrs.ManagedResourceMode, 397 Type: "test_instance", 398 Name: "all_instances", 399 }, 400 Key: addrs.IntKey(1), 401 }, 402 }, addrs.ResourcePrecondition, 0), 403 }, 404 }) 405 diags = diags.Append( 406 &hcl.Diagnostic{ 407 Severity: hcl.DiagError, 408 Summary: "expected failure in test_instance.all_instances[2]", 409 Detail: "this should be removed", 410 Extra: &addrs.CheckRuleDiagnosticExtra{ 411 CheckRule: addrs.NewCheckRule(addrs.AbsResourceInstance{ 412 Module: addrs.RootModuleInstance, 413 Resource: addrs.ResourceInstance{ 414 Resource: addrs.Resource{ 415 Mode: addrs.ManagedResourceMode, 416 Type: "test_instance", 417 Name: "all_instances", 418 }, 419 Key: addrs.IntKey(2), 420 }, 421 }, addrs.ResourcePrecondition, 0), 422 }, 423 }) 424 425 // Fifth, we'll create diagnostics for several instances of 426 // the test_instance.instance resource, only some of which 427 // should be removed. 428 diags = diags.Append( 429 &hcl.Diagnostic{ 430 Severity: hcl.DiagError, 431 Summary: "expected failure in test_instance.instance[0]", 432 Detail: "this should be removed", 433 Extra: &addrs.CheckRuleDiagnosticExtra{ 434 CheckRule: addrs.NewCheckRule(addrs.AbsResourceInstance{ 435 Module: addrs.RootModuleInstance, 436 Resource: addrs.ResourceInstance{ 437 Resource: addrs.Resource{ 438 Mode: addrs.ManagedResourceMode, 439 Type: "test_instance", 440 Name: "instance", 441 }, 442 Key: addrs.IntKey(0), 443 }, 444 }, addrs.ResourcePrecondition, 0), 445 }, 446 }) 447 diags = diags.Append( 448 &hcl.Diagnostic{ 449 Severity: hcl.DiagError, 450 Summary: "expected failure in test_instance.instance[1]", 451 Detail: "this should not be removed", 452 Extra: &addrs.CheckRuleDiagnosticExtra{ 453 CheckRule: addrs.NewCheckRule(addrs.AbsResourceInstance{ 454 Module: addrs.RootModuleInstance, 455 Resource: addrs.ResourceInstance{ 456 Resource: addrs.Resource{ 457 Mode: addrs.ManagedResourceMode, 458 Type: "test_instance", 459 Name: "instance", 460 }, 461 Key: addrs.IntKey(1), 462 }, 463 }, addrs.ResourcePrecondition, 0), 464 }, 465 }) 466 diags = diags.Append( 467 &hcl.Diagnostic{ 468 Severity: hcl.DiagError, 469 Summary: "expected failure in test_instance.instance[2]", 470 Detail: "this should be removed", 471 Extra: &addrs.CheckRuleDiagnosticExtra{ 472 CheckRule: addrs.NewCheckRule(addrs.AbsResourceInstance{ 473 Module: addrs.RootModuleInstance, 474 Resource: addrs.ResourceInstance{ 475 Resource: addrs.Resource{ 476 Mode: addrs.ManagedResourceMode, 477 Type: "test_instance", 478 Name: "instance", 479 }, 480 Key: addrs.IntKey(2), 481 }, 482 }, addrs.ResourcePrecondition, 0), 483 }, 484 }) 485 486 // Finally, we'll create an error that originated from 487 // test_instance.missing but in a child module which shouldn't 488 // be removed. 489 diags = diags.Append( 490 &hcl.Diagnostic{ 491 Severity: hcl.DiagError, 492 Summary: "failure in child module", 493 Detail: "this should not be removed", 494 Extra: &addrs.CheckRuleDiagnosticExtra{ 495 CheckRule: addrs.NewCheckRule(addrs.AbsResourceInstance{ 496 Module: []addrs.ModuleInstanceStep{ 497 { 498 Name: "child_module", 499 }, 500 }, 501 Resource: addrs.ResourceInstance{ 502 Resource: addrs.Resource{ 503 Mode: addrs.ManagedResourceMode, 504 Type: "test_instance", 505 Name: "missing", 506 }, 507 }, 508 }, addrs.ResourcePrecondition, 0), 509 }, 510 }) 511 512 return diags 513 }), 514 Output: []output{ 515 { 516 Description: tfdiags.Description{ 517 Summary: "unexpected failure", 518 Detail: "this should not be removed", 519 }, 520 Severity: tfdiags.Error, 521 }, 522 { 523 Description: tfdiags.Description{ 524 Summary: "expected warning in test_instance.single", 525 Detail: "this should not be removed", 526 }, 527 Severity: tfdiags.Warning, 528 }, 529 { 530 Description: tfdiags.Description{ 531 Summary: "expected failure in test_instance.instance[1]", 532 Detail: "this should not be removed", 533 }, 534 Severity: tfdiags.Error, 535 }, 536 { 537 Description: tfdiags.Description{ 538 Summary: "failure in child module", 539 Detail: "this should not be removed", 540 }, 541 Severity: tfdiags.Error, 542 }, 543 { 544 Description: tfdiags.Description{ 545 Summary: "Missing expected failure", 546 Detail: "The checkable object, test_instance.missing, was expected to report an error but did not.", 547 }, 548 Severity: tfdiags.Error, 549 }, 550 }, 551 }, 552 "check_assertions": { 553 ExpectedFailures: []string{ 554 "check.expected", 555 "check.missing", 556 }, 557 Input: createDiagnostics(func(diags tfdiags.Diagnostics) tfdiags.Diagnostics { 558 // First, we'll add an unexpected warning from a check block 559 // assertion that should get upgraded to an error. 560 diags = diags.Append( 561 &hcl.Diagnostic{ 562 Severity: hcl.DiagWarning, 563 Summary: "unexpected failure", 564 Detail: "this should upgrade and not be removed", 565 Extra: &addrs.CheckRuleDiagnosticExtra{ 566 CheckRule: addrs.NewCheckRule(addrs.AbsCheck{ 567 Module: addrs.RootModuleInstance, 568 Check: addrs.Check{ 569 Name: "unexpected", 570 }, 571 }, addrs.CheckAssertion, 0), 572 }, 573 }) 574 575 // Second, we'll add an unexpected warning from a check block 576 // in a child module that should get upgrade to error. 577 diags = diags.Append( 578 &hcl.Diagnostic{ 579 Severity: hcl.DiagWarning, 580 Summary: "expected failure in child module", 581 Detail: "this should upgrade and not be removed", 582 Extra: &addrs.CheckRuleDiagnosticExtra{ 583 CheckRule: addrs.NewCheckRule(addrs.AbsCheck{ 584 Module: []addrs.ModuleInstanceStep{ 585 { 586 Name: "child_module", 587 }, 588 }, 589 Check: addrs.Check{ 590 Name: "expected", 591 }, 592 }, addrs.CheckAssertion, 0), 593 }, 594 }) 595 596 // Third, we'll add an expected warning from a check block 597 // assertion that should be removed. 598 diags = diags.Append( 599 &hcl.Diagnostic{ 600 Severity: hcl.DiagWarning, 601 Summary: "expected failure", 602 Detail: "this should be removed", 603 Extra: &addrs.CheckRuleDiagnosticExtra{ 604 CheckRule: addrs.NewCheckRule(addrs.AbsCheck{ 605 Module: addrs.RootModuleInstance, 606 Check: addrs.Check{ 607 Name: "expected", 608 }, 609 }, addrs.CheckAssertion, 0), 610 }, 611 }) 612 613 // The second expected failure has no diagnostics, we just want 614 // to make sure that a new diagnostic is added for this case. 615 616 return diags 617 }), 618 Output: []output{ 619 { 620 Description: tfdiags.Description{ 621 Summary: "unexpected failure", 622 Detail: "this should upgrade and not be removed", 623 }, 624 Severity: tfdiags.Error, 625 }, 626 { 627 Description: tfdiags.Description{ 628 Summary: "expected failure in child module", 629 Detail: "this should upgrade and not be removed", 630 }, 631 Severity: tfdiags.Error, 632 }, 633 { 634 Description: tfdiags.Description{ 635 Summary: "Missing expected failure", 636 Detail: "The checkable object, check.missing, was expected to report an error but did not.", 637 }, 638 Severity: tfdiags.Error, 639 }, 640 }, 641 }, 642 "check_data_sources": { 643 ExpectedFailures: []string{ 644 "check.expected", 645 }, 646 Input: createDiagnostics(func(diags tfdiags.Diagnostics) tfdiags.Diagnostics { 647 // First, we'll add an unexpected warning from a check block 648 // assertion that should be propagated as an error. 649 diags = diags.Append( 650 tfdiags.Override( 651 tfdiags.Sourceless(tfdiags.Error, "unexpected failure", "this should be an error and not removed"), 652 tfdiags.Warning, 653 func() tfdiags.DiagnosticExtraWrapper { 654 return &addrs.CheckRuleDiagnosticExtra{ 655 CheckRule: addrs.NewCheckRule(addrs.AbsCheck{ 656 Module: addrs.RootModuleInstance, 657 Check: addrs.Check{ 658 Name: "unexpected", 659 }, 660 }, addrs.CheckDataResource, 0), 661 } 662 })) 663 664 // Second, we'll add an unexpected warning from a check block 665 // assertion that should remain as a warning. 666 diags = diags.Append( 667 tfdiags.Override( 668 tfdiags.Sourceless(tfdiags.Warning, "unexpected warning", "this should be a warning and not removed"), 669 tfdiags.Warning, 670 func() tfdiags.DiagnosticExtraWrapper { 671 return &addrs.CheckRuleDiagnosticExtra{ 672 CheckRule: addrs.NewCheckRule(addrs.AbsCheck{ 673 Module: addrs.RootModuleInstance, 674 Check: addrs.Check{ 675 Name: "unexpected", 676 }, 677 }, addrs.CheckDataResource, 0), 678 } 679 })) 680 681 // Third, we'll add an unexpected warning from a check block 682 // in a child module that should be propagated as an error. 683 diags = diags.Append( 684 tfdiags.Override( 685 tfdiags.Sourceless(tfdiags.Error, "expected failure from child module", "this should be an error and not removed"), 686 tfdiags.Warning, 687 func() tfdiags.DiagnosticExtraWrapper { 688 return &addrs.CheckRuleDiagnosticExtra{ 689 CheckRule: addrs.NewCheckRule(addrs.AbsCheck{ 690 Module: []addrs.ModuleInstanceStep{ 691 { 692 Name: "child_module", 693 }, 694 }, 695 Check: addrs.Check{ 696 Name: "expected", 697 }, 698 }, addrs.CheckDataResource, 0), 699 } 700 })) 701 702 // Fourth, we'll add an expected warning that should be removed. 703 diags = diags.Append( 704 tfdiags.Override( 705 tfdiags.Sourceless(tfdiags.Error, "expected failure", "this should be removed"), 706 tfdiags.Warning, 707 func() tfdiags.DiagnosticExtraWrapper { 708 return &addrs.CheckRuleDiagnosticExtra{ 709 CheckRule: addrs.NewCheckRule(addrs.AbsCheck{ 710 Module: addrs.RootModuleInstance, 711 Check: addrs.Check{ 712 Name: "expected", 713 }, 714 }, addrs.CheckDataResource, 0), 715 } 716 })) 717 718 return diags 719 }), 720 Output: []output{ 721 { 722 Description: tfdiags.Description{ 723 Summary: "unexpected failure", 724 Detail: "this should be an error and not removed", 725 }, 726 Severity: tfdiags.Error, 727 }, 728 { 729 Description: tfdiags.Description{ 730 Summary: "unexpected warning", 731 Detail: "this should be a warning and not removed", 732 }, 733 Severity: tfdiags.Warning, 734 }, 735 { 736 Description: tfdiags.Description{ 737 Summary: "expected failure from child module", 738 Detail: "this should be an error and not removed", 739 }, 740 Severity: tfdiags.Error, 741 }, 742 }, 743 }, 744 } 745 for name, tc := range tcs { 746 t.Run(name, func(t *testing.T) { 747 var traversals []hcl.Traversal 748 for _, ef := range tc.ExpectedFailures { 749 traversal, diags := hclsyntax.ParseTraversalAbs([]byte(ef), "foo.tf", hcl.Pos{Line: 1, Column: 1}) 750 if diags.HasErrors() { 751 t.Errorf("invalid expected failure %s: %v", ef, diags.Error()) 752 } 753 traversals = append(traversals, traversal) 754 } 755 756 if t.Failed() { 757 return 758 } 759 760 run := Run{ 761 Config: &configs.TestRun{ 762 ExpectFailures: traversals, 763 }, 764 } 765 766 out := run.ValidateExpectedFailures(tc.Input) 767 ix := 0 768 for ; ix < len(tc.Output); ix++ { 769 expected := tc.Output[ix] 770 771 if ix >= len(out) { 772 t.Errorf("missing diagnostic at %d, expected: [%s] %s, %s", ix, expected.Severity, expected.Description.Summary, expected.Description.Detail) 773 continue 774 } 775 776 actual := output{ 777 Description: out[ix].Description(), 778 Severity: out[ix].Severity(), 779 } 780 781 if diff := cmp.Diff(expected, actual); len(diff) > 0 { 782 t.Errorf("mismatched diagnostic at %d:\n%s", ix, diff) 783 } 784 } 785 786 for ; ix < len(out); ix++ { 787 actual := out[ix] 788 t.Errorf("additional diagnostic at %d: [%s] %s, %s", ix, actual.Severity(), actual.Description().Summary, actual.Description().Detail) 789 } 790 }) 791 } 792 } 793 794 func createDiagnostics(populate func(diags tfdiags.Diagnostics) tfdiags.Diagnostics) tfdiags.Diagnostics { 795 var diags tfdiags.Diagnostics 796 diags = populate(diags) 797 return diags 798 }