github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/states/statemgr/testing.go (about) 1 package statemgr 2 3 import ( 4 "reflect" 5 "testing" 6 7 "github.com/davecgh/go-spew/spew" 8 9 "github.com/hashicorp/terraform/states/statefile" 10 11 "github.com/hashicorp/terraform/addrs" 12 "github.com/zclconf/go-cty/cty" 13 14 "github.com/hashicorp/terraform/states" 15 ) 16 17 // TestFull is a helper for testing full state manager implementations. It 18 // expects that the given implementation is pre-loaded with a snapshot of the 19 // result from TestFullInitialState. 20 // 21 // If the given state manager also implements PersistentMeta, this function 22 // will test that the snapshot metadata changes as expected between calls 23 // to the methods of Persistent. 24 func TestFull(t *testing.T, s Full) { 25 t.Helper() 26 27 if err := s.RefreshState(); err != nil { 28 t.Fatalf("err: %s", err) 29 } 30 31 // Check that the initial state is correct. 32 // These do have different Lineages, but we will replace current below. 33 initial := TestFullInitialState() 34 if state := s.State(); !state.Equal(initial) { 35 t.Fatalf("state does not match expected initial state\n\ngot:\n%s\nwant:\n%s", spew.Sdump(state), spew.Sdump(initial)) 36 } 37 38 var initialMeta SnapshotMeta 39 if sm, ok := s.(PersistentMeta); ok { 40 initialMeta = sm.StateSnapshotMeta() 41 } 42 43 // Now we've proven that the state we're starting with is an initial 44 // state, we'll complete our work here with that state, since otherwise 45 // further writes would violate the invariant that we only try to write 46 // states that share the same lineage as what was initially written. 47 current := s.State() 48 49 // Write a new state and verify that we have it 50 current.RootModule().SetOutputValue("bar", cty.StringVal("baz"), false) 51 52 if err := s.WriteState(current); err != nil { 53 t.Fatalf("err: %s", err) 54 } 55 56 if actual := s.State(); !actual.Equal(current) { 57 t.Fatalf("bad:\n%#v\n\n%#v", actual, current) 58 } 59 60 // Test persistence 61 if err := s.PersistState(); err != nil { 62 t.Fatalf("err: %s", err) 63 } 64 65 // Refresh if we got it 66 if err := s.RefreshState(); err != nil { 67 t.Fatalf("err: %s", err) 68 } 69 70 var newMeta SnapshotMeta 71 if sm, ok := s.(PersistentMeta); ok { 72 newMeta = sm.StateSnapshotMeta() 73 if got, want := newMeta.Lineage, initialMeta.Lineage; got != want { 74 t.Errorf("Lineage changed from %q to %q", want, got) 75 } 76 if after, before := newMeta.Serial, initialMeta.Serial; after == before { 77 t.Errorf("Serial didn't change from %d after new module added", before) 78 } 79 } 80 81 // Same serial 82 serial := newMeta.Serial 83 if err := s.WriteState(current); err != nil { 84 t.Fatalf("err: %s", err) 85 } 86 if err := s.PersistState(); err != nil { 87 t.Fatalf("err: %s", err) 88 } 89 90 if sm, ok := s.(PersistentMeta); ok { 91 newMeta = sm.StateSnapshotMeta() 92 if newMeta.Serial != serial { 93 t.Fatalf("serial changed after persisting with no changes: got %d, want %d", newMeta.Serial, serial) 94 } 95 } 96 97 if sm, ok := s.(PersistentMeta); ok { 98 newMeta = sm.StateSnapshotMeta() 99 } 100 101 // Change the serial 102 current = current.DeepCopy() 103 current.EnsureModule(addrs.RootModuleInstance).SetOutputValue( 104 "serialCheck", cty.StringVal("true"), false, 105 ) 106 if err := s.WriteState(current); err != nil { 107 t.Fatalf("err: %s", err) 108 } 109 if err := s.PersistState(); err != nil { 110 t.Fatalf("err: %s", err) 111 } 112 113 if sm, ok := s.(PersistentMeta); ok { 114 oldMeta := newMeta 115 newMeta = sm.StateSnapshotMeta() 116 117 if newMeta.Serial <= serial { 118 t.Fatalf("serial incorrect after persisting with changes: got %d, want > %d", newMeta.Serial, serial) 119 } 120 121 if newMeta.TerraformVersion != oldMeta.TerraformVersion { 122 t.Fatalf("TFVersion changed from %s to %s", oldMeta.TerraformVersion, newMeta.TerraformVersion) 123 } 124 125 // verify that Lineage doesn't change along with Serial, or during copying. 126 if newMeta.Lineage != oldMeta.Lineage { 127 t.Fatalf("Lineage changed from %q to %q", oldMeta.Lineage, newMeta.Lineage) 128 } 129 } 130 131 // Check that State() returns a copy by modifying the copy and comparing 132 // to the current state. 133 stateCopy := s.State() 134 stateCopy.EnsureModule(addrs.RootModuleInstance.Child("another", addrs.NoKey)) 135 if reflect.DeepEqual(stateCopy, s.State()) { 136 t.Fatal("State() should return a copy") 137 } 138 139 // our current expected state should also marshal identically to the persisted state 140 if !statefile.StatesMarshalEqual(current, s.State()) { 141 t.Fatalf("Persisted state altered unexpectedly.\n\ngot:\n%s\nwant:\n%s", spew.Sdump(s.State()), spew.Sdump(current)) 142 } 143 } 144 145 // TestFullInitialState is a state that should be snapshotted into a 146 // full state manager before passing it into TestFull. 147 func TestFullInitialState() *states.State { 148 state := states.NewState() 149 childMod := state.EnsureModule(addrs.RootModuleInstance.Child("child", addrs.NoKey)) 150 rAddr := addrs.Resource{ 151 Mode: addrs.ManagedResourceMode, 152 Type: "null_resource", 153 Name: "foo", 154 } 155 childMod.SetResourceMeta(rAddr, states.EachList, rAddr.DefaultProviderConfig().Absolute(addrs.RootModuleInstance)) 156 return state 157 }