github.com/richardbowden/terraform@v0.6.12-0.20160901200758-30ea22c25211/state/testing.go (about)

     1  package state
     2  
     3  import (
     4  	"reflect"
     5  	"testing"
     6  
     7  	"github.com/hashicorp/terraform/terraform"
     8  )
     9  
    10  // TestState is a helper for testing state implementations. It is expected
    11  // that the given implementation is pre-loaded with the TestStateInitial
    12  // state.
    13  func TestState(t *testing.T, s interface{}) {
    14  	reader, ok := s.(StateReader)
    15  	if !ok {
    16  		t.Fatalf("must at least be a StateReader")
    17  	}
    18  
    19  	// If it implements refresh, refresh
    20  	if rs, ok := s.(StateRefresher); ok {
    21  		if err := rs.RefreshState(); err != nil {
    22  			t.Fatalf("err: %s", err)
    23  		}
    24  	}
    25  
    26  	// current will track our current state
    27  	current := TestStateInitial()
    28  
    29  	// Check that the initial state is correct
    30  	if state := reader.State(); !current.Equal(state) {
    31  		t.Fatalf("not initial:\n%#v\n\n%#v", state, current)
    32  	}
    33  
    34  	// Write a new state and verify that we have it
    35  	if ws, ok := s.(StateWriter); ok {
    36  		current.AddModuleState(&terraform.ModuleState{
    37  			Path: []string{"root"},
    38  			Outputs: map[string]*terraform.OutputState{
    39  				"bar": &terraform.OutputState{
    40  					Type:      "string",
    41  					Sensitive: false,
    42  					Value:     "baz",
    43  				},
    44  			},
    45  		})
    46  
    47  		if err := ws.WriteState(current); err != nil {
    48  			t.Fatalf("err: %s", err)
    49  		}
    50  
    51  		if actual := reader.State(); !actual.Equal(current) {
    52  			t.Fatalf("bad:\n%#v\n\n%#v", actual, current)
    53  		}
    54  	}
    55  
    56  	// Test persistence
    57  	if ps, ok := s.(StatePersister); ok {
    58  		if err := ps.PersistState(); err != nil {
    59  			t.Fatalf("err: %s", err)
    60  		}
    61  
    62  		// Refresh if we got it
    63  		if rs, ok := s.(StateRefresher); ok {
    64  			if err := rs.RefreshState(); err != nil {
    65  				t.Fatalf("err: %s", err)
    66  			}
    67  		}
    68  
    69  		// Just set the serials the same... Then compare.
    70  		actual := reader.State()
    71  		if !actual.Equal(current) {
    72  			t.Fatalf("bad: %#v\n\n%#v", actual, current)
    73  		}
    74  	}
    75  
    76  	// If we can write and persist then verify that the serial
    77  	// is only implemented on change.
    78  	writer, writeOk := s.(StateWriter)
    79  	persister, persistOk := s.(StatePersister)
    80  	if writeOk && persistOk {
    81  		// Same serial
    82  		serial := current.Serial
    83  		if err := writer.WriteState(current); err != nil {
    84  			t.Fatalf("err: %s", err)
    85  		}
    86  		if err := persister.PersistState(); err != nil {
    87  			t.Fatalf("err: %s", err)
    88  		}
    89  
    90  		if reader.State().Serial != serial {
    91  			t.Fatalf("bad: expected %d, got %d", serial, reader.State().Serial)
    92  		}
    93  
    94  		// Change the serial
    95  		currentCopy := *current
    96  		current = &currentCopy
    97  		current.Modules = []*terraform.ModuleState{
    98  			&terraform.ModuleState{
    99  				Path: []string{"root", "somewhere"},
   100  				Outputs: map[string]*terraform.OutputState{
   101  					"serialCheck": &terraform.OutputState{
   102  						Type:      "string",
   103  						Sensitive: false,
   104  						Value:     "true",
   105  					},
   106  				},
   107  			},
   108  		}
   109  		if err := writer.WriteState(current); err != nil {
   110  			t.Fatalf("err: %s", err)
   111  		}
   112  		if err := persister.PersistState(); err != nil {
   113  			t.Fatalf("err: %s", err)
   114  		}
   115  
   116  		if reader.State().Serial <= serial {
   117  			t.Fatalf("bad: expected %d, got %d", serial, reader.State().Serial)
   118  		}
   119  
   120  		// Check that State() returns a copy by modifying the copy and comparing
   121  		// to the current state.
   122  		stateCopy := reader.State()
   123  		stateCopy.Serial++
   124  		if reflect.DeepEqual(stateCopy, current) {
   125  			t.Fatal("State() should return a copy")
   126  		}
   127  	}
   128  }
   129  
   130  // TestStateInitial is the initial state that a State should have
   131  // for TestState.
   132  func TestStateInitial() *terraform.State {
   133  	initial := &terraform.State{
   134  		Modules: []*terraform.ModuleState{
   135  			&terraform.ModuleState{
   136  				Path: []string{"root", "child"},
   137  				Outputs: map[string]*terraform.OutputState{
   138  					"foo": &terraform.OutputState{
   139  						Type:      "string",
   140  						Sensitive: false,
   141  						Value:     "bar",
   142  					},
   143  				},
   144  			},
   145  		},
   146  	}
   147  
   148  	initial.Init()
   149  
   150  	return initial
   151  }