Fixed tofu test when module has no resource (#1409)

Signed-off-by: siddharthasonker95 <158144589+siddharthasonker95@users.noreply.github.com>
This commit is contained in:
Siddhartha Sonker 2024-05-06 18:19:42 +05:30 committed by GitHub
parent 4726d106c8
commit 6065bc593f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 51 additions and 0 deletions

View File

@ -9,6 +9,7 @@ ENHANCEMENTS:
BUG FIXES:
* Added a check in the `tofu test` to validate that the names of test run blocks do not contain spaces. ([#1489](https://github.com/opentofu/opentofu/pull/1489))
* `tofu test` now supports accessing module outputs when the module has no resources. ([#1409](https://github.com/opentofu/opentofu/pull/1409))
## Previous Releases

View File

@ -799,6 +799,10 @@ func TestTest_Modules(t *testing.T) {
code int
skip bool
}{
"pass_module_with_no_resource": {
expected: "main.tftest.hcl... pass\n run \"run\"... pass\n\nSuccess! 1 passed, 0 failed.\n",
code: 0,
},
"with_nested_setup_modules": {
expected: "main.tftest.hcl... pass\n run \"load_module\"... pass\n\nSuccess! 1 passed, 0 failed.\n",
code: 0,

View File

@ -0,0 +1,7 @@
module "second" {
source = "../second"
}
output "id" {
value = module.second.id
}

View File

@ -0,0 +1,5 @@
module "first" {
source = "./first"
}
resource "test_resource" "resource" {}

View File

@ -0,0 +1,8 @@
run "run" {
command = apply
assert {
condition = module.first.id != 0
error_message = "Fail"
}
}

View File

@ -0,0 +1,5 @@
resource "test_resource" "resource" {}
output "id" {
value = test_resource.resource.id
}

View File

@ -67,6 +67,13 @@ func (ctx *TestContext) EvaluateAgainstPlan(run *moduletest.Run) {
}
func (ctx *TestContext) evaluate(state *states.SyncState, changes *plans.ChangesSync, run *moduletest.Run, operation walkOperation) {
// The state does not include the module that has no resources, making its outputs unusable.
// synchronizeStates function synchronizes the state with the planned state, ensuring inclusion of all modules.
if ctx.Plan != nil && ctx.Plan.PlannedState != nil &&
len(ctx.State.Modules) != len(ctx.Plan.PlannedState.Modules) {
state = synchronizeStates(ctx.State, ctx.Plan.PlannedState)
}
data := &evaluationStateData{
Evaluator: &Evaluator{
Operation: operation,
@ -186,3 +193,17 @@ func (ctx *TestContext) evaluate(state *states.SyncState, changes *plans.Changes
}
}
}
// synchronizeStates compares the planned state to the current state and incorporates any missing modules
// from the planned state into the current state.
//
// If a module has no resources, it is included in the current state to ensure that its output variables are usable.
func synchronizeStates(state, plannedState *states.State) *states.SyncState {
newState := state.DeepCopy()
for key, value := range plannedState.Modules {
if _, exists := newState.Modules[key]; !exists {
newState.Modules[key] = value
}
}
return newState.SyncWrapper()
}