github.com/muratcelep/terraform@v1.1.0-beta2-not-internal-4/not-internal/plans/planfile/planfile_test.go (about)

     1  package planfile
     2  
     3  import (
     4  	"io/ioutil"
     5  	"path/filepath"
     6  	"testing"
     7  
     8  	"github.com/google/go-cmp/cmp"
     9  
    10  	"github.com/muratcelep/terraform/not-internal/addrs"
    11  	"github.com/muratcelep/terraform/not-internal/configs/configload"
    12  	"github.com/muratcelep/terraform/not-internal/depsfile"
    13  	"github.com/muratcelep/terraform/not-internal/getproviders"
    14  	"github.com/muratcelep/terraform/not-internal/plans"
    15  	"github.com/muratcelep/terraform/not-internal/states"
    16  	"github.com/muratcelep/terraform/not-internal/states/statefile"
    17  	tfversion "github.com/muratcelep/terraform/version"
    18  )
    19  
    20  func TestRoundtrip(t *testing.T) {
    21  	fixtureDir := filepath.Join("testdata", "test-config")
    22  	loader, err := configload.NewLoader(&configload.Config{
    23  		ModulesDir: filepath.Join(fixtureDir, ".terraform", "modules"),
    24  	})
    25  	if err != nil {
    26  		t.Fatal(err)
    27  	}
    28  
    29  	_, snapIn, diags := loader.LoadConfigWithSnapshot(fixtureDir)
    30  	if diags.HasErrors() {
    31  		t.Fatal(diags.Error())
    32  	}
    33  
    34  	// Just a minimal state file so we can test that it comes out again at all.
    35  	// We don't need to test the entire thing because the state file
    36  	// serialization is already tested in its own package.
    37  	stateFileIn := &statefile.File{
    38  		TerraformVersion: tfversion.SemVer,
    39  		Serial:           2,
    40  		Lineage:          "abc123",
    41  		State:            states.NewState(),
    42  	}
    43  	prevStateFileIn := &statefile.File{
    44  		TerraformVersion: tfversion.SemVer,
    45  		Serial:           1,
    46  		Lineage:          "abc123",
    47  		State:            states.NewState(),
    48  	}
    49  
    50  	// Minimal plan too, since the serialization of the tfplan portion of the
    51  	// file is tested more fully in tfplan_test.go .
    52  	planIn := &plans.Plan{
    53  		Changes: &plans.Changes{
    54  			Resources: []*plans.ResourceInstanceChangeSrc{},
    55  			Outputs:   []*plans.OutputChangeSrc{},
    56  		},
    57  		DriftedResources: []*plans.ResourceInstanceChangeSrc{},
    58  		VariableValues: map[string]plans.DynamicValue{
    59  			"foo": plans.DynamicValue([]byte("foo placeholder")),
    60  		},
    61  		Backend: plans.Backend{
    62  			Type:      "local",
    63  			Config:    plans.DynamicValue([]byte("config placeholder")),
    64  			Workspace: "default",
    65  		},
    66  
    67  		// Due to some historical oddities in how we've changed modelling over
    68  		// time, we also include the states (without the corresponding file
    69  		// headers) in the plans.Plan object. This is currently ignored by
    70  		// Create but will be returned by ReadPlan and so we need to include
    71  		// it here so that we'll get a match when we compare input and output
    72  		// below.
    73  		PrevRunState: prevStateFileIn.State,
    74  		PriorState:   stateFileIn.State,
    75  	}
    76  
    77  	locksIn := depsfile.NewLocks()
    78  	locksIn.SetProvider(
    79  		addrs.NewDefaultProvider("boop"),
    80  		getproviders.MustParseVersion("1.0.0"),
    81  		getproviders.MustParseVersionConstraints(">= 1.0.0"),
    82  		[]getproviders.Hash{
    83  			getproviders.MustParseHash("fake:hello"),
    84  		},
    85  	)
    86  
    87  	workDir, err := ioutil.TempDir("", "tf-planfile")
    88  	if err != nil {
    89  		t.Fatal(err)
    90  	}
    91  	planFn := filepath.Join(workDir, "tfplan")
    92  
    93  	err = Create(planFn, CreateArgs{
    94  		ConfigSnapshot:       snapIn,
    95  		PreviousRunStateFile: prevStateFileIn,
    96  		StateFile:            stateFileIn,
    97  		Plan:                 planIn,
    98  		DependencyLocks:      locksIn,
    99  	})
   100  	if err != nil {
   101  		t.Fatalf("failed to create plan file: %s", err)
   102  	}
   103  
   104  	pr, err := Open(planFn)
   105  	if err != nil {
   106  		t.Fatalf("failed to open plan file for reading: %s", err)
   107  	}
   108  
   109  	t.Run("ReadPlan", func(t *testing.T) {
   110  		planOut, err := pr.ReadPlan()
   111  		if err != nil {
   112  			t.Fatalf("failed to read plan: %s", err)
   113  		}
   114  		if diff := cmp.Diff(planIn, planOut); diff != "" {
   115  			t.Errorf("plan did not survive round-trip\n%s", diff)
   116  		}
   117  	})
   118  
   119  	t.Run("ReadStateFile", func(t *testing.T) {
   120  		stateFileOut, err := pr.ReadStateFile()
   121  		if err != nil {
   122  			t.Fatalf("failed to read state: %s", err)
   123  		}
   124  		if diff := cmp.Diff(stateFileIn, stateFileOut); diff != "" {
   125  			t.Errorf("state file did not survive round-trip\n%s", diff)
   126  		}
   127  	})
   128  
   129  	t.Run("ReadPrevStateFile", func(t *testing.T) {
   130  		prevStateFileOut, err := pr.ReadPrevStateFile()
   131  		if err != nil {
   132  			t.Fatalf("failed to read state: %s", err)
   133  		}
   134  		if diff := cmp.Diff(prevStateFileIn, prevStateFileOut); diff != "" {
   135  			t.Errorf("state file did not survive round-trip\n%s", diff)
   136  		}
   137  	})
   138  
   139  	t.Run("ReadConfigSnapshot", func(t *testing.T) {
   140  		snapOut, err := pr.ReadConfigSnapshot()
   141  		if err != nil {
   142  			t.Fatalf("failed to read config snapshot: %s", err)
   143  		}
   144  		if diff := cmp.Diff(snapIn, snapOut); diff != "" {
   145  			t.Errorf("config snapshot did not survive round-trip\n%s", diff)
   146  		}
   147  	})
   148  
   149  	t.Run("ReadConfig", func(t *testing.T) {
   150  		// Reading from snapshots is tested in the configload package, so
   151  		// here we'll just test that we can successfully do it, to see if the
   152  		// glue code in _this_ package is correct.
   153  		_, diags := pr.ReadConfig()
   154  		if diags.HasErrors() {
   155  			t.Errorf("when reading config: %s", diags.Err())
   156  		}
   157  	})
   158  
   159  	t.Run("ReadDependencyLocks", func(t *testing.T) {
   160  		locksOut, diags := pr.ReadDependencyLocks()
   161  		if diags.HasErrors() {
   162  			t.Fatalf("when reading config: %s", diags.Err())
   163  		}
   164  		got := locksOut.AllProviders()
   165  		want := locksIn.AllProviders()
   166  		if diff := cmp.Diff(want, got, cmp.AllowUnexported(depsfile.ProviderLock{})); diff != "" {
   167  			t.Errorf("provider locks did not survive round-trip\n%s", diff)
   168  		}
   169  	})
   170  }