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 }