github.com/opentofu/opentofu@v1.7.1/internal/tofu/node_module_expand_test.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package tofu 7 8 import ( 9 "testing" 10 11 "github.com/hashicorp/hcl/v2/hcltest" 12 "github.com/opentofu/opentofu/internal/addrs" 13 "github.com/opentofu/opentofu/internal/configs" 14 "github.com/opentofu/opentofu/internal/instances" 15 "github.com/opentofu/opentofu/internal/states" 16 "github.com/zclconf/go-cty/cty" 17 ) 18 19 func TestNodeExpandModuleExecute(t *testing.T) { 20 ctx := &MockEvalContext{ 21 InstanceExpanderExpander: instances.NewExpander(), 22 } 23 ctx.installSimpleEval() 24 25 node := nodeExpandModule{ 26 Addr: addrs.Module{"child"}, 27 ModuleCall: &configs.ModuleCall{ 28 Count: hcltest.MockExprLiteral(cty.NumberIntVal(2)), 29 }, 30 } 31 32 err := node.Execute(ctx, walkApply) 33 if err != nil { 34 t.Fatalf("unexpected error: %s", err) 35 } 36 37 if !ctx.InstanceExpanderCalled { 38 t.Fatal("did not expand") 39 } 40 } 41 42 func TestNodeCloseModuleExecute(t *testing.T) { 43 t.Run("walkApply", func(t *testing.T) { 44 state := states.NewState() 45 state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 46 ctx := &MockEvalContext{ 47 StateState: state.SyncWrapper(), 48 } 49 node := nodeCloseModule{addrs.Module{"child"}} 50 diags := node.Execute(ctx, walkApply) 51 if diags.HasErrors() { 52 t.Fatalf("unexpected error: %s", diags.Err()) 53 } 54 55 // Since module.child has no resources, it should be removed 56 if _, ok := state.Modules["module.child"]; !ok { 57 t.Fatal("module.child should not be removed from state yet") 58 } 59 60 // the root module should do all the module cleanup 61 node = nodeCloseModule{addrs.RootModule} 62 diags = node.Execute(ctx, walkApply) 63 if diags.HasErrors() { 64 t.Fatalf("unexpected error: %s", diags.Err()) 65 } 66 67 // Since module.child has no resources, it should be removed 68 if _, ok := state.Modules["module.child"]; ok { 69 t.Fatal("module.child was not removed from state") 70 } 71 }) 72 73 // walkImport is a no-op 74 t.Run("walkImport", func(t *testing.T) { 75 state := states.NewState() 76 state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 77 ctx := &MockEvalContext{ 78 StateState: state.SyncWrapper(), 79 } 80 node := nodeCloseModule{addrs.Module{"child"}} 81 82 diags := node.Execute(ctx, walkImport) 83 if diags.HasErrors() { 84 t.Fatalf("unexpected error: %s", diags.Err()) 85 } 86 if _, ok := state.Modules["module.child"]; !ok { 87 t.Fatal("module.child was removed from state, expected no-op") 88 } 89 }) 90 } 91 92 func TestNodeValidateModuleExecute(t *testing.T) { 93 t.Run("success", func(t *testing.T) { 94 ctx := &MockEvalContext{ 95 InstanceExpanderExpander: instances.NewExpander(), 96 } 97 ctx.installSimpleEval() 98 node := nodeValidateModule{ 99 nodeExpandModule{ 100 Addr: addrs.Module{"child"}, 101 ModuleCall: &configs.ModuleCall{ 102 Count: hcltest.MockExprLiteral(cty.NumberIntVal(2)), 103 }, 104 }, 105 } 106 107 diags := node.Execute(ctx, walkApply) 108 if diags.HasErrors() { 109 t.Fatalf("unexpected error: %v", diags.Err()) 110 } 111 }) 112 113 t.Run("invalid count", func(t *testing.T) { 114 ctx := &MockEvalContext{ 115 InstanceExpanderExpander: instances.NewExpander(), 116 } 117 ctx.installSimpleEval() 118 node := nodeValidateModule{ 119 nodeExpandModule{ 120 Addr: addrs.Module{"child"}, 121 ModuleCall: &configs.ModuleCall{ 122 Count: hcltest.MockExprLiteral(cty.StringVal("invalid")), 123 }, 124 }, 125 } 126 127 err := node.Execute(ctx, walkApply) 128 if err == nil { 129 t.Fatal("expected error, got success") 130 } 131 }) 132 133 }