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  }