opentofu/internal/terraform/transform_diff_test.go
Martin Atkins 9e277033bc core: Create apply graph nodes even for no-op "changes"
We previously would optimize away the graph nodes for any resource
instance without a real change pending, but that means we don't get an
opportunity to re-check any invariants associated with the instance, such
as preconditions and postconditions.

Other upstream changes during apply can potentially decide the outcome of
a condition even if the instance itself isn't being changed, so we do
still need to revisit these during apply or else we might skip running
certain checks altogether, if they yielded unknown results during planning
and then don't get run during apply.
2022-07-22 15:27:15 -07:00

129 lines
3.3 KiB
Go

package terraform
import (
"strings"
"testing"
"github.com/zclconf/go-cty/cty"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/plans"
)
func TestDiffTransformer_nilDiff(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance}
tf := &DiffTransformer{}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
if len(g.Vertices()) > 0 {
t.Fatal("graph should be empty")
}
}
func TestDiffTransformer(t *testing.T) {
g := Graph{Path: addrs.RootModuleInstance}
beforeVal, err := plans.NewDynamicValue(cty.StringVal(""), cty.String)
if err != nil {
t.Fatal(err)
}
afterVal, err := plans.NewDynamicValue(cty.StringVal(""), cty.String)
if err != nil {
t.Fatal(err)
}
tf := &DiffTransformer{
Changes: &plans.Changes{
Resources: []*plans.ResourceInstanceChangeSrc{
{
Addr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "foo",
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
ProviderAddr: addrs.AbsProviderConfig{
Provider: addrs.NewDefaultProvider("aws"),
Module: addrs.RootModule,
},
ChangeSrc: plans.ChangeSrc{
Action: plans.Update,
Before: beforeVal,
After: afterVal,
},
},
},
},
}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformDiffBasicStr)
if actual != expected {
t.Fatalf("bad:\n\n%s", actual)
}
}
func TestDiffTransformer_noOpChange(t *testing.T) {
// "No-op" changes are how we record explicitly in a plan that we did
// indeed visit a particular resource instance during the planning phase
// and concluded that no changes were needed, as opposed to the resource
// instance not existing at all or having been excluded from planning
// entirely.
//
// We must include nodes for resource instances with no-op changes in the
// apply graph, even though they won't take any external actions, because
// there are some secondary effects such as precondition/postcondition
// checks that can refer to objects elsewhere and so might have their
// results changed even if the resource instance they are attached to
// didn't actually change directly itself.
g := Graph{Path: addrs.RootModuleInstance}
beforeVal, err := plans.NewDynamicValue(cty.StringVal(""), cty.String)
if err != nil {
t.Fatal(err)
}
tf := &DiffTransformer{
Changes: &plans.Changes{
Resources: []*plans.ResourceInstanceChangeSrc{
{
Addr: addrs.Resource{
Mode: addrs.ManagedResourceMode,
Type: "aws_instance",
Name: "foo",
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
ProviderAddr: addrs.AbsProviderConfig{
Provider: addrs.NewDefaultProvider("aws"),
Module: addrs.RootModule,
},
ChangeSrc: plans.ChangeSrc{
// A "no-op" change has the no-op action and has the
// same object as both Before and After.
Action: plans.NoOp,
Before: beforeVal,
After: beforeVal,
},
},
},
},
}
if err := tf.Transform(&g); err != nil {
t.Fatalf("err: %s", err)
}
actual := strings.TrimSpace(g.String())
expected := strings.TrimSpace(testTransformDiffBasicStr)
if actual != expected {
t.Fatalf("bad:\n\n%s", actual)
}
}
const testTransformDiffBasicStr = `
aws_instance.foo
`