github.com/r3labs/terraform@v0.8.4/terraform/state_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"reflect"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/hashicorp/terraform/config"
    12  )
    13  
    14  func TestStateValidate(t *testing.T) {
    15  	cases := map[string]struct {
    16  		In  *State
    17  		Err bool
    18  	}{
    19  		"empty state": {
    20  			&State{},
    21  			false,
    22  		},
    23  
    24  		"multiple modules": {
    25  			&State{
    26  				Modules: []*ModuleState{
    27  					&ModuleState{
    28  						Path: []string{"root", "foo"},
    29  					},
    30  					&ModuleState{
    31  						Path: []string{"root", "foo"},
    32  					},
    33  				},
    34  			},
    35  			true,
    36  		},
    37  	}
    38  
    39  	for name, tc := range cases {
    40  		// Init the state
    41  		tc.In.init()
    42  
    43  		err := tc.In.Validate()
    44  		if (err != nil) != tc.Err {
    45  			t.Fatalf("%s: err: %s", name, err)
    46  		}
    47  	}
    48  }
    49  
    50  func TestStateAddModule(t *testing.T) {
    51  	cases := []struct {
    52  		In  [][]string
    53  		Out [][]string
    54  	}{
    55  		{
    56  			[][]string{
    57  				[]string{"root"},
    58  				[]string{"root", "child"},
    59  			},
    60  			[][]string{
    61  				[]string{"root"},
    62  				[]string{"root", "child"},
    63  			},
    64  		},
    65  
    66  		{
    67  			[][]string{
    68  				[]string{"root", "foo", "bar"},
    69  				[]string{"root", "foo"},
    70  				[]string{"root"},
    71  				[]string{"root", "bar"},
    72  			},
    73  			[][]string{
    74  				[]string{"root"},
    75  				[]string{"root", "bar"},
    76  				[]string{"root", "foo"},
    77  				[]string{"root", "foo", "bar"},
    78  			},
    79  		},
    80  		// Same last element, different middle element
    81  		{
    82  			[][]string{
    83  				[]string{"root", "foo", "bar"}, // This one should sort after...
    84  				[]string{"root", "foo"},
    85  				[]string{"root"},
    86  				[]string{"root", "bar", "bar"}, // ...this one.
    87  				[]string{"root", "bar"},
    88  			},
    89  			[][]string{
    90  				[]string{"root"},
    91  				[]string{"root", "bar"},
    92  				[]string{"root", "foo"},
    93  				[]string{"root", "bar", "bar"},
    94  				[]string{"root", "foo", "bar"},
    95  			},
    96  		},
    97  	}
    98  
    99  	for _, tc := range cases {
   100  		s := new(State)
   101  		for _, p := range tc.In {
   102  			s.AddModule(p)
   103  		}
   104  
   105  		actual := make([][]string, 0, len(tc.In))
   106  		for _, m := range s.Modules {
   107  			actual = append(actual, m.Path)
   108  		}
   109  
   110  		if !reflect.DeepEqual(actual, tc.Out) {
   111  			t.Fatalf("In: %#v\n\nOut: %#v", tc.In, actual)
   112  		}
   113  	}
   114  }
   115  
   116  func TestStateOutputTypeRoundTrip(t *testing.T) {
   117  	state := &State{
   118  		Modules: []*ModuleState{
   119  			&ModuleState{
   120  				Path: RootModulePath,
   121  				Outputs: map[string]*OutputState{
   122  					"string_output": &OutputState{
   123  						Value: "String Value",
   124  						Type:  "string",
   125  					},
   126  				},
   127  			},
   128  		},
   129  	}
   130  	state.init()
   131  
   132  	buf := new(bytes.Buffer)
   133  	if err := WriteState(state, buf); err != nil {
   134  		t.Fatalf("err: %s", err)
   135  	}
   136  
   137  	roundTripped, err := ReadState(buf)
   138  	if err != nil {
   139  		t.Fatalf("err: %s", err)
   140  	}
   141  
   142  	if !reflect.DeepEqual(state, roundTripped) {
   143  		t.Logf("expected:\n%#v", state)
   144  		t.Fatalf("got:\n%#v", roundTripped)
   145  	}
   146  }
   147  
   148  func TestStateModuleOrphans(t *testing.T) {
   149  	state := &State{
   150  		Modules: []*ModuleState{
   151  			&ModuleState{
   152  				Path: RootModulePath,
   153  			},
   154  			&ModuleState{
   155  				Path: []string{RootModuleName, "foo"},
   156  			},
   157  			&ModuleState{
   158  				Path: []string{RootModuleName, "bar"},
   159  			},
   160  		},
   161  	}
   162  
   163  	state.init()
   164  
   165  	config := testModule(t, "state-module-orphans").Config()
   166  	actual := state.ModuleOrphans(RootModulePath, config)
   167  	expected := [][]string{
   168  		[]string{RootModuleName, "foo"},
   169  	}
   170  
   171  	if !reflect.DeepEqual(actual, expected) {
   172  		t.Fatalf("bad: %#v", actual)
   173  	}
   174  }
   175  
   176  func TestStateModuleOrphans_nested(t *testing.T) {
   177  	state := &State{
   178  		Modules: []*ModuleState{
   179  			&ModuleState{
   180  				Path: RootModulePath,
   181  			},
   182  			&ModuleState{
   183  				Path: []string{RootModuleName, "foo", "bar"},
   184  			},
   185  		},
   186  	}
   187  
   188  	state.init()
   189  
   190  	actual := state.ModuleOrphans(RootModulePath, nil)
   191  	expected := [][]string{
   192  		[]string{RootModuleName, "foo"},
   193  	}
   194  
   195  	if !reflect.DeepEqual(actual, expected) {
   196  		t.Fatalf("bad: %#v", actual)
   197  	}
   198  }
   199  
   200  func TestStateModuleOrphans_nilConfig(t *testing.T) {
   201  	state := &State{
   202  		Modules: []*ModuleState{
   203  			&ModuleState{
   204  				Path: RootModulePath,
   205  			},
   206  			&ModuleState{
   207  				Path: []string{RootModuleName, "foo"},
   208  			},
   209  			&ModuleState{
   210  				Path: []string{RootModuleName, "bar"},
   211  			},
   212  		},
   213  	}
   214  
   215  	state.init()
   216  
   217  	actual := state.ModuleOrphans(RootModulePath, nil)
   218  	expected := [][]string{
   219  		[]string{RootModuleName, "foo"},
   220  		[]string{RootModuleName, "bar"},
   221  	}
   222  
   223  	if !reflect.DeepEqual(actual, expected) {
   224  		t.Fatalf("bad: %#v", actual)
   225  	}
   226  }
   227  
   228  func TestStateModuleOrphans_deepNestedNilConfig(t *testing.T) {
   229  	state := &State{
   230  		Modules: []*ModuleState{
   231  			&ModuleState{
   232  				Path: RootModulePath,
   233  			},
   234  			&ModuleState{
   235  				Path: []string{RootModuleName, "parent", "childfoo"},
   236  			},
   237  			&ModuleState{
   238  				Path: []string{RootModuleName, "parent", "childbar"},
   239  			},
   240  		},
   241  	}
   242  
   243  	state.init()
   244  
   245  	actual := state.ModuleOrphans(RootModulePath, nil)
   246  	expected := [][]string{
   247  		[]string{RootModuleName, "parent"},
   248  	}
   249  
   250  	if !reflect.DeepEqual(actual, expected) {
   251  		t.Fatalf("bad: %#v", actual)
   252  	}
   253  }
   254  
   255  func TestStateDeepCopy(t *testing.T) {
   256  	cases := []struct {
   257  		State *State
   258  	}{
   259  		// Version
   260  		{
   261  			&State{Version: 5},
   262  		},
   263  		// TFVersion
   264  		{
   265  			&State{TFVersion: "5"},
   266  		},
   267  		// Modules
   268  		{
   269  			&State{
   270  				Version: 6,
   271  				Modules: []*ModuleState{
   272  					&ModuleState{
   273  						Path: rootModulePath,
   274  						Resources: map[string]*ResourceState{
   275  							"test_instance.foo": &ResourceState{
   276  								Primary: &InstanceState{
   277  									Meta: map[string]string{},
   278  								},
   279  							},
   280  						},
   281  					},
   282  				},
   283  			},
   284  		},
   285  		// Deposed
   286  		// The nil values shouldn't be there if the State was properly init'ed,
   287  		// but the Copy should still work anyway.
   288  		{
   289  			&State{
   290  				Version: 6,
   291  				Modules: []*ModuleState{
   292  					&ModuleState{
   293  						Path: rootModulePath,
   294  						Resources: map[string]*ResourceState{
   295  							"test_instance.foo": &ResourceState{
   296  								Primary: &InstanceState{
   297  									Meta: map[string]string{},
   298  								},
   299  								Deposed: []*InstanceState{
   300  									{ID: "test"},
   301  									nil,
   302  								},
   303  							},
   304  						},
   305  					},
   306  				},
   307  			},
   308  		},
   309  	}
   310  
   311  	for i, tc := range cases {
   312  		t.Run(fmt.Sprintf("copy-%d", i), func(t *testing.T) {
   313  			actual := tc.State.DeepCopy()
   314  			expected := tc.State
   315  			if !reflect.DeepEqual(actual, expected) {
   316  				t.Fatalf("Expected: %#v\nRecevied: %#v\n", expected, actual)
   317  			}
   318  		})
   319  	}
   320  }
   321  
   322  func TestStateEqual(t *testing.T) {
   323  	cases := []struct {
   324  		Result   bool
   325  		One, Two *State
   326  	}{
   327  		// Nils
   328  		{
   329  			false,
   330  			nil,
   331  			&State{Version: 2},
   332  		},
   333  
   334  		{
   335  			true,
   336  			nil,
   337  			nil,
   338  		},
   339  
   340  		// Different versions
   341  		{
   342  			false,
   343  			&State{Version: 5},
   344  			&State{Version: 2},
   345  		},
   346  
   347  		// Different modules
   348  		{
   349  			false,
   350  			&State{
   351  				Modules: []*ModuleState{
   352  					&ModuleState{
   353  						Path: RootModulePath,
   354  					},
   355  				},
   356  			},
   357  			&State{},
   358  		},
   359  
   360  		{
   361  			true,
   362  			&State{
   363  				Modules: []*ModuleState{
   364  					&ModuleState{
   365  						Path: RootModulePath,
   366  					},
   367  				},
   368  			},
   369  			&State{
   370  				Modules: []*ModuleState{
   371  					&ModuleState{
   372  						Path: RootModulePath,
   373  					},
   374  				},
   375  			},
   376  		},
   377  
   378  		// Meta differs
   379  		{
   380  			false,
   381  			&State{
   382  				Modules: []*ModuleState{
   383  					&ModuleState{
   384  						Path: rootModulePath,
   385  						Resources: map[string]*ResourceState{
   386  							"test_instance.foo": &ResourceState{
   387  								Primary: &InstanceState{
   388  									Meta: map[string]string{
   389  										"schema_version": "1",
   390  									},
   391  								},
   392  							},
   393  						},
   394  					},
   395  				},
   396  			},
   397  			&State{
   398  				Modules: []*ModuleState{
   399  					&ModuleState{
   400  						Path: rootModulePath,
   401  						Resources: map[string]*ResourceState{
   402  							"test_instance.foo": &ResourceState{
   403  								Primary: &InstanceState{
   404  									Meta: map[string]string{
   405  										"schema_version": "2",
   406  									},
   407  								},
   408  							},
   409  						},
   410  					},
   411  				},
   412  			},
   413  		},
   414  	}
   415  
   416  	for i, tc := range cases {
   417  		if tc.One.Equal(tc.Two) != tc.Result {
   418  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
   419  		}
   420  		if tc.Two.Equal(tc.One) != tc.Result {
   421  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
   422  		}
   423  	}
   424  }
   425  
   426  func TestStateCompareAges(t *testing.T) {
   427  	cases := []struct {
   428  		Result   StateAgeComparison
   429  		Err      bool
   430  		One, Two *State
   431  	}{
   432  		{
   433  			StateAgeEqual, false,
   434  			&State{
   435  				Lineage: "1",
   436  				Serial:  2,
   437  			},
   438  			&State{
   439  				Lineage: "1",
   440  				Serial:  2,
   441  			},
   442  		},
   443  		{
   444  			StateAgeReceiverOlder, false,
   445  			&State{
   446  				Lineage: "1",
   447  				Serial:  2,
   448  			},
   449  			&State{
   450  				Lineage: "1",
   451  				Serial:  3,
   452  			},
   453  		},
   454  		{
   455  			StateAgeReceiverNewer, false,
   456  			&State{
   457  				Lineage: "1",
   458  				Serial:  3,
   459  			},
   460  			&State{
   461  				Lineage: "1",
   462  				Serial:  2,
   463  			},
   464  		},
   465  		{
   466  			StateAgeEqual, true,
   467  			&State{
   468  				Lineage: "1",
   469  				Serial:  2,
   470  			},
   471  			&State{
   472  				Lineage: "2",
   473  				Serial:  2,
   474  			},
   475  		},
   476  		{
   477  			StateAgeEqual, true,
   478  			&State{
   479  				Lineage: "1",
   480  				Serial:  3,
   481  			},
   482  			&State{
   483  				Lineage: "2",
   484  				Serial:  2,
   485  			},
   486  		},
   487  	}
   488  
   489  	for i, tc := range cases {
   490  		result, err := tc.One.CompareAges(tc.Two)
   491  
   492  		if err != nil && !tc.Err {
   493  			t.Errorf(
   494  				"%d: got error, but want success\n\n%s\n\n%s",
   495  				i, tc.One, tc.Two,
   496  			)
   497  			continue
   498  		}
   499  
   500  		if err == nil && tc.Err {
   501  			t.Errorf(
   502  				"%d: got success, but want error\n\n%s\n\n%s",
   503  				i, tc.One, tc.Two,
   504  			)
   505  			continue
   506  		}
   507  
   508  		if result != tc.Result {
   509  			t.Errorf(
   510  				"%d: got result %d, but want %d\n\n%s\n\n%s",
   511  				i, result, tc.Result, tc.One, tc.Two,
   512  			)
   513  			continue
   514  		}
   515  	}
   516  }
   517  
   518  func TestStateSameLineage(t *testing.T) {
   519  	cases := []struct {
   520  		Result   bool
   521  		One, Two *State
   522  	}{
   523  		{
   524  			true,
   525  			&State{
   526  				Lineage: "1",
   527  			},
   528  			&State{
   529  				Lineage: "1",
   530  			},
   531  		},
   532  		{
   533  			// Empty lineage is compatible with all
   534  			true,
   535  			&State{
   536  				Lineage: "",
   537  			},
   538  			&State{
   539  				Lineage: "1",
   540  			},
   541  		},
   542  		{
   543  			// Empty lineage is compatible with all
   544  			true,
   545  			&State{
   546  				Lineage: "1",
   547  			},
   548  			&State{
   549  				Lineage: "",
   550  			},
   551  		},
   552  		{
   553  			false,
   554  			&State{
   555  				Lineage: "1",
   556  			},
   557  			&State{
   558  				Lineage: "2",
   559  			},
   560  		},
   561  	}
   562  
   563  	for i, tc := range cases {
   564  		result := tc.One.SameLineage(tc.Two)
   565  
   566  		if result != tc.Result {
   567  			t.Errorf(
   568  				"%d: got %v, but want %v\n\n%s\n\n%s",
   569  				i, result, tc.Result, tc.One, tc.Two,
   570  			)
   571  			continue
   572  		}
   573  	}
   574  }
   575  
   576  func TestStateIncrementSerialMaybe(t *testing.T) {
   577  	cases := map[string]struct {
   578  		S1, S2 *State
   579  		Serial int64
   580  	}{
   581  		"S2 is nil": {
   582  			&State{},
   583  			nil,
   584  			0,
   585  		},
   586  		"S2 is identical": {
   587  			&State{},
   588  			&State{},
   589  			0,
   590  		},
   591  		"S2 is different": {
   592  			&State{},
   593  			&State{
   594  				Modules: []*ModuleState{
   595  					&ModuleState{Path: rootModulePath},
   596  				},
   597  			},
   598  			1,
   599  		},
   600  		"S2 is different, but only via Instance Metadata": {
   601  			&State{
   602  				Serial: 3,
   603  				Modules: []*ModuleState{
   604  					&ModuleState{
   605  						Path: rootModulePath,
   606  						Resources: map[string]*ResourceState{
   607  							"test_instance.foo": &ResourceState{
   608  								Primary: &InstanceState{
   609  									Meta: map[string]string{},
   610  								},
   611  							},
   612  						},
   613  					},
   614  				},
   615  			},
   616  			&State{
   617  				Serial: 3,
   618  				Modules: []*ModuleState{
   619  					&ModuleState{
   620  						Path: rootModulePath,
   621  						Resources: map[string]*ResourceState{
   622  							"test_instance.foo": &ResourceState{
   623  								Primary: &InstanceState{
   624  									Meta: map[string]string{
   625  										"schema_version": "1",
   626  									},
   627  								},
   628  							},
   629  						},
   630  					},
   631  				},
   632  			},
   633  			4,
   634  		},
   635  		"S1 serial is higher": {
   636  			&State{Serial: 5},
   637  			&State{
   638  				Serial: 3,
   639  				Modules: []*ModuleState{
   640  					&ModuleState{Path: rootModulePath},
   641  				},
   642  			},
   643  			5,
   644  		},
   645  		"S2 has a different TFVersion": {
   646  			&State{TFVersion: "0.1"},
   647  			&State{TFVersion: "0.2"},
   648  			1,
   649  		},
   650  	}
   651  
   652  	for name, tc := range cases {
   653  		tc.S1.IncrementSerialMaybe(tc.S2)
   654  		if tc.S1.Serial != tc.Serial {
   655  			t.Fatalf("Bad: %s\nGot: %d", name, tc.S1.Serial)
   656  		}
   657  	}
   658  }
   659  
   660  func TestStateRemove(t *testing.T) {
   661  	cases := map[string]struct {
   662  		Address  string
   663  		One, Two *State
   664  	}{
   665  		"simple resource": {
   666  			"test_instance.foo",
   667  			&State{
   668  				Modules: []*ModuleState{
   669  					&ModuleState{
   670  						Path: rootModulePath,
   671  						Resources: map[string]*ResourceState{
   672  							"test_instance.foo": &ResourceState{
   673  								Type: "test_instance",
   674  								Primary: &InstanceState{
   675  									ID: "foo",
   676  								},
   677  							},
   678  
   679  							"test_instance.bar": &ResourceState{
   680  								Type: "test_instance",
   681  								Primary: &InstanceState{
   682  									ID: "foo",
   683  								},
   684  							},
   685  						},
   686  					},
   687  				},
   688  			},
   689  			&State{
   690  				Modules: []*ModuleState{
   691  					&ModuleState{
   692  						Path: rootModulePath,
   693  						Resources: map[string]*ResourceState{
   694  							"test_instance.bar": &ResourceState{
   695  								Type: "test_instance",
   696  								Primary: &InstanceState{
   697  									ID: "foo",
   698  								},
   699  							},
   700  						},
   701  					},
   702  				},
   703  			},
   704  		},
   705  
   706  		"single instance": {
   707  			"test_instance.foo.primary",
   708  			&State{
   709  				Modules: []*ModuleState{
   710  					&ModuleState{
   711  						Path: rootModulePath,
   712  						Resources: map[string]*ResourceState{
   713  							"test_instance.foo": &ResourceState{
   714  								Type: "test_instance",
   715  								Primary: &InstanceState{
   716  									ID: "foo",
   717  								},
   718  							},
   719  						},
   720  					},
   721  				},
   722  			},
   723  			&State{
   724  				Modules: []*ModuleState{
   725  					&ModuleState{
   726  						Path:      rootModulePath,
   727  						Resources: map[string]*ResourceState{},
   728  					},
   729  				},
   730  			},
   731  		},
   732  
   733  		"single instance in multi-count": {
   734  			"test_instance.foo[0]",
   735  			&State{
   736  				Modules: []*ModuleState{
   737  					&ModuleState{
   738  						Path: rootModulePath,
   739  						Resources: map[string]*ResourceState{
   740  							"test_instance.foo.0": &ResourceState{
   741  								Type: "test_instance",
   742  								Primary: &InstanceState{
   743  									ID: "foo",
   744  								},
   745  							},
   746  
   747  							"test_instance.foo.1": &ResourceState{
   748  								Type: "test_instance",
   749  								Primary: &InstanceState{
   750  									ID: "foo",
   751  								},
   752  							},
   753  						},
   754  					},
   755  				},
   756  			},
   757  			&State{
   758  				Modules: []*ModuleState{
   759  					&ModuleState{
   760  						Path: rootModulePath,
   761  						Resources: map[string]*ResourceState{
   762  							"test_instance.foo.1": &ResourceState{
   763  								Type: "test_instance",
   764  								Primary: &InstanceState{
   765  									ID: "foo",
   766  								},
   767  							},
   768  						},
   769  					},
   770  				},
   771  			},
   772  		},
   773  
   774  		"single resource, multi-count": {
   775  			"test_instance.foo",
   776  			&State{
   777  				Modules: []*ModuleState{
   778  					&ModuleState{
   779  						Path: rootModulePath,
   780  						Resources: map[string]*ResourceState{
   781  							"test_instance.foo.0": &ResourceState{
   782  								Type: "test_instance",
   783  								Primary: &InstanceState{
   784  									ID: "foo",
   785  								},
   786  							},
   787  
   788  							"test_instance.foo.1": &ResourceState{
   789  								Type: "test_instance",
   790  								Primary: &InstanceState{
   791  									ID: "foo",
   792  								},
   793  							},
   794  						},
   795  					},
   796  				},
   797  			},
   798  			&State{
   799  				Modules: []*ModuleState{
   800  					&ModuleState{
   801  						Path:      rootModulePath,
   802  						Resources: map[string]*ResourceState{},
   803  					},
   804  				},
   805  			},
   806  		},
   807  
   808  		"full module": {
   809  			"module.foo",
   810  			&State{
   811  				Modules: []*ModuleState{
   812  					&ModuleState{
   813  						Path: rootModulePath,
   814  						Resources: map[string]*ResourceState{
   815  							"test_instance.foo": &ResourceState{
   816  								Type: "test_instance",
   817  								Primary: &InstanceState{
   818  									ID: "foo",
   819  								},
   820  							},
   821  						},
   822  					},
   823  
   824  					&ModuleState{
   825  						Path: []string{"root", "foo"},
   826  						Resources: map[string]*ResourceState{
   827  							"test_instance.foo": &ResourceState{
   828  								Type: "test_instance",
   829  								Primary: &InstanceState{
   830  									ID: "foo",
   831  								},
   832  							},
   833  
   834  							"test_instance.bar": &ResourceState{
   835  								Type: "test_instance",
   836  								Primary: &InstanceState{
   837  									ID: "foo",
   838  								},
   839  							},
   840  						},
   841  					},
   842  				},
   843  			},
   844  			&State{
   845  				Modules: []*ModuleState{
   846  					&ModuleState{
   847  						Path: rootModulePath,
   848  						Resources: map[string]*ResourceState{
   849  							"test_instance.foo": &ResourceState{
   850  								Type: "test_instance",
   851  								Primary: &InstanceState{
   852  									ID: "foo",
   853  								},
   854  							},
   855  						},
   856  					},
   857  				},
   858  			},
   859  		},
   860  
   861  		"module and children": {
   862  			"module.foo",
   863  			&State{
   864  				Modules: []*ModuleState{
   865  					&ModuleState{
   866  						Path: rootModulePath,
   867  						Resources: map[string]*ResourceState{
   868  							"test_instance.foo": &ResourceState{
   869  								Type: "test_instance",
   870  								Primary: &InstanceState{
   871  									ID: "foo",
   872  								},
   873  							},
   874  						},
   875  					},
   876  
   877  					&ModuleState{
   878  						Path: []string{"root", "foo"},
   879  						Resources: map[string]*ResourceState{
   880  							"test_instance.foo": &ResourceState{
   881  								Type: "test_instance",
   882  								Primary: &InstanceState{
   883  									ID: "foo",
   884  								},
   885  							},
   886  
   887  							"test_instance.bar": &ResourceState{
   888  								Type: "test_instance",
   889  								Primary: &InstanceState{
   890  									ID: "foo",
   891  								},
   892  							},
   893  						},
   894  					},
   895  
   896  					&ModuleState{
   897  						Path: []string{"root", "foo", "bar"},
   898  						Resources: map[string]*ResourceState{
   899  							"test_instance.foo": &ResourceState{
   900  								Type: "test_instance",
   901  								Primary: &InstanceState{
   902  									ID: "foo",
   903  								},
   904  							},
   905  
   906  							"test_instance.bar": &ResourceState{
   907  								Type: "test_instance",
   908  								Primary: &InstanceState{
   909  									ID: "foo",
   910  								},
   911  							},
   912  						},
   913  					},
   914  				},
   915  			},
   916  			&State{
   917  				Modules: []*ModuleState{
   918  					&ModuleState{
   919  						Path: rootModulePath,
   920  						Resources: map[string]*ResourceState{
   921  							"test_instance.foo": &ResourceState{
   922  								Type: "test_instance",
   923  								Primary: &InstanceState{
   924  									ID: "foo",
   925  								},
   926  							},
   927  						},
   928  					},
   929  				},
   930  			},
   931  		},
   932  	}
   933  
   934  	for k, tc := range cases {
   935  		if err := tc.One.Remove(tc.Address); err != nil {
   936  			t.Fatalf("bad: %s\n\n%s", k, err)
   937  		}
   938  
   939  		if !tc.One.Equal(tc.Two) {
   940  			t.Fatalf("Bad: %s\n\n%s\n\n%s", k, tc.One.String(), tc.Two.String())
   941  		}
   942  	}
   943  }
   944  
   945  func TestResourceStateEqual(t *testing.T) {
   946  	cases := []struct {
   947  		Result   bool
   948  		One, Two *ResourceState
   949  	}{
   950  		// Different types
   951  		{
   952  			false,
   953  			&ResourceState{Type: "foo"},
   954  			&ResourceState{Type: "bar"},
   955  		},
   956  
   957  		// Different dependencies
   958  		{
   959  			false,
   960  			&ResourceState{Dependencies: []string{"foo"}},
   961  			&ResourceState{Dependencies: []string{"bar"}},
   962  		},
   963  
   964  		{
   965  			false,
   966  			&ResourceState{Dependencies: []string{"foo", "bar"}},
   967  			&ResourceState{Dependencies: []string{"foo"}},
   968  		},
   969  
   970  		{
   971  			true,
   972  			&ResourceState{Dependencies: []string{"bar", "foo"}},
   973  			&ResourceState{Dependencies: []string{"foo", "bar"}},
   974  		},
   975  
   976  		// Different primaries
   977  		{
   978  			false,
   979  			&ResourceState{Primary: nil},
   980  			&ResourceState{Primary: &InstanceState{ID: "foo"}},
   981  		},
   982  
   983  		{
   984  			true,
   985  			&ResourceState{Primary: &InstanceState{ID: "foo"}},
   986  			&ResourceState{Primary: &InstanceState{ID: "foo"}},
   987  		},
   988  
   989  		// Different tainted
   990  		{
   991  			false,
   992  			&ResourceState{
   993  				Primary: &InstanceState{
   994  					ID: "foo",
   995  				},
   996  			},
   997  			&ResourceState{
   998  				Primary: &InstanceState{
   999  					ID:      "foo",
  1000  					Tainted: true,
  1001  				},
  1002  			},
  1003  		},
  1004  
  1005  		{
  1006  			true,
  1007  			&ResourceState{
  1008  				Primary: &InstanceState{
  1009  					ID:      "foo",
  1010  					Tainted: true,
  1011  				},
  1012  			},
  1013  			&ResourceState{
  1014  				Primary: &InstanceState{
  1015  					ID:      "foo",
  1016  					Tainted: true,
  1017  				},
  1018  			},
  1019  		},
  1020  	}
  1021  
  1022  	for i, tc := range cases {
  1023  		if tc.One.Equal(tc.Two) != tc.Result {
  1024  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
  1025  		}
  1026  		if tc.Two.Equal(tc.One) != tc.Result {
  1027  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
  1028  		}
  1029  	}
  1030  }
  1031  
  1032  func TestResourceStateTaint(t *testing.T) {
  1033  	cases := map[string]struct {
  1034  		Input  *ResourceState
  1035  		Output *ResourceState
  1036  	}{
  1037  		"no primary": {
  1038  			&ResourceState{},
  1039  			&ResourceState{},
  1040  		},
  1041  
  1042  		"primary, not tainted": {
  1043  			&ResourceState{
  1044  				Primary: &InstanceState{ID: "foo"},
  1045  			},
  1046  			&ResourceState{
  1047  				Primary: &InstanceState{
  1048  					ID:      "foo",
  1049  					Tainted: true,
  1050  				},
  1051  			},
  1052  		},
  1053  
  1054  		"primary, tainted": {
  1055  			&ResourceState{
  1056  				Primary: &InstanceState{
  1057  					ID:      "foo",
  1058  					Tainted: true,
  1059  				},
  1060  			},
  1061  			&ResourceState{
  1062  				Primary: &InstanceState{
  1063  					ID:      "foo",
  1064  					Tainted: true,
  1065  				},
  1066  			},
  1067  		},
  1068  	}
  1069  
  1070  	for k, tc := range cases {
  1071  		tc.Input.Taint()
  1072  		if !reflect.DeepEqual(tc.Input, tc.Output) {
  1073  			t.Fatalf(
  1074  				"Failure: %s\n\nExpected: %#v\n\nGot: %#v",
  1075  				k, tc.Output, tc.Input)
  1076  		}
  1077  	}
  1078  }
  1079  
  1080  func TestResourceStateUntaint(t *testing.T) {
  1081  	cases := map[string]struct {
  1082  		Input          *ResourceState
  1083  		ExpectedOutput *ResourceState
  1084  	}{
  1085  		"no primary, err": {
  1086  			Input:          &ResourceState{},
  1087  			ExpectedOutput: &ResourceState{},
  1088  		},
  1089  
  1090  		"primary, not tainted": {
  1091  			Input: &ResourceState{
  1092  				Primary: &InstanceState{ID: "foo"},
  1093  			},
  1094  			ExpectedOutput: &ResourceState{
  1095  				Primary: &InstanceState{ID: "foo"},
  1096  			},
  1097  		},
  1098  		"primary, tainted": {
  1099  			Input: &ResourceState{
  1100  				Primary: &InstanceState{
  1101  					ID:      "foo",
  1102  					Tainted: true,
  1103  				},
  1104  			},
  1105  			ExpectedOutput: &ResourceState{
  1106  				Primary: &InstanceState{ID: "foo"},
  1107  			},
  1108  		},
  1109  	}
  1110  
  1111  	for k, tc := range cases {
  1112  		tc.Input.Untaint()
  1113  		if !reflect.DeepEqual(tc.Input, tc.ExpectedOutput) {
  1114  			t.Fatalf(
  1115  				"Failure: %s\n\nExpected: %#v\n\nGot: %#v",
  1116  				k, tc.ExpectedOutput, tc.Input)
  1117  		}
  1118  	}
  1119  }
  1120  
  1121  func TestInstanceStateEmpty(t *testing.T) {
  1122  	cases := map[string]struct {
  1123  		In     *InstanceState
  1124  		Result bool
  1125  	}{
  1126  		"nil is empty": {
  1127  			nil,
  1128  			true,
  1129  		},
  1130  		"non-nil but without ID is empty": {
  1131  			&InstanceState{},
  1132  			true,
  1133  		},
  1134  		"with ID is not empty": {
  1135  			&InstanceState{
  1136  				ID: "i-abc123",
  1137  			},
  1138  			false,
  1139  		},
  1140  	}
  1141  
  1142  	for tn, tc := range cases {
  1143  		if tc.In.Empty() != tc.Result {
  1144  			t.Fatalf("%q expected %#v to be empty: %#v", tn, tc.In, tc.Result)
  1145  		}
  1146  	}
  1147  }
  1148  
  1149  func TestInstanceStateEqual(t *testing.T) {
  1150  	cases := []struct {
  1151  		Result   bool
  1152  		One, Two *InstanceState
  1153  	}{
  1154  		// Nils
  1155  		{
  1156  			false,
  1157  			nil,
  1158  			&InstanceState{},
  1159  		},
  1160  
  1161  		{
  1162  			false,
  1163  			&InstanceState{},
  1164  			nil,
  1165  		},
  1166  
  1167  		// Different IDs
  1168  		{
  1169  			false,
  1170  			&InstanceState{ID: "foo"},
  1171  			&InstanceState{ID: "bar"},
  1172  		},
  1173  
  1174  		// Different Attributes
  1175  		{
  1176  			false,
  1177  			&InstanceState{Attributes: map[string]string{"foo": "bar"}},
  1178  			&InstanceState{Attributes: map[string]string{"foo": "baz"}},
  1179  		},
  1180  
  1181  		// Different Attribute keys
  1182  		{
  1183  			false,
  1184  			&InstanceState{Attributes: map[string]string{"foo": "bar"}},
  1185  			&InstanceState{Attributes: map[string]string{"bar": "baz"}},
  1186  		},
  1187  
  1188  		{
  1189  			false,
  1190  			&InstanceState{Attributes: map[string]string{"bar": "baz"}},
  1191  			&InstanceState{Attributes: map[string]string{"foo": "bar"}},
  1192  		},
  1193  	}
  1194  
  1195  	for i, tc := range cases {
  1196  		if tc.One.Equal(tc.Two) != tc.Result {
  1197  			t.Fatalf("Bad: %d\n\n%s\n\n%s", i, tc.One.String(), tc.Two.String())
  1198  		}
  1199  	}
  1200  }
  1201  
  1202  func TestStateEmpty(t *testing.T) {
  1203  	cases := []struct {
  1204  		In     *State
  1205  		Result bool
  1206  	}{
  1207  		{
  1208  			nil,
  1209  			true,
  1210  		},
  1211  		{
  1212  			&State{},
  1213  			true,
  1214  		},
  1215  		{
  1216  			&State{
  1217  				Remote: &RemoteState{Type: "foo"},
  1218  			},
  1219  			true,
  1220  		},
  1221  		{
  1222  			&State{
  1223  				Modules: []*ModuleState{
  1224  					&ModuleState{},
  1225  				},
  1226  			},
  1227  			false,
  1228  		},
  1229  	}
  1230  
  1231  	for i, tc := range cases {
  1232  		if tc.In.Empty() != tc.Result {
  1233  			t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In)
  1234  		}
  1235  	}
  1236  }
  1237  
  1238  func TestStateHasResources(t *testing.T) {
  1239  	cases := []struct {
  1240  		In     *State
  1241  		Result bool
  1242  	}{
  1243  		{
  1244  			nil,
  1245  			false,
  1246  		},
  1247  		{
  1248  			&State{},
  1249  			false,
  1250  		},
  1251  		{
  1252  			&State{
  1253  				Remote: &RemoteState{Type: "foo"},
  1254  			},
  1255  			false,
  1256  		},
  1257  		{
  1258  			&State{
  1259  				Modules: []*ModuleState{
  1260  					&ModuleState{},
  1261  				},
  1262  			},
  1263  			false,
  1264  		},
  1265  		{
  1266  			&State{
  1267  				Modules: []*ModuleState{
  1268  					&ModuleState{},
  1269  					&ModuleState{},
  1270  				},
  1271  			},
  1272  			false,
  1273  		},
  1274  		{
  1275  			&State{
  1276  				Modules: []*ModuleState{
  1277  					&ModuleState{},
  1278  					&ModuleState{
  1279  						Resources: map[string]*ResourceState{
  1280  							"foo.foo": &ResourceState{},
  1281  						},
  1282  					},
  1283  				},
  1284  			},
  1285  			true,
  1286  		},
  1287  	}
  1288  
  1289  	for i, tc := range cases {
  1290  		if tc.In.HasResources() != tc.Result {
  1291  			t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In)
  1292  		}
  1293  	}
  1294  }
  1295  
  1296  func TestStateFromFutureTerraform(t *testing.T) {
  1297  	cases := []struct {
  1298  		In     string
  1299  		Result bool
  1300  	}{
  1301  		{
  1302  			"",
  1303  			false,
  1304  		},
  1305  		{
  1306  			"0.1",
  1307  			false,
  1308  		},
  1309  		{
  1310  			"999.15.1",
  1311  			true,
  1312  		},
  1313  	}
  1314  
  1315  	for _, tc := range cases {
  1316  		state := &State{TFVersion: tc.In}
  1317  		actual := state.FromFutureTerraform()
  1318  		if actual != tc.Result {
  1319  			t.Fatalf("%s: bad: %v", tc.In, actual)
  1320  		}
  1321  	}
  1322  }
  1323  
  1324  func TestStateIsRemote(t *testing.T) {
  1325  	cases := []struct {
  1326  		In     *State
  1327  		Result bool
  1328  	}{
  1329  		{
  1330  			nil,
  1331  			false,
  1332  		},
  1333  		{
  1334  			&State{},
  1335  			false,
  1336  		},
  1337  		{
  1338  			&State{
  1339  				Remote: &RemoteState{Type: "foo"},
  1340  			},
  1341  			true,
  1342  		},
  1343  	}
  1344  
  1345  	for i, tc := range cases {
  1346  		if tc.In.IsRemote() != tc.Result {
  1347  			t.Fatalf("bad %d %#v:\n\n%#v", i, tc.Result, tc.In)
  1348  		}
  1349  	}
  1350  }
  1351  
  1352  func TestInstanceState_MergeDiff(t *testing.T) {
  1353  	is := InstanceState{
  1354  		ID: "foo",
  1355  		Attributes: map[string]string{
  1356  			"foo":  "bar",
  1357  			"port": "8000",
  1358  		},
  1359  	}
  1360  
  1361  	diff := &InstanceDiff{
  1362  		Attributes: map[string]*ResourceAttrDiff{
  1363  			"foo": &ResourceAttrDiff{
  1364  				Old: "bar",
  1365  				New: "baz",
  1366  			},
  1367  			"bar": &ResourceAttrDiff{
  1368  				Old: "",
  1369  				New: "foo",
  1370  			},
  1371  			"baz": &ResourceAttrDiff{
  1372  				Old:         "",
  1373  				New:         "foo",
  1374  				NewComputed: true,
  1375  			},
  1376  			"port": &ResourceAttrDiff{
  1377  				NewRemoved: true,
  1378  			},
  1379  		},
  1380  	}
  1381  
  1382  	is2 := is.MergeDiff(diff)
  1383  
  1384  	expected := map[string]string{
  1385  		"foo": "baz",
  1386  		"bar": "foo",
  1387  		"baz": config.UnknownVariableValue,
  1388  	}
  1389  
  1390  	if !reflect.DeepEqual(expected, is2.Attributes) {
  1391  		t.Fatalf("bad: %#v", is2.Attributes)
  1392  	}
  1393  }
  1394  
  1395  func TestInstanceState_MergeDiff_nil(t *testing.T) {
  1396  	var is *InstanceState
  1397  
  1398  	diff := &InstanceDiff{
  1399  		Attributes: map[string]*ResourceAttrDiff{
  1400  			"foo": &ResourceAttrDiff{
  1401  				Old: "",
  1402  				New: "baz",
  1403  			},
  1404  		},
  1405  	}
  1406  
  1407  	is2 := is.MergeDiff(diff)
  1408  
  1409  	expected := map[string]string{
  1410  		"foo": "baz",
  1411  	}
  1412  
  1413  	if !reflect.DeepEqual(expected, is2.Attributes) {
  1414  		t.Fatalf("bad: %#v", is2.Attributes)
  1415  	}
  1416  }
  1417  
  1418  func TestInstanceState_MergeDiff_nilDiff(t *testing.T) {
  1419  	is := InstanceState{
  1420  		ID: "foo",
  1421  		Attributes: map[string]string{
  1422  			"foo": "bar",
  1423  		},
  1424  	}
  1425  
  1426  	is2 := is.MergeDiff(nil)
  1427  
  1428  	expected := map[string]string{
  1429  		"foo": "bar",
  1430  	}
  1431  
  1432  	if !reflect.DeepEqual(expected, is2.Attributes) {
  1433  		t.Fatalf("bad: %#v", is2.Attributes)
  1434  	}
  1435  }
  1436  
  1437  func TestReadWriteState(t *testing.T) {
  1438  	state := &State{
  1439  		Serial:  9,
  1440  		Lineage: "5d1ad1a1-4027-4665-a908-dbe6adff11d8",
  1441  		Remote: &RemoteState{
  1442  			Type: "http",
  1443  			Config: map[string]string{
  1444  				"url": "http://my-cool-server.com/",
  1445  			},
  1446  		},
  1447  		Modules: []*ModuleState{
  1448  			&ModuleState{
  1449  				Path: rootModulePath,
  1450  				Dependencies: []string{
  1451  					"aws_instance.bar",
  1452  				},
  1453  				Resources: map[string]*ResourceState{
  1454  					"foo": &ResourceState{
  1455  						Primary: &InstanceState{
  1456  							ID: "bar",
  1457  							Ephemeral: EphemeralState{
  1458  								ConnInfo: map[string]string{
  1459  									"type":     "ssh",
  1460  									"user":     "root",
  1461  									"password": "supersecret",
  1462  								},
  1463  							},
  1464  						},
  1465  					},
  1466  				},
  1467  			},
  1468  		},
  1469  	}
  1470  	state.init()
  1471  
  1472  	buf := new(bytes.Buffer)
  1473  	if err := WriteState(state, buf); err != nil {
  1474  		t.Fatalf("err: %s", err)
  1475  	}
  1476  
  1477  	// Verify that the version and serial are set
  1478  	if state.Version != StateVersion {
  1479  		t.Fatalf("bad version number: %d", state.Version)
  1480  	}
  1481  
  1482  	actual, err := ReadState(buf)
  1483  	if err != nil {
  1484  		t.Fatalf("err: %s", err)
  1485  	}
  1486  
  1487  	// ReadState should not restore sensitive information!
  1488  	mod := state.RootModule()
  1489  	mod.Resources["foo"].Primary.Ephemeral = EphemeralState{}
  1490  	mod.Resources["foo"].Primary.Ephemeral.init()
  1491  
  1492  	if !reflect.DeepEqual(actual, state) {
  1493  		t.Logf("expected:\n%#v", state)
  1494  		t.Fatalf("got:\n%#v", actual)
  1495  	}
  1496  }
  1497  
  1498  func TestReadStateNewVersion(t *testing.T) {
  1499  	type out struct {
  1500  		Version int
  1501  	}
  1502  
  1503  	buf, err := json.Marshal(&out{StateVersion + 1})
  1504  	if err != nil {
  1505  		t.Fatalf("err: %v", err)
  1506  	}
  1507  
  1508  	s, err := ReadState(bytes.NewReader(buf))
  1509  	if s != nil {
  1510  		t.Fatalf("unexpected: %#v", s)
  1511  	}
  1512  	if !strings.Contains(err.Error(), "does not support state version") {
  1513  		t.Fatalf("err: %v", err)
  1514  	}
  1515  }
  1516  
  1517  func TestReadStateTFVersion(t *testing.T) {
  1518  	type tfVersion struct {
  1519  		Version   int    `json:"version"`
  1520  		TFVersion string `json:"terraform_version"`
  1521  	}
  1522  
  1523  	cases := []struct {
  1524  		Written string
  1525  		Read    string
  1526  		Err     bool
  1527  	}{
  1528  		{
  1529  			"0.0.0",
  1530  			"0.0.0",
  1531  			false,
  1532  		},
  1533  		{
  1534  			"",
  1535  			"",
  1536  			false,
  1537  		},
  1538  		{
  1539  			"bad",
  1540  			"",
  1541  			true,
  1542  		},
  1543  	}
  1544  
  1545  	for _, tc := range cases {
  1546  		buf, err := json.Marshal(&tfVersion{
  1547  			Version:   2,
  1548  			TFVersion: tc.Written,
  1549  		})
  1550  		if err != nil {
  1551  			t.Fatalf("err: %v", err)
  1552  		}
  1553  
  1554  		s, err := ReadState(bytes.NewReader(buf))
  1555  		if (err != nil) != tc.Err {
  1556  			t.Fatalf("%s: err: %s", tc.Written, err)
  1557  		}
  1558  		if err != nil {
  1559  			continue
  1560  		}
  1561  
  1562  		if s.TFVersion != tc.Read {
  1563  			t.Fatalf("%s: bad: %s", tc.Written, s.TFVersion)
  1564  		}
  1565  	}
  1566  }
  1567  
  1568  func TestWriteStateTFVersion(t *testing.T) {
  1569  	cases := []struct {
  1570  		Write string
  1571  		Read  string
  1572  		Err   bool
  1573  	}{
  1574  		{
  1575  			"0.0.0",
  1576  			"0.0.0",
  1577  			false,
  1578  		},
  1579  		{
  1580  			"",
  1581  			"",
  1582  			false,
  1583  		},
  1584  		{
  1585  			"bad",
  1586  			"",
  1587  			true,
  1588  		},
  1589  	}
  1590  
  1591  	for _, tc := range cases {
  1592  		var buf bytes.Buffer
  1593  		err := WriteState(&State{TFVersion: tc.Write}, &buf)
  1594  		if (err != nil) != tc.Err {
  1595  			t.Fatalf("%s: err: %s", tc.Write, err)
  1596  		}
  1597  		if err != nil {
  1598  			continue
  1599  		}
  1600  
  1601  		s, err := ReadState(&buf)
  1602  		if err != nil {
  1603  			t.Fatalf("%s: err: %s", tc.Write, err)
  1604  		}
  1605  
  1606  		if s.TFVersion != tc.Read {
  1607  			t.Fatalf("%s: bad: %s", tc.Write, s.TFVersion)
  1608  		}
  1609  	}
  1610  }
  1611  
  1612  func TestParseResourceStateKey(t *testing.T) {
  1613  	cases := []struct {
  1614  		Input       string
  1615  		Expected    *ResourceStateKey
  1616  		ExpectedErr bool
  1617  	}{
  1618  		{
  1619  			Input: "aws_instance.foo.3",
  1620  			Expected: &ResourceStateKey{
  1621  				Mode:  config.ManagedResourceMode,
  1622  				Type:  "aws_instance",
  1623  				Name:  "foo",
  1624  				Index: 3,
  1625  			},
  1626  		},
  1627  		{
  1628  			Input: "aws_instance.foo.0",
  1629  			Expected: &ResourceStateKey{
  1630  				Mode:  config.ManagedResourceMode,
  1631  				Type:  "aws_instance",
  1632  				Name:  "foo",
  1633  				Index: 0,
  1634  			},
  1635  		},
  1636  		{
  1637  			Input: "aws_instance.foo",
  1638  			Expected: &ResourceStateKey{
  1639  				Mode:  config.ManagedResourceMode,
  1640  				Type:  "aws_instance",
  1641  				Name:  "foo",
  1642  				Index: -1,
  1643  			},
  1644  		},
  1645  		{
  1646  			Input: "data.aws_ami.foo",
  1647  			Expected: &ResourceStateKey{
  1648  				Mode:  config.DataResourceMode,
  1649  				Type:  "aws_ami",
  1650  				Name:  "foo",
  1651  				Index: -1,
  1652  			},
  1653  		},
  1654  		{
  1655  			Input:       "aws_instance.foo.malformed",
  1656  			ExpectedErr: true,
  1657  		},
  1658  		{
  1659  			Input:       "aws_instance.foo.malformedwithnumber.123",
  1660  			ExpectedErr: true,
  1661  		},
  1662  		{
  1663  			Input:       "malformed",
  1664  			ExpectedErr: true,
  1665  		},
  1666  	}
  1667  	for _, tc := range cases {
  1668  		rsk, err := ParseResourceStateKey(tc.Input)
  1669  		if rsk != nil && tc.Expected != nil && !rsk.Equal(tc.Expected) {
  1670  			t.Fatalf("%s: expected %s, got %s", tc.Input, tc.Expected, rsk)
  1671  		}
  1672  		if (err != nil) != tc.ExpectedErr {
  1673  			t.Fatalf("%s: expected err: %t, got %s", tc.Input, tc.ExpectedErr, err)
  1674  		}
  1675  	}
  1676  }
  1677  
  1678  func TestStateModuleOrphans_empty(t *testing.T) {
  1679  	state := &State{
  1680  		Modules: []*ModuleState{
  1681  			&ModuleState{
  1682  				Path: RootModulePath,
  1683  			},
  1684  			&ModuleState{
  1685  				Path: []string{RootModuleName, "foo", "bar"},
  1686  			},
  1687  			&ModuleState{
  1688  				Path: []string{},
  1689  			},
  1690  			nil,
  1691  		},
  1692  	}
  1693  
  1694  	state.init()
  1695  
  1696  	// just calling this to check for panic
  1697  	state.ModuleOrphans(RootModulePath, nil)
  1698  }
  1699  
  1700  func TestReadState_prune(t *testing.T) {
  1701  	state := &State{
  1702  		Modules: []*ModuleState{
  1703  			&ModuleState{Path: rootModulePath},
  1704  			nil,
  1705  		},
  1706  	}
  1707  	state.init()
  1708  
  1709  	buf := new(bytes.Buffer)
  1710  	if err := WriteState(state, buf); err != nil {
  1711  		t.Fatalf("err: %s", err)
  1712  	}
  1713  
  1714  	actual, err := ReadState(buf)
  1715  	if err != nil {
  1716  		t.Fatalf("err: %s", err)
  1717  	}
  1718  
  1719  	expected := &State{
  1720  		Version: state.Version,
  1721  		Lineage: state.Lineage,
  1722  	}
  1723  	expected.init()
  1724  
  1725  	if !reflect.DeepEqual(actual, expected) {
  1726  		t.Fatalf("got:\n%#v", actual)
  1727  	}
  1728  }