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  }