github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/plans/planfile/planfile_test.go (about) 1 package planfile 2 3 import ( 4 "path/filepath" 5 "testing" 6 7 "github.com/google/go-cmp/cmp" 8 9 "github.com/hashicorp/terraform/internal/addrs" 10 "github.com/hashicorp/terraform/internal/configs/configload" 11 "github.com/hashicorp/terraform/internal/depsfile" 12 "github.com/hashicorp/terraform/internal/getproviders" 13 "github.com/hashicorp/terraform/internal/plans" 14 "github.com/hashicorp/terraform/internal/states" 15 "github.com/hashicorp/terraform/internal/states/statefile" 16 tfversion "github.com/hashicorp/terraform/version" 17 ) 18 19 func TestRoundtrip(t *testing.T) { 20 fixtureDir := filepath.Join("testdata", "test-config") 21 loader, err := configload.NewLoader(&configload.Config{ 22 ModulesDir: filepath.Join(fixtureDir, ".terraform", "modules"), 23 }) 24 if err != nil { 25 t.Fatal(err) 26 } 27 28 _, snapIn, diags := loader.LoadConfigWithSnapshot(fixtureDir) 29 if diags.HasErrors() { 30 t.Fatal(diags.Error()) 31 } 32 33 // Just a minimal state file so we can test that it comes out again at all. 34 // We don't need to test the entire thing because the state file 35 // serialization is already tested in its own package. 36 stateFileIn := &statefile.File{ 37 TerraformVersion: tfversion.SemVer, 38 Serial: 2, 39 Lineage: "abc123", 40 State: states.NewState(), 41 } 42 prevStateFileIn := &statefile.File{ 43 TerraformVersion: tfversion.SemVer, 44 Serial: 1, 45 Lineage: "abc123", 46 State: states.NewState(), 47 } 48 49 // Minimal plan too, since the serialization of the tfplan portion of the 50 // file is tested more fully in tfplan_test.go . 51 planIn := &plans.Plan{ 52 Changes: &plans.Changes{ 53 Resources: []*plans.ResourceInstanceChangeSrc{}, 54 Outputs: []*plans.OutputChangeSrc{}, 55 }, 56 DriftedResources: []*plans.ResourceInstanceChangeSrc{}, 57 VariableValues: map[string]plans.DynamicValue{ 58 "foo": plans.DynamicValue([]byte("foo placeholder")), 59 }, 60 Backend: plans.Backend{ 61 Type: "local", 62 Config: plans.DynamicValue([]byte("config placeholder")), 63 Workspace: "default", 64 }, 65 Checks: &states.CheckResults{}, 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 planFn := filepath.Join(t.TempDir(), "tfplan") 88 89 err = Create(planFn, CreateArgs{ 90 ConfigSnapshot: snapIn, 91 PreviousRunStateFile: prevStateFileIn, 92 StateFile: stateFileIn, 93 Plan: planIn, 94 DependencyLocks: locksIn, 95 }) 96 if err != nil { 97 t.Fatalf("failed to create plan file: %s", err) 98 } 99 100 pr, err := Open(planFn) 101 if err != nil { 102 t.Fatalf("failed to open plan file for reading: %s", err) 103 } 104 105 t.Run("ReadPlan", func(t *testing.T) { 106 planOut, err := pr.ReadPlan() 107 if err != nil { 108 t.Fatalf("failed to read plan: %s", err) 109 } 110 if diff := cmp.Diff(planIn, planOut); diff != "" { 111 t.Errorf("plan did not survive round-trip\n%s", diff) 112 } 113 }) 114 115 t.Run("ReadStateFile", func(t *testing.T) { 116 stateFileOut, err := pr.ReadStateFile() 117 if err != nil { 118 t.Fatalf("failed to read state: %s", err) 119 } 120 if diff := cmp.Diff(stateFileIn, stateFileOut); diff != "" { 121 t.Errorf("state file did not survive round-trip\n%s", diff) 122 } 123 }) 124 125 t.Run("ReadPrevStateFile", func(t *testing.T) { 126 prevStateFileOut, err := pr.ReadPrevStateFile() 127 if err != nil { 128 t.Fatalf("failed to read state: %s", err) 129 } 130 if diff := cmp.Diff(prevStateFileIn, prevStateFileOut); diff != "" { 131 t.Errorf("state file did not survive round-trip\n%s", diff) 132 } 133 }) 134 135 t.Run("ReadConfigSnapshot", func(t *testing.T) { 136 snapOut, err := pr.ReadConfigSnapshot() 137 if err != nil { 138 t.Fatalf("failed to read config snapshot: %s", err) 139 } 140 if diff := cmp.Diff(snapIn, snapOut); diff != "" { 141 t.Errorf("config snapshot did not survive round-trip\n%s", diff) 142 } 143 }) 144 145 t.Run("ReadConfig", func(t *testing.T) { 146 // Reading from snapshots is tested in the configload package, so 147 // here we'll just test that we can successfully do it, to see if the 148 // glue code in _this_ package is correct. 149 _, diags := pr.ReadConfig() 150 if diags.HasErrors() { 151 t.Errorf("when reading config: %s", diags.Err()) 152 } 153 }) 154 155 t.Run("ReadDependencyLocks", func(t *testing.T) { 156 locksOut, diags := pr.ReadDependencyLocks() 157 if diags.HasErrors() { 158 t.Fatalf("when reading config: %s", diags.Err()) 159 } 160 got := locksOut.AllProviders() 161 want := locksIn.AllProviders() 162 if diff := cmp.Diff(want, got, cmp.AllowUnexported(depsfile.ProviderLock{})); diff != "" { 163 t.Errorf("provider locks did not survive round-trip\n%s", diff) 164 } 165 }) 166 }