terraform/internal/refactoring/testing_helpers.go
2026-03-06 11:59:59 +01:00

109 lines
3.4 KiB
Go

// Copyright IBM Corp. 2014, 2026
// SPDX-License-Identifier: BUSL-1.1
package refactoring
import (
"testing"
"github.com/hashicorp/hcl/v2/hcltest"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/gocty"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/hashicorp/terraform/internal/configs"
"github.com/hashicorp/terraform/internal/instances"
)
// FakeExternalModuleSource is used in tests to simulate an external module source.
var FakeExternalModuleSource = addrs.ModuleSourceRemote{
Package: addrs.ModulePackage("example.com/test/fake"),
}
// StaticPopulateExpanderModule populates an expander for testing by statically
// evaluating count and for_each expressions in a configuration.
//
// This is exported so that test code in package refactoring_test can use it
// without creating an import cycle with the terraform package.
func StaticPopulateExpanderModule(t *testing.T, rootCfg *configs.Config, moduleAddr addrs.ModuleInstance, expander *instances.Expander) {
t.Helper()
modCfg := rootCfg.DescendantForInstance(moduleAddr)
if modCfg == nil {
t.Fatalf("no configuration for %s", moduleAddr)
}
if len(modCfg.Path) > 0 && modCfg.Path[len(modCfg.Path)-1] == "fake_external" {
modCfg.SourceAddr = FakeExternalModuleSource
}
for _, call := range modCfg.Module.ModuleCalls {
callAddr := addrs.ModuleCall{Name: call.Name}
if call.Name == "fake_external" {
call.SourceExpr = hcltest.MockExprLiteral(cty.StringVal(FakeExternalModuleSource.String()))
}
switch {
case call.ForEach != nil:
val, diags := call.ForEach.Value(nil)
if diags.HasErrors() {
t.Fatalf("invalid for_each: %s", diags.Error())
}
expander.SetModuleForEach(moduleAddr, callAddr, val.AsValueMap())
case call.Count != nil:
val, diags := call.Count.Value(nil)
if diags.HasErrors() {
t.Fatalf("invalid count: %s", diags.Error())
}
var count int
err := gocty.FromCtyValue(val, &count)
if err != nil {
t.Fatalf("invalid count at %s: %s", call.Count.Range(), err)
}
expander.SetModuleCount(moduleAddr, callAddr, count)
default:
expander.SetModuleSingle(moduleAddr, callAddr)
}
calledMod := modCfg.Path.Child(call.Name)
for _, inst := range expander.ExpandModule(calledMod, false) {
StaticPopulateExpanderModule(t, rootCfg, inst, expander)
}
}
for _, rc := range modCfg.Module.ManagedResources {
StaticPopulateExpanderResource(t, moduleAddr, rc, expander)
}
for _, rc := range modCfg.Module.DataResources {
StaticPopulateExpanderResource(t, moduleAddr, rc, expander)
}
}
// StaticPopulateExpanderResource populates resource instances in an expander for testing.
func StaticPopulateExpanderResource(t *testing.T, moduleAddr addrs.ModuleInstance, rCfg *configs.Resource, expander *instances.Expander) {
t.Helper()
addr := rCfg.Addr()
switch {
case rCfg.ForEach != nil:
val, diags := rCfg.ForEach.Value(nil)
if diags.HasErrors() {
t.Fatalf("invalid for_each: %s", diags.Error())
}
expander.SetResourceForEach(moduleAddr, addr, val.AsValueMap())
case rCfg.Count != nil:
val, diags := rCfg.Count.Value(nil)
if diags.HasErrors() {
t.Fatalf("invalid count: %s", diags.Error())
}
var count int
err := gocty.FromCtyValue(val, &count)
if err != nil {
t.Fatalf("invalid count at %s: %s", rCfg.Count.Range(), err)
}
expander.SetResourceCount(moduleAddr, addr, count)
default:
expander.SetResourceSingle(moduleAddr, addr)
}
}