Files
opentf/internal/tfdiags/override_test.go
Martin Atkins 8701ed9907 tfdiags: Override cooperates with contextual diagnostics
This package has two different mechanisms that can cause an existing
diagnostic to be replaced with a modified version:
- "Override" effectively wraps an arbitrary other diagnostic with a new
  severity and optional new "extra info".
- Contextual diagnostics allow a diagnostic to be created in one place
  with some incomplete information, and then "elaborated" in another place
  where more information is available.

Those two mechanisms did not previously interact well together: passing
a contextual diagnostic to Override would prevent its elaboration process
from taking effect.

Now the overriddenDiagnostic type -- implementation detail of "Override" --
implements the same unexported interface that the contextual diagnostics
do, and delegates the elaboration request to the diagnostic that it's
wrapping so that the elaboration can take effect while still preserving
the effect of the overrides.

This combination is okay because in practice "override" only affects
Severity and ExtraInfo, while contextual diagnostics today only affect
the source location information and the "address" components of the
diagnostic, so the two can work independently of one another.

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
2025-05-06 14:54:24 -07:00

125 lines
3.4 KiB
Go

// Copyright (c) The OpenTofu Authors
// SPDX-License-Identifier: MPL-2.0
// Copyright (c) 2023 HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package tfdiags
import (
"testing"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hcltest"
)
func TestOverride_UpdatesSeverity(t *testing.T) {
original := Sourceless(Error, "summary", "detail")
override := Override(original, Warning, nil)
if override.Severity() != Warning {
t.Errorf("expected warning but was %s", override.Severity())
}
}
func TestOverride_MaintainsExtra(t *testing.T) {
original := hclDiagnostic{&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "summary",
Detail: "detail",
Extra: "extra",
}}
override := Override(original, Warning, nil)
if override.ExtraInfo().(string) != "extra" {
t.Errorf("invalid extra info %v", override.ExtraInfo())
}
}
func TestOverride_WrapsExtra(t *testing.T) {
original := hclDiagnostic{&hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "summary",
Detail: "detail",
Extra: "extra",
}}
override := Override(original, Warning, func() DiagnosticExtraWrapper {
return &extraWrapper{
mine: "mine",
}
})
wrapper := override.ExtraInfo().(*extraWrapper)
if wrapper.mine != "mine" {
t.Errorf("invalid extra info %v", override.ExtraInfo())
}
if wrapper.original.(string) != "extra" {
t.Errorf("invalid wrapped extra info %v", override.ExtraInfo())
}
}
func TestOverride_Contextual(t *testing.T) {
// Wrapping a body-contextual diagnostic with Override should still allow
// for the final elaboration of the wrapped diagnostic, while also
// preserving the override data.
original := WholeContainingBody(
Error,
"Placeholder error",
"...",
)
override := Override(original, Warning, func() DiagnosticExtraWrapper {
return &extraWrapper{
mine: "mine",
}
})
var diags Diagnostics
diags = diags.Append(override)
diags = diags.InConfigBody(hcltest.MockBody(&hcl.BodyContent{}), "placeholder address")
if got, want := len(diags), 1; got != want {
t.Fatalf("wrong number of diagnostics after elaboration %d; want %d", got, want)
}
diag := diags[0]
desc := diag.Description()
if got, want := desc.Address, "placeholder address"; got != want {
t.Errorf("wrong value for diag.Description().Address\ngot: %q\nwant: %q", got, want)
}
if got, want := diag.Severity(), Warning; got != want {
t.Errorf("wrong final severity %s; want %s", got, want)
}
wrapper, ok := diag.ExtraInfo().(*extraWrapper)
if !ok {
t.Fatalf("final diagnostic has wrong ExtraInfo type\ngot: %T\nwant: %T", diag.ExtraInfo(), wrapper)
}
if got, want := wrapper.mine, "mine"; got != want {
t.Errorf("wrong ExtraInfo value\ngot: %q\nwant: %q", got, want)
}
}
func TestUndoOverride(t *testing.T) {
original := Sourceless(Error, "summary", "detail")
override := Override(original, Warning, nil)
restored := UndoOverride(override)
if restored.Severity() != Error {
t.Errorf("expected warning but was %s", restored.Severity())
}
}
func TestUndoOverride_NotOverridden(t *testing.T) {
original := Sourceless(Error, "summary", "detail")
restored := UndoOverride(original) // Shouldn't do anything bad.
if restored.Severity() != Error {
t.Errorf("expected warning but was %s", restored.Severity())
}
}
type extraWrapper struct {
mine string
original interface{}
}
func (e *extraWrapper) WrapDiagnosticExtra(inner interface{}) {
e.original = inner
}