github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/configs/configload/loader_load_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package configload 5 6 import ( 7 "path/filepath" 8 "reflect" 9 "sort" 10 "strings" 11 "testing" 12 13 "github.com/davecgh/go-spew/spew" 14 "github.com/zclconf/go-cty/cty" 15 16 "github.com/terramate-io/tf/configs" 17 ) 18 19 func TestLoaderLoadConfig_okay(t *testing.T) { 20 fixtureDir := filepath.Clean("testdata/already-installed") 21 loader, err := NewLoader(&Config{ 22 ModulesDir: filepath.Join(fixtureDir, ".terraform/modules"), 23 }) 24 if err != nil { 25 t.Fatalf("unexpected error from NewLoader: %s", err) 26 } 27 28 cfg, diags := loader.LoadConfig(fixtureDir) 29 assertNoDiagnostics(t, diags) 30 if cfg == nil { 31 t.Fatalf("config is nil; want non-nil") 32 } 33 34 var gotPaths []string 35 cfg.DeepEach(func(c *configs.Config) { 36 gotPaths = append(gotPaths, strings.Join(c.Path, ".")) 37 }) 38 sort.Strings(gotPaths) 39 wantPaths := []string{ 40 "", // root module 41 "child_a", 42 "child_a.child_c", 43 "child_b", 44 "child_b.child_d", 45 } 46 47 if !reflect.DeepEqual(gotPaths, wantPaths) { 48 t.Fatalf("wrong module paths\ngot: %swant %s", spew.Sdump(gotPaths), spew.Sdump(wantPaths)) 49 } 50 51 t.Run("child_a.child_c output", func(t *testing.T) { 52 output := cfg.Children["child_a"].Children["child_c"].Module.Outputs["hello"] 53 got, diags := output.Expr.Value(nil) 54 assertNoDiagnostics(t, diags) 55 assertResultCtyEqual(t, got, cty.StringVal("Hello from child_c")) 56 }) 57 t.Run("child_b.child_d output", func(t *testing.T) { 58 output := cfg.Children["child_b"].Children["child_d"].Module.Outputs["hello"] 59 got, diags := output.Expr.Value(nil) 60 assertNoDiagnostics(t, diags) 61 assertResultCtyEqual(t, got, cty.StringVal("Hello from child_d")) 62 }) 63 } 64 65 func TestLoaderLoadConfig_addVersion(t *testing.T) { 66 // This test is for what happens when there is a version constraint added 67 // to a module that previously didn't have one. 68 fixtureDir := filepath.Clean("testdata/add-version-constraint") 69 loader, err := NewLoader(&Config{ 70 ModulesDir: filepath.Join(fixtureDir, ".terraform/modules"), 71 }) 72 if err != nil { 73 t.Fatalf("unexpected error from NewLoader: %s", err) 74 } 75 76 _, diags := loader.LoadConfig(fixtureDir) 77 if !diags.HasErrors() { 78 t.Fatalf("success; want error") 79 } 80 got := diags.Error() 81 want := "Module version requirements have changed" 82 if !strings.Contains(got, want) { 83 t.Fatalf("wrong error\ngot:\n%s\n\nwant: containing %q", got, want) 84 } 85 } 86 87 func TestLoaderLoadConfig_loadDiags(t *testing.T) { 88 // building a config which didn't load correctly may cause configs to panic 89 fixtureDir := filepath.Clean("testdata/invalid-names") 90 loader, err := NewLoader(&Config{ 91 ModulesDir: filepath.Join(fixtureDir, ".terraform/modules"), 92 }) 93 if err != nil { 94 t.Fatalf("unexpected error from NewLoader: %s", err) 95 } 96 97 cfg, diags := loader.LoadConfig(fixtureDir) 98 if !diags.HasErrors() { 99 t.Fatal("success; want error") 100 } 101 102 if cfg == nil { 103 t.Fatal("partial config not returned with diagnostics") 104 } 105 106 if cfg.Module == nil { 107 t.Fatal("expected config module") 108 } 109 } 110 111 func TestLoaderLoadConfig_loadDiagsFromSubmodules(t *testing.T) { 112 // building a config which didn't load correctly may cause configs to panic 113 fixtureDir := filepath.Clean("testdata/invalid-names-in-submodules") 114 loader, err := NewLoader(&Config{ 115 ModulesDir: filepath.Join(fixtureDir, ".terraform/modules"), 116 }) 117 if err != nil { 118 t.Fatalf("unexpected error from NewLoader: %s", err) 119 } 120 121 cfg, diags := loader.LoadConfig(fixtureDir) 122 if !diags.HasErrors() { 123 t.Fatalf("loading succeeded; want an error") 124 } 125 if got, want := diags.Error(), " Invalid provider local name"; !strings.Contains(got, want) { 126 t.Errorf("missing expected error\nwant substring: %s\ngot: %s", want, got) 127 } 128 129 if cfg == nil { 130 t.Fatal("partial config not returned with diagnostics") 131 } 132 133 if cfg.Module == nil { 134 t.Fatal("expected config module") 135 } 136 } 137 138 func TestLoaderLoadConfig_childProviderGrandchildCount(t *testing.T) { 139 // This test is focused on the specific situation where: 140 // - A child module contains a nested provider block, which is no longer 141 // recommended but supported for backward-compatibility. 142 // - A child of that child does _not_ contain a nested provider block, 143 // and is called with "count" (would also apply to "for_each" and 144 // "depends_on"). 145 // It isn't valid to use "count" with a module that _itself_ contains 146 // a provider configuration, but it _is_ valid for a module with a 147 // provider configuration to call another module with count. We previously 148 // botched this rule and so this is a regression test to cover the 149 // solution to that mistake: 150 // https://github.com/terramate-io/tf/issues/31081 151 152 // Since this test is based on success rather than failure and it's 153 // covering a relatively large set of code where only a small part 154 // contributes to the test, we'll make sure to test both the success and 155 // failure cases here so that we'll have a better chance of noticing if a 156 // future change makes this succeed only because we've reorganized the code 157 // so that the check isn't happening at all anymore. 158 // 159 // If the "not okay" subtest fails, you should also be skeptical about 160 // whether the "okay" subtest is still valid, even if it happens to 161 // still be passing. 162 t.Run("okay", func(t *testing.T) { 163 fixtureDir := filepath.Clean("testdata/child-provider-grandchild-count") 164 loader, err := NewLoader(&Config{ 165 ModulesDir: filepath.Join(fixtureDir, ".terraform/modules"), 166 }) 167 if err != nil { 168 t.Fatalf("unexpected error from NewLoader: %s", err) 169 } 170 171 cfg, diags := loader.LoadConfig(fixtureDir) 172 assertNoDiagnostics(t, diags) 173 if cfg == nil { 174 t.Fatalf("config is nil; want non-nil") 175 } 176 177 var gotPaths []string 178 cfg.DeepEach(func(c *configs.Config) { 179 gotPaths = append(gotPaths, strings.Join(c.Path, ".")) 180 }) 181 sort.Strings(gotPaths) 182 wantPaths := []string{ 183 "", // root module 184 "child", 185 "child.grandchild", 186 } 187 188 if !reflect.DeepEqual(gotPaths, wantPaths) { 189 t.Fatalf("wrong module paths\ngot: %swant %s", spew.Sdump(gotPaths), spew.Sdump(wantPaths)) 190 } 191 }) 192 t.Run("not okay", func(t *testing.T) { 193 fixtureDir := filepath.Clean("testdata/child-provider-child-count") 194 loader, err := NewLoader(&Config{ 195 ModulesDir: filepath.Join(fixtureDir, ".terraform/modules"), 196 }) 197 if err != nil { 198 t.Fatalf("unexpected error from NewLoader: %s", err) 199 } 200 201 _, diags := loader.LoadConfig(fixtureDir) 202 if !diags.HasErrors() { 203 t.Fatalf("loading succeeded; want an error") 204 } 205 if got, want := diags.Error(), "Module is incompatible with count, for_each, and depends_on"; !strings.Contains(got, want) { 206 t.Errorf("missing expected error\nwant substring: %s\ngot: %s", want, got) 207 } 208 }) 209 210 }