github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/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/iaas-resource-provision/iaas-rpc/internal/configs/configload"
    11  	"github.com/iaas-resource-provision/iaas-rpc/internal/plans"
    12  	"github.com/iaas-resource-provision/iaas-rpc/internal/states"
    13  	"github.com/iaas-resource-provision/iaas-rpc/internal/states/statefile"
    14  	tfversion "github.com/iaas-resource-provision/iaas-rpc/version"
    15  )
    16  
    17  func TestRoundtrip(t *testing.T) {
    18  	fixtureDir := filepath.Join("testdata", "test-config")
    19  	loader, err := configload.NewLoader(&configload.Config{
    20  		ModulesDir: filepath.Join(fixtureDir, ".terraform", "modules"),
    21  	})
    22  	if err != nil {
    23  		t.Fatal(err)
    24  	}
    25  
    26  	_, snapIn, diags := loader.LoadConfigWithSnapshot(fixtureDir)
    27  	if diags.HasErrors() {
    28  		t.Fatal(diags.Error())
    29  	}
    30  
    31  	// Just a minimal state file so we can test that it comes out again at all.
    32  	// We don't need to test the entire thing because the state file
    33  	// serialization is already tested in its own package.
    34  	stateFileIn := &statefile.File{
    35  		TerraformVersion: tfversion.SemVer,
    36  		Serial:           2,
    37  		Lineage:          "abc123",
    38  		State:            states.NewState(),
    39  	}
    40  	prevStateFileIn := &statefile.File{
    41  		TerraformVersion: tfversion.SemVer,
    42  		Serial:           1,
    43  		Lineage:          "abc123",
    44  		State:            states.NewState(),
    45  	}
    46  
    47  	// Minimal plan too, since the serialization of the tfplan portion of the
    48  	// file is tested more fully in tfplan_test.go .
    49  	planIn := &plans.Plan{
    50  		Changes: &plans.Changes{
    51  			Resources: []*plans.ResourceInstanceChangeSrc{},
    52  			Outputs:   []*plans.OutputChangeSrc{},
    53  		},
    54  		ProviderSHA256s: map[string][]byte{},
    55  		VariableValues: map[string]plans.DynamicValue{
    56  			"foo": plans.DynamicValue([]byte("foo placeholder")),
    57  		},
    58  		Backend: plans.Backend{
    59  			Type:      "local",
    60  			Config:    plans.DynamicValue([]byte("config placeholder")),
    61  			Workspace: "default",
    62  		},
    63  
    64  		// Due to some historical oddities in how we've changed modelling over
    65  		// time, we also include the states (without the corresponding file
    66  		// headers) in the plans.Plan object. This is currently ignored by
    67  		// Create but will be returned by ReadPlan and so we need to include
    68  		// it here so that we'll get a match when we compare input and output
    69  		// below.
    70  		PrevRunState: prevStateFileIn.State,
    71  		PriorState:   stateFileIn.State,
    72  	}
    73  
    74  	workDir, err := ioutil.TempDir("", "tf-planfile")
    75  	if err != nil {
    76  		t.Fatal(err)
    77  	}
    78  	planFn := filepath.Join(workDir, "tfplan")
    79  
    80  	err = Create(planFn, snapIn, prevStateFileIn, stateFileIn, planIn)
    81  	if err != nil {
    82  		t.Fatalf("failed to create plan file: %s", err)
    83  	}
    84  
    85  	pr, err := Open(planFn)
    86  	if err != nil {
    87  		t.Fatalf("failed to open plan file for reading: %s", err)
    88  	}
    89  
    90  	t.Run("ReadPlan", func(t *testing.T) {
    91  		planOut, err := pr.ReadPlan()
    92  		if err != nil {
    93  			t.Fatalf("failed to read plan: %s", err)
    94  		}
    95  		if diff := cmp.Diff(planIn, planOut); diff != "" {
    96  			t.Errorf("plan did not survive round-trip\n%s", diff)
    97  		}
    98  	})
    99  
   100  	t.Run("ReadStateFile", func(t *testing.T) {
   101  		stateFileOut, err := pr.ReadStateFile()
   102  		if err != nil {
   103  			t.Fatalf("failed to read state: %s", err)
   104  		}
   105  		if diff := cmp.Diff(stateFileIn, stateFileOut); diff != "" {
   106  			t.Errorf("state file did not survive round-trip\n%s", diff)
   107  		}
   108  	})
   109  
   110  	t.Run("ReadPrevStateFile", func(t *testing.T) {
   111  		prevStateFileOut, err := pr.ReadPrevStateFile()
   112  		if err != nil {
   113  			t.Fatalf("failed to read state: %s", err)
   114  		}
   115  		if diff := cmp.Diff(prevStateFileIn, prevStateFileOut); diff != "" {
   116  			t.Errorf("state file did not survive round-trip\n%s", diff)
   117  		}
   118  	})
   119  
   120  	t.Run("ReadConfigSnapshot", func(t *testing.T) {
   121  		snapOut, err := pr.ReadConfigSnapshot()
   122  		if err != nil {
   123  			t.Fatalf("failed to read config snapshot: %s", err)
   124  		}
   125  		if diff := cmp.Diff(snapIn, snapOut); diff != "" {
   126  			t.Errorf("config snapshot did not survive round-trip\n%s", diff)
   127  		}
   128  	})
   129  
   130  	t.Run("ReadConfig", func(t *testing.T) {
   131  		// Reading from snapshots is tested in the configload package, so
   132  		// here we'll just test that we can successfully do it, to see if the
   133  		// glue code in _this_ package is correct.
   134  		_, diags := pr.ReadConfig()
   135  		if diags.HasErrors() {
   136  			t.Errorf("when reading config: %s", diags.Err())
   137  		}
   138  	})
   139  }