diff --git a/.changes/v1.12/BUG FIXES-20250516-114251.yaml b/.changes/v1.12/BUG FIXES-20250516-114251.yaml new file mode 100644 index 000000000000..2a1252eaf408 --- /dev/null +++ b/.changes/v1.12/BUG FIXES-20250516-114251.yaml @@ -0,0 +1,5 @@ +kind: BUG FIXES +body: Avoid crash on test failure in comparison in function call +time: 2025-05-16T11:42:51.289379+01:00 +custom: + Issue: "37071" diff --git a/internal/command/format/diagnostic_test.go b/internal/command/format/diagnostic_test.go index 0c2137589802..40130e00459b 100644 --- a/internal/command/format/diagnostic_test.go +++ b/internal/command/format/diagnostic_test.go @@ -342,6 +342,69 @@ func TestDiagnostic(t *testing.T) { [red]│[reset] [red]│[reset] LHS not equal to RHS [red]╵[reset] +`, + }, + "error originating from failed wrapped test assertion by function": { + &hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "Test assertion failed", + Detail: "Example crash", + Subject: &hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + Expression: &hclsyntax.FunctionCallExpr{ + Name: "tobool", + Args: []hclsyntax.Expression{ + &hclsyntax.BinaryOpExpr{ + Op: hclsyntax.OpEqual, + LHS: &hclsyntax.LiteralValueExpr{ + Val: cty.ObjectVal(map[string]cty.Value{ + "inner": cty.StringVal("str1"), + "extra": cty.StringVal("str2"), + }), + }, + RHS: &hclsyntax.LiteralValueExpr{ + Val: cty.ObjectVal(map[string]cty.Value{ + "inner": cty.StringVal("str11"), + "extra": cty.StringVal("str21"), + }), + }, + SrcRange: hcl.Range{ + Filename: "test.tf", + Start: hcl.Pos{Line: 1, Column: 6, Byte: 5}, + End: hcl.Pos{Line: 1, Column: 12, Byte: 11}, + }, + }, + }, + }, + EvalContext: &hcl.EvalContext{ + Variables: map[string]cty.Value{}, + Functions: map[string]function.Function{ + "tobool": function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "param_0", + Type: cty.String, + }, + }, + }), + }, + }, + // This is simulating what the test assertion expression + // type would generate on evaluation, by implementing the + // same interface it uses. + Extra: diagnosticCausedByTestFailure{true}, + }, + `[red]╷[reset] +[red]│[reset] [bold][red]Error: [reset][bold]Test assertion failed[reset] +[red]│[reset] +[red]│[reset] on test.tf line 1: +[red]│[reset] 1: test [underline]source[reset] code +[red]│[reset] +[red]│[reset] Example crash +[red]╵[reset] `, }, } diff --git a/internal/command/views/json/diagnostic.go b/internal/command/views/json/diagnostic.go index 90f8459c23dc..0b8371b92d51 100644 --- a/internal/command/views/json/diagnostic.go +++ b/internal/command/views/json/diagnostic.go @@ -446,7 +446,9 @@ func NewDiagnostic(diag tfdiags.Diagnostic, sources map[string][]byte) *Diagnost // If the test assertion is a binary expression, we'll include the human-readable // formatted LHS and RHS values in the diagnostic snippet. diagnostic.Snippet.TestAssertionExpr = formatRunBinaryDiag(ctx, fromExpr.Expression) - diagnostic.Snippet.TestAssertionExpr.ShowVerbose = testDiag.IsTestVerboseMode() + if diagnostic.Snippet.TestAssertionExpr != nil { + diagnostic.Snippet.TestAssertionExpr.ShowVerbose = testDiag.IsTestVerboseMode() + } } }