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  }