Files
opentf/internal/lang/marks/marks_test.go
Martin Atkins e6955dd824 lang/marks: Use lower-overhead cty functions for marks
In the previous commit we upgraded to cty v1.17.0, which already gave us
some performance improvements for functions like UnmarkDeepWithPaths.

However, this release also includes some new functions that allow more
surgical work with marks, and so we can get some additional gains by using
those in the codepaths related to deprecation tracking.

In particular, using WrangleMarksDeep instead of UnmarkDeepWithPaths means
that we can tell cty that we only want to remove the deprecation-related
marks, and so when given a value that contains only non-deprecation-related
marks it can avoid constructing a new cty.Value altogether, and instead
just return the one that was given. In the case where there _are_
deprecation-related marks, it will rebuild only the parts of the data
structure that lead to those marks. Deprecation-related marks are rare in
practice, so this should be a relatively good payoff.

Signed-off-by: Martin Atkins <mart@degeneration.co.uk>
2025-09-09 10:07:29 -07:00

171 lines
4.6 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 marks
import (
"fmt"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/hcl/v2"
"github.com/zclconf/go-cty/cty"
"github.com/opentofu/opentofu/internal/addrs"
"github.com/opentofu/opentofu/internal/tfdiags"
)
// Integration test for the marks to see if consolidate warnings is
// checking by the key of the mark together with the address
func TestMarkConsolidateWarnings(t *testing.T) {
var diags tfdiags.Diagnostics
for i := 0; i < 2; i++ {
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
// Summary is the same for both diagnostics, but key is different
Summary: "Output deprecated",
Detail: fmt.Sprintf("This one has an output %d", i),
Subject: &hcl.Range{
Filename: "foo.tf",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 1, Byte: 0},
},
Extra: DeprecationCause{
By: addrs.OutputValue{
Name: "output",
},
Key: fmt.Sprintf("output%d", i),
Message: "output deprecate",
},
})
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "Variable deprecated",
Detail: fmt.Sprintf("This one has a var %d", i),
Subject: &hcl.Range{
Filename: "foo.tf",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 1, Byte: 0},
},
Extra: DeprecationCause{
By: addrs.InputVariable{
Name: "variable",
},
Key: fmt.Sprintf("variable%d", i),
Message: "variable deprecate",
},
})
}
// Adding an extra diagnostic with the same key to be consolidated
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "Variable deprecated",
Detail: "This one has a var 1",
Subject: &hcl.Range{
Filename: "foo.tf",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 1, Byte: 0},
},
Extra: DeprecationCause{
By: addrs.InputVariable{
Name: "variable",
},
Key: "variable1",
Message: "variable deprecate",
},
})
// Adding an extra diagnostic with a variable in a module
diags = diags.Append(&hcl.Diagnostic{
Severity: hcl.DiagWarning,
Summary: "Variable deprecated",
Detail: "This one has a var 1 in a module",
Subject: &hcl.Range{
Filename: "foo.tf",
Start: hcl.Pos{Line: 1, Column: 1, Byte: 0},
End: hcl.Pos{Line: 1, Column: 1, Byte: 0},
},
Extra: DeprecationCause{
By: addrs.InputVariable{
Name: "mod1.variable",
},
Key: "mod1.variable1",
Message: "variable deprecate",
},
})
consolidatedDiags := diags.Consolidate(1, tfdiags.Warning)
expectedDescriptions := [][2]string{
{"Output deprecated", "This one has an output 0"},
{"Variable deprecated", "This one has a var 0"},
{"Output deprecated", "This one has an output 1"},
{"Variable deprecated", "This one has a var 1\n\n(and one more similar warning elsewhere)"},
{"Variable deprecated", "This one has a var 1 in a module"},
}
// We created 5 diagnostics, but the last one is consolidated
expectedLen := len(expectedDescriptions)
if len(consolidatedDiags) != expectedLen {
t.Errorf("len %d is expected, got %d", expectedLen, len(consolidatedDiags))
}
for i, vals := range expectedDescriptions {
if diff := cmp.Diff(vals[0], consolidatedDiags[i].Description().Summary); diff != "" {
t.Errorf("%d: wrong summary: %s", i, diff)
}
if diff := cmp.Diff(vals[1], consolidatedDiags[i].Description().Detail); diff != "" {
t.Errorf("%d: wrong detail msg: %s", i, diff)
}
}
}
func TestHasDeprecated(t *testing.T) {
tests := []struct {
name string
input cty.Value
want bool
}{
{
name: "no marks",
input: cty.StringVal("test"),
want: false,
},
{
name: "only sensitive mark",
input: cty.StringVal("test").Mark(Sensitive),
want: false,
},
{
name: "has deprecation mark",
input: Deprecated(cty.StringVal("test"), DeprecationCause{
By: addrs.InputVariable{Name: "var1"},
Key: "var1",
Message: "deprecated",
}),
want: true,
},
{
name: "mixed marks with deprecation",
input: Deprecated(cty.StringVal("test").Mark(Sensitive), DeprecationCause{
By: addrs.InputVariable{Name: "var1"},
Key: "var1",
Message: "deprecated",
}),
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := HasDeprecated(tt.input)
if got != tt.want {
t.Errorf("HasDeprecated() = %v, want %v", got, tt.want)
}
})
}
}