kubeform.dev/terraform-backend-sdk@v0.0.0-20220310143633-45f07fe731c5/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 "kubeform.dev/terraform-backend-sdk/configs/configload" 11 "kubeform.dev/terraform-backend-sdk/plans" 12 "kubeform.dev/terraform-backend-sdk/states" 13 "kubeform.dev/terraform-backend-sdk/states/statefile" 14 tfversion "kubeform.dev/terraform-backend-sdk/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 DriftedResources: []*plans.ResourceInstanceChangeSrc{}, 55 ProviderSHA256s: map[string][]byte{}, 56 VariableValues: map[string]plans.DynamicValue{ 57 "foo": plans.DynamicValue([]byte("foo placeholder")), 58 }, 59 Backend: plans.Backend{ 60 Type: "local", 61 Config: plans.DynamicValue([]byte("config placeholder")), 62 Workspace: "default", 63 }, 64 65 // Due to some historical oddities in how we've changed modelling over 66 // time, we also include the states (without the corresponding file 67 // headers) in the plans.Plan object. This is currently ignored by 68 // Create but will be returned by ReadPlan and so we need to include 69 // it here so that we'll get a match when we compare input and output 70 // below. 71 PrevRunState: prevStateFileIn.State, 72 PriorState: stateFileIn.State, 73 } 74 75 workDir, err := ioutil.TempDir("", "tf-planfile") 76 if err != nil { 77 t.Fatal(err) 78 } 79 planFn := filepath.Join(workDir, "tfplan") 80 81 err = Create(planFn, snapIn, prevStateFileIn, stateFileIn, planIn) 82 if err != nil { 83 t.Fatalf("failed to create plan file: %s", err) 84 } 85 86 pr, err := Open(planFn) 87 if err != nil { 88 t.Fatalf("failed to open plan file for reading: %s", err) 89 } 90 91 t.Run("ReadPlan", func(t *testing.T) { 92 planOut, err := pr.ReadPlan() 93 if err != nil { 94 t.Fatalf("failed to read plan: %s", err) 95 } 96 if diff := cmp.Diff(planIn, planOut); diff != "" { 97 t.Errorf("plan did not survive round-trip\n%s", diff) 98 } 99 }) 100 101 t.Run("ReadStateFile", func(t *testing.T) { 102 stateFileOut, err := pr.ReadStateFile() 103 if err != nil { 104 t.Fatalf("failed to read state: %s", err) 105 } 106 if diff := cmp.Diff(stateFileIn, stateFileOut); diff != "" { 107 t.Errorf("state file did not survive round-trip\n%s", diff) 108 } 109 }) 110 111 t.Run("ReadPrevStateFile", func(t *testing.T) { 112 prevStateFileOut, err := pr.ReadPrevStateFile() 113 if err != nil { 114 t.Fatalf("failed to read state: %s", err) 115 } 116 if diff := cmp.Diff(prevStateFileIn, prevStateFileOut); diff != "" { 117 t.Errorf("state file did not survive round-trip\n%s", diff) 118 } 119 }) 120 121 t.Run("ReadConfigSnapshot", func(t *testing.T) { 122 snapOut, err := pr.ReadConfigSnapshot() 123 if err != nil { 124 t.Fatalf("failed to read config snapshot: %s", err) 125 } 126 if diff := cmp.Diff(snapIn, snapOut); diff != "" { 127 t.Errorf("config snapshot did not survive round-trip\n%s", diff) 128 } 129 }) 130 131 t.Run("ReadConfig", func(t *testing.T) { 132 // Reading from snapshots is tested in the configload package, so 133 // here we'll just test that we can successfully do it, to see if the 134 // glue code in _this_ package is correct. 135 _, diags := pr.ReadConfig() 136 if diags.HasErrors() { 137 t.Errorf("when reading config: %s", diags.Err()) 138 } 139 }) 140 }