mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-29 10:21:01 -06:00
4bc1696fd1
The graph walking mechanism is specified as requiring a graph with a single root, which in practice means there's exactly one node in the graph which doesn't have any dependencies. However, we previously weren't verifying that invariant is true for subgraphs returned from DynamicExpand. It was working anyway, but it's not ideal to be relying on a behavior that isn't guaranteed by our underlying infrastructure. We also previously had the RootTransformer being a bit clever and trying to avoid adding a new node if there is already only a single graph with no dependencies. That special case isn't particularly valuable since there's no harm in turning a one-node graph into a two-node graph with an explicit separate root node, and doing that allows us to assume that the root node is always present and is always exactly terraform.rootNode. Many existing DynamicExpand implementations were not producing valid graphs and were previously getting away with it. All of them now produce properly-rooted graphs that should pass validation, and we will guarantee that with an explicit check of the DynamicExpand return value before we try to walk that subgraph. For good measure we also verify that the root node is exactly terraform.rootNode, even though that isn't strictly required by our graph walker, just to help us catch potential future bugs where a DynamicExpand implementation neglects to add our singleton root node.
96 lines
2.0 KiB
Go
96 lines
2.0 KiB
Go
package terraform
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/hashicorp/terraform/internal/addrs"
|
|
)
|
|
|
|
func TestRootTransformer(t *testing.T) {
|
|
t.Run("many nodes", func(t *testing.T) {
|
|
mod := testModule(t, "transform-root-basic")
|
|
|
|
g := Graph{Path: addrs.RootModuleInstance}
|
|
{
|
|
tf := &ConfigTransformer{Config: mod}
|
|
if err := tf.Transform(&g); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
}
|
|
|
|
{
|
|
transform := &MissingProviderTransformer{}
|
|
if err := transform.Transform(&g); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
}
|
|
|
|
{
|
|
transform := &ProviderTransformer{}
|
|
if err := transform.Transform(&g); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
}
|
|
|
|
{
|
|
transform := &RootTransformer{}
|
|
if err := transform.Transform(&g); err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
}
|
|
|
|
actual := strings.TrimSpace(g.String())
|
|
expected := strings.TrimSpace(testTransformRootBasicStr)
|
|
if actual != expected {
|
|
t.Fatalf("wrong result\n\ngot:\n%s\n\nwant:\n%s", actual, expected)
|
|
}
|
|
|
|
root, err := g.Root()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
if _, ok := root.(graphNodeRoot); !ok {
|
|
t.Fatalf("bad: %#v", root)
|
|
}
|
|
})
|
|
|
|
t.Run("only one initial node", func(t *testing.T) {
|
|
g := Graph{Path: addrs.RootModuleInstance}
|
|
g.Add("foo")
|
|
addRootNodeToGraph(&g)
|
|
got := strings.TrimSpace(g.String())
|
|
want := strings.TrimSpace(`
|
|
foo
|
|
root
|
|
foo
|
|
`)
|
|
if got != want {
|
|
t.Errorf("wrong final graph\ngot:\n%s\nwant:\n%s", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("graph initially empty", func(t *testing.T) {
|
|
g := Graph{Path: addrs.RootModuleInstance}
|
|
addRootNodeToGraph(&g)
|
|
got := strings.TrimSpace(g.String())
|
|
want := `root`
|
|
if got != want {
|
|
t.Errorf("wrong final graph\ngot:\n%s\nwant:\n%s", got, want)
|
|
}
|
|
})
|
|
|
|
}
|
|
|
|
const testTransformRootBasicStr = `
|
|
aws_instance.foo
|
|
provider["registry.terraform.io/hashicorp/aws"]
|
|
do_droplet.bar
|
|
provider["registry.terraform.io/hashicorp/do"]
|
|
provider["registry.terraform.io/hashicorp/aws"]
|
|
provider["registry.terraform.io/hashicorp/do"]
|
|
root
|
|
aws_instance.foo
|
|
do_droplet.bar
|
|
`
|