github.com/kanishk98/terraform@v1.3.0-dev.0.20220917174235-661ca8088a6a/internal/command/apply_destroy_test.go (about)

     1  package command
     2  
     3  import (
     4  	"os"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/davecgh/go-spew/spew"
     9  	"github.com/mitchellh/cli"
    10  	"github.com/zclconf/go-cty/cty"
    11  
    12  	"github.com/hashicorp/terraform/internal/addrs"
    13  	"github.com/hashicorp/terraform/internal/configs/configschema"
    14  	"github.com/hashicorp/terraform/internal/providers"
    15  	"github.com/hashicorp/terraform/internal/states"
    16  	"github.com/hashicorp/terraform/internal/states/statefile"
    17  )
    18  
    19  func TestApply_destroy(t *testing.T) {
    20  	// Create a temporary working directory that is empty
    21  	td := t.TempDir()
    22  	testCopyDir(t, testFixturePath("apply"), td)
    23  	defer testChdir(t, td)()
    24  
    25  	originalState := states.BuildState(func(s *states.SyncState) {
    26  		s.SetResourceInstanceCurrent(
    27  			addrs.Resource{
    28  				Mode: addrs.ManagedResourceMode,
    29  				Type: "test_instance",
    30  				Name: "foo",
    31  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
    32  			&states.ResourceInstanceObjectSrc{
    33  				AttrsJSON: []byte(`{"id":"bar"}`),
    34  				Status:    states.ObjectReady,
    35  			},
    36  			addrs.AbsProviderConfig{
    37  				Provider: addrs.NewDefaultProvider("test"),
    38  				Module:   addrs.RootModule,
    39  			},
    40  		)
    41  	})
    42  	statePath := testStateFile(t, originalState)
    43  
    44  	p := testProvider()
    45  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
    46  		ResourceTypes: map[string]providers.Schema{
    47  			"test_instance": {
    48  				Block: &configschema.Block{
    49  					Attributes: map[string]*configschema.Attribute{
    50  						"id":  {Type: cty.String, Computed: true},
    51  						"ami": {Type: cty.String, Optional: true},
    52  					},
    53  				},
    54  			},
    55  		},
    56  	}
    57  
    58  	view, done := testView(t)
    59  	c := &ApplyCommand{
    60  		Destroy: true,
    61  		Meta: Meta{
    62  			testingOverrides: metaOverridesForProvider(p),
    63  			View:             view,
    64  		},
    65  	}
    66  
    67  	// Run the apply command pointing to our existing state
    68  	args := []string{
    69  		"-auto-approve",
    70  		"-state", statePath,
    71  	}
    72  	code := c.Run(args)
    73  	output := done(t)
    74  	if code != 0 {
    75  		t.Log(output.Stdout())
    76  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
    77  	}
    78  
    79  	// Verify a new state exists
    80  	if _, err := os.Stat(statePath); err != nil {
    81  		t.Fatalf("err: %s", err)
    82  	}
    83  
    84  	f, err := os.Open(statePath)
    85  	if err != nil {
    86  		t.Fatalf("err: %s", err)
    87  	}
    88  	defer f.Close()
    89  
    90  	stateFile, err := statefile.Read(f)
    91  	if err != nil {
    92  		t.Fatalf("err: %s", err)
    93  	}
    94  	if stateFile.State == nil {
    95  		t.Fatal("state should not be nil")
    96  	}
    97  
    98  	actualStr := strings.TrimSpace(stateFile.State.String())
    99  	expectedStr := strings.TrimSpace(testApplyDestroyStr)
   100  	if actualStr != expectedStr {
   101  		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
   102  	}
   103  
   104  	// Should have a backup file
   105  	f, err = os.Open(statePath + DefaultBackupExtension)
   106  	if err != nil {
   107  		t.Fatalf("err: %s", err)
   108  	}
   109  
   110  	backupStateFile, err := statefile.Read(f)
   111  	f.Close()
   112  	if err != nil {
   113  		t.Fatalf("err: %s", err)
   114  	}
   115  
   116  	actualStr = strings.TrimSpace(backupStateFile.State.String())
   117  	expectedStr = strings.TrimSpace(originalState.String())
   118  	if actualStr != expectedStr {
   119  		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
   120  	}
   121  }
   122  
   123  func TestApply_destroyApproveNo(t *testing.T) {
   124  	// Create a temporary working directory that is empty
   125  	td := t.TempDir()
   126  	testCopyDir(t, testFixturePath("apply"), td)
   127  	defer testChdir(t, td)()
   128  
   129  	// Create some existing state
   130  	originalState := states.BuildState(func(s *states.SyncState) {
   131  		s.SetResourceInstanceCurrent(
   132  			addrs.Resource{
   133  				Mode: addrs.ManagedResourceMode,
   134  				Type: "test_instance",
   135  				Name: "foo",
   136  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   137  			&states.ResourceInstanceObjectSrc{
   138  				AttrsJSON: []byte(`{"id":"bar"}`),
   139  				Status:    states.ObjectReady,
   140  			},
   141  			addrs.AbsProviderConfig{
   142  				Provider: addrs.NewDefaultProvider("test"),
   143  				Module:   addrs.RootModule,
   144  			},
   145  		)
   146  	})
   147  	statePath := testStateFile(t, originalState)
   148  
   149  	p := applyFixtureProvider()
   150  
   151  	defer testInputMap(t, map[string]string{
   152  		"approve": "no",
   153  	})()
   154  
   155  	// Do not use the NewMockUi initializer here, as we want to delay
   156  	// the call to init until after setting up the input mocks
   157  	ui := new(cli.MockUi)
   158  	view, done := testView(t)
   159  	c := &ApplyCommand{
   160  		Destroy: true,
   161  		Meta: Meta{
   162  			testingOverrides: metaOverridesForProvider(p),
   163  			Ui:               ui,
   164  			View:             view,
   165  		},
   166  	}
   167  
   168  	args := []string{
   169  		"-state", statePath,
   170  	}
   171  	code := c.Run(args)
   172  	output := done(t)
   173  	if code != 1 {
   174  		t.Fatalf("bad: %d\n\n%s", code, output.Stdout())
   175  	}
   176  	if got, want := output.Stdout(), "Destroy cancelled"; !strings.Contains(got, want) {
   177  		t.Fatalf("expected output to include %q, but was:\n%s", want, got)
   178  	}
   179  
   180  	state := testStateRead(t, statePath)
   181  	if state == nil {
   182  		t.Fatal("state should not be nil")
   183  	}
   184  	actualStr := strings.TrimSpace(state.String())
   185  	expectedStr := strings.TrimSpace(originalState.String())
   186  	if actualStr != expectedStr {
   187  		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
   188  	}
   189  }
   190  
   191  func TestApply_destroyApproveYes(t *testing.T) {
   192  	// Create a temporary working directory that is empty
   193  	td := t.TempDir()
   194  	testCopyDir(t, testFixturePath("apply"), td)
   195  	defer testChdir(t, td)()
   196  
   197  	// Create some existing state
   198  	originalState := states.BuildState(func(s *states.SyncState) {
   199  		s.SetResourceInstanceCurrent(
   200  			addrs.Resource{
   201  				Mode: addrs.ManagedResourceMode,
   202  				Type: "test_instance",
   203  				Name: "foo",
   204  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   205  			&states.ResourceInstanceObjectSrc{
   206  				AttrsJSON: []byte(`{"id":"bar"}`),
   207  				Status:    states.ObjectReady,
   208  			},
   209  			addrs.AbsProviderConfig{
   210  				Provider: addrs.NewDefaultProvider("test"),
   211  				Module:   addrs.RootModule,
   212  			},
   213  		)
   214  	})
   215  	statePath := testStateFile(t, originalState)
   216  
   217  	p := applyFixtureProvider()
   218  
   219  	defer testInputMap(t, map[string]string{
   220  		"approve": "yes",
   221  	})()
   222  
   223  	// Do not use the NewMockUi initializer here, as we want to delay
   224  	// the call to init until after setting up the input mocks
   225  	ui := new(cli.MockUi)
   226  	view, done := testView(t)
   227  	c := &ApplyCommand{
   228  		Destroy: true,
   229  		Meta: Meta{
   230  			testingOverrides: metaOverridesForProvider(p),
   231  			Ui:               ui,
   232  			View:             view,
   233  		},
   234  	}
   235  
   236  	args := []string{
   237  		"-state", statePath,
   238  	}
   239  	code := c.Run(args)
   240  	output := done(t)
   241  	if code != 0 {
   242  		t.Log(output.Stdout())
   243  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   244  	}
   245  
   246  	if _, err := os.Stat(statePath); err != nil {
   247  		t.Fatalf("err: %s", err)
   248  	}
   249  
   250  	state := testStateRead(t, statePath)
   251  	if state == nil {
   252  		t.Fatal("state should not be nil")
   253  	}
   254  
   255  	actualStr := strings.TrimSpace(state.String())
   256  	expectedStr := strings.TrimSpace(testApplyDestroyStr)
   257  	if actualStr != expectedStr {
   258  		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
   259  	}
   260  }
   261  
   262  func TestApply_destroyLockedState(t *testing.T) {
   263  	// Create a temporary working directory that is empty
   264  	td := t.TempDir()
   265  	testCopyDir(t, testFixturePath("apply"), td)
   266  	defer testChdir(t, td)()
   267  
   268  	originalState := states.BuildState(func(s *states.SyncState) {
   269  		s.SetResourceInstanceCurrent(
   270  			addrs.Resource{
   271  				Mode: addrs.ManagedResourceMode,
   272  				Type: "test_instance",
   273  				Name: "foo",
   274  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   275  			&states.ResourceInstanceObjectSrc{
   276  				AttrsJSON: []byte(`{"id":"bar"}`),
   277  				Status:    states.ObjectReady,
   278  			},
   279  			addrs.AbsProviderConfig{
   280  				Provider: addrs.NewDefaultProvider("test"),
   281  				Module:   addrs.RootModule,
   282  			},
   283  		)
   284  	})
   285  	statePath := testStateFile(t, originalState)
   286  
   287  	unlock, err := testLockState(t, testDataDir, statePath)
   288  	if err != nil {
   289  		t.Fatal(err)
   290  	}
   291  	defer unlock()
   292  
   293  	p := testProvider()
   294  	view, done := testView(t)
   295  	c := &ApplyCommand{
   296  		Destroy: true,
   297  		Meta: Meta{
   298  			testingOverrides: metaOverridesForProvider(p),
   299  			View:             view,
   300  		},
   301  	}
   302  
   303  	// Run the apply command pointing to our existing state
   304  	args := []string{
   305  		"-auto-approve",
   306  		"-state", statePath,
   307  	}
   308  
   309  	code := c.Run(args)
   310  	output := done(t)
   311  	if code == 0 {
   312  		t.Fatalf("bad: %d\n\n%s", code, output.Stdout())
   313  	}
   314  
   315  	if !strings.Contains(output.Stderr(), "lock") {
   316  		t.Fatal("command output does not look like a lock error:", output.Stderr())
   317  	}
   318  }
   319  
   320  func TestApply_destroyPlan(t *testing.T) {
   321  	// Create a temporary working directory that is empty
   322  	td := t.TempDir()
   323  	testCopyDir(t, testFixturePath("apply"), td)
   324  	defer testChdir(t, td)()
   325  
   326  	planPath := testPlanFileNoop(t)
   327  
   328  	p := testProvider()
   329  	view, done := testView(t)
   330  	c := &ApplyCommand{
   331  		Destroy: true,
   332  		Meta: Meta{
   333  			testingOverrides: metaOverridesForProvider(p),
   334  			View:             view,
   335  		},
   336  	}
   337  
   338  	// Run the apply command pointing to our existing state
   339  	args := []string{
   340  		planPath,
   341  	}
   342  	code := c.Run(args)
   343  	output := done(t)
   344  	if code != 1 {
   345  		t.Fatalf("bad: %d\n\n%s", code, output.Stdout())
   346  	}
   347  	if !strings.Contains(output.Stderr(), "plan file") {
   348  		t.Fatal("expected command output to refer to plan file, but got:", output.Stderr())
   349  	}
   350  }
   351  
   352  func TestApply_destroyPath(t *testing.T) {
   353  	// Create a temporary working directory that is empty
   354  	td := t.TempDir()
   355  	testCopyDir(t, testFixturePath("apply"), td)
   356  	defer testChdir(t, td)()
   357  
   358  	p := applyFixtureProvider()
   359  
   360  	view, done := testView(t)
   361  	c := &ApplyCommand{
   362  		Destroy: true,
   363  		Meta: Meta{
   364  			testingOverrides: metaOverridesForProvider(p),
   365  			View:             view,
   366  		},
   367  	}
   368  
   369  	args := []string{
   370  		"-auto-approve",
   371  		testFixturePath("apply"),
   372  	}
   373  	code := c.Run(args)
   374  	output := done(t)
   375  	if code != 1 {
   376  		t.Fatalf("bad: %d\n\n%s", code, output.Stdout())
   377  	}
   378  	if !strings.Contains(output.Stderr(), "-chdir") {
   379  		t.Fatal("expected command output to refer to -chdir flag, but got:", output.Stderr())
   380  	}
   381  }
   382  
   383  // Config with multiple resources with dependencies, targeting destroy of a
   384  // root node, expecting all other resources to be destroyed due to
   385  // dependencies.
   386  func TestApply_destroyTargetedDependencies(t *testing.T) {
   387  	// Create a temporary working directory that is empty
   388  	td := t.TempDir()
   389  	testCopyDir(t, testFixturePath("apply-destroy-targeted"), td)
   390  	defer testChdir(t, td)()
   391  
   392  	originalState := states.BuildState(func(s *states.SyncState) {
   393  		s.SetResourceInstanceCurrent(
   394  			addrs.Resource{
   395  				Mode: addrs.ManagedResourceMode,
   396  				Type: "test_instance",
   397  				Name: "foo",
   398  			}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   399  			&states.ResourceInstanceObjectSrc{
   400  				AttrsJSON: []byte(`{"id":"i-ab123"}`),
   401  				Status:    states.ObjectReady,
   402  			},
   403  			addrs.AbsProviderConfig{
   404  				Provider: addrs.NewDefaultProvider("test"),
   405  				Module:   addrs.RootModule,
   406  			},
   407  		)
   408  		s.SetResourceInstanceCurrent(
   409  			addrs.Resource{
   410  				Mode: addrs.ManagedResourceMode,
   411  				Type: "test_load_balancer",
   412  				Name: "foo",
   413  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   414  			&states.ResourceInstanceObjectSrc{
   415  				AttrsJSON:    []byte(`{"id":"i-abc123"}`),
   416  				Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")},
   417  				Status:       states.ObjectReady,
   418  			},
   419  			addrs.AbsProviderConfig{
   420  				Provider: addrs.NewDefaultProvider("test"),
   421  				Module:   addrs.RootModule,
   422  			},
   423  		)
   424  	})
   425  	statePath := testStateFile(t, originalState)
   426  
   427  	p := testProvider()
   428  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
   429  		ResourceTypes: map[string]providers.Schema{
   430  			"test_instance": {
   431  				Block: &configschema.Block{
   432  					Attributes: map[string]*configschema.Attribute{
   433  						"id": {Type: cty.String, Computed: true},
   434  					},
   435  				},
   436  			},
   437  			"test_load_balancer": {
   438  				Block: &configschema.Block{
   439  					Attributes: map[string]*configschema.Attribute{
   440  						"id":        {Type: cty.String, Computed: true},
   441  						"instances": {Type: cty.List(cty.String), Optional: true},
   442  					},
   443  				},
   444  			},
   445  		},
   446  	}
   447  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
   448  		return providers.PlanResourceChangeResponse{
   449  			PlannedState: req.ProposedNewState,
   450  		}
   451  	}
   452  
   453  	view, done := testView(t)
   454  	c := &ApplyCommand{
   455  		Destroy: true,
   456  		Meta: Meta{
   457  			testingOverrides: metaOverridesForProvider(p),
   458  			View:             view,
   459  		},
   460  	}
   461  
   462  	// Run the apply command pointing to our existing state
   463  	args := []string{
   464  		"-auto-approve",
   465  		"-target", "test_instance.foo",
   466  		"-state", statePath,
   467  	}
   468  	code := c.Run(args)
   469  	output := done(t)
   470  	if code != 0 {
   471  		t.Log(output.Stdout())
   472  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   473  	}
   474  
   475  	// Verify a new state exists
   476  	if _, err := os.Stat(statePath); err != nil {
   477  		t.Fatalf("err: %s", err)
   478  	}
   479  
   480  	f, err := os.Open(statePath)
   481  	if err != nil {
   482  		t.Fatalf("err: %s", err)
   483  	}
   484  	defer f.Close()
   485  
   486  	stateFile, err := statefile.Read(f)
   487  	if err != nil {
   488  		t.Fatalf("err: %s", err)
   489  	}
   490  	if stateFile == nil || stateFile.State == nil {
   491  		t.Fatal("state should not be nil")
   492  	}
   493  
   494  	spew.Config.DisableMethods = true
   495  	if !stateFile.State.Empty() {
   496  		t.Fatalf("unexpected final state\ngot: %s\nwant: empty state", spew.Sdump(stateFile.State))
   497  	}
   498  
   499  	// Should have a backup file
   500  	f, err = os.Open(statePath + DefaultBackupExtension)
   501  	if err != nil {
   502  		t.Fatalf("err: %s", err)
   503  	}
   504  
   505  	backupStateFile, err := statefile.Read(f)
   506  	f.Close()
   507  	if err != nil {
   508  		t.Fatalf("err: %s", err)
   509  	}
   510  
   511  	actualStr := strings.TrimSpace(backupStateFile.State.String())
   512  	expectedStr := strings.TrimSpace(originalState.String())
   513  	if actualStr != expectedStr {
   514  		t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\nb%s", actualStr, expectedStr)
   515  	}
   516  }
   517  
   518  // Config with multiple resources with dependencies, targeting destroy of a
   519  // leaf node, expecting the other resources to remain.
   520  func TestApply_destroyTargeted(t *testing.T) {
   521  	// Create a temporary working directory that is empty
   522  	td := t.TempDir()
   523  	testCopyDir(t, testFixturePath("apply-destroy-targeted"), td)
   524  	defer testChdir(t, td)()
   525  
   526  	originalState := states.BuildState(func(s *states.SyncState) {
   527  		s.SetResourceInstanceCurrent(
   528  			addrs.Resource{
   529  				Mode: addrs.ManagedResourceMode,
   530  				Type: "test_instance",
   531  				Name: "foo",
   532  			}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   533  			&states.ResourceInstanceObjectSrc{
   534  				AttrsJSON: []byte(`{"id":"i-ab123"}`),
   535  				Status:    states.ObjectReady,
   536  			},
   537  			addrs.AbsProviderConfig{
   538  				Provider: addrs.NewDefaultProvider("test"),
   539  				Module:   addrs.RootModule,
   540  			},
   541  		)
   542  		s.SetResourceInstanceCurrent(
   543  			addrs.Resource{
   544  				Mode: addrs.ManagedResourceMode,
   545  				Type: "test_load_balancer",
   546  				Name: "foo",
   547  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   548  			&states.ResourceInstanceObjectSrc{
   549  				AttrsJSON:    []byte(`{"id":"i-abc123"}`),
   550  				Dependencies: []addrs.ConfigResource{mustResourceAddr("test_instance.foo")},
   551  				Status:       states.ObjectReady,
   552  			},
   553  			addrs.AbsProviderConfig{
   554  				Provider: addrs.NewDefaultProvider("test"),
   555  				Module:   addrs.RootModule,
   556  			},
   557  		)
   558  	})
   559  	wantState := states.BuildState(func(s *states.SyncState) {
   560  		s.SetResourceInstanceCurrent(
   561  			addrs.Resource{
   562  				Mode: addrs.ManagedResourceMode,
   563  				Type: "test_instance",
   564  				Name: "foo",
   565  			}.Instance(addrs.IntKey(0)).Absolute(addrs.RootModuleInstance),
   566  			&states.ResourceInstanceObjectSrc{
   567  				AttrsJSON: []byte(`{"id":"i-ab123"}`),
   568  				Status:    states.ObjectReady,
   569  			},
   570  			addrs.AbsProviderConfig{
   571  				Provider: addrs.NewDefaultProvider("test"),
   572  				Module:   addrs.RootModule,
   573  			},
   574  		)
   575  	})
   576  	statePath := testStateFile(t, originalState)
   577  
   578  	p := testProvider()
   579  	p.GetProviderSchemaResponse = &providers.GetProviderSchemaResponse{
   580  		ResourceTypes: map[string]providers.Schema{
   581  			"test_instance": {
   582  				Block: &configschema.Block{
   583  					Attributes: map[string]*configschema.Attribute{
   584  						"id": {Type: cty.String, Computed: true},
   585  					},
   586  				},
   587  			},
   588  			"test_load_balancer": {
   589  				Block: &configschema.Block{
   590  					Attributes: map[string]*configschema.Attribute{
   591  						"id":        {Type: cty.String, Computed: true},
   592  						"instances": {Type: cty.List(cty.String), Optional: true},
   593  					},
   594  				},
   595  			},
   596  		},
   597  	}
   598  	p.PlanResourceChangeFn = func(req providers.PlanResourceChangeRequest) providers.PlanResourceChangeResponse {
   599  		return providers.PlanResourceChangeResponse{
   600  			PlannedState: req.ProposedNewState,
   601  		}
   602  	}
   603  
   604  	view, done := testView(t)
   605  	c := &ApplyCommand{
   606  		Destroy: true,
   607  		Meta: Meta{
   608  			testingOverrides: metaOverridesForProvider(p),
   609  			View:             view,
   610  		},
   611  	}
   612  
   613  	// Run the apply command pointing to our existing state
   614  	args := []string{
   615  		"-auto-approve",
   616  		"-target", "test_load_balancer.foo",
   617  		"-state", statePath,
   618  	}
   619  	code := c.Run(args)
   620  	output := done(t)
   621  	if code != 0 {
   622  		t.Log(output.Stdout())
   623  		t.Fatalf("bad: %d\n\n%s", code, output.Stderr())
   624  	}
   625  
   626  	// Verify a new state exists
   627  	if _, err := os.Stat(statePath); err != nil {
   628  		t.Fatalf("err: %s", err)
   629  	}
   630  
   631  	f, err := os.Open(statePath)
   632  	if err != nil {
   633  		t.Fatalf("err: %s", err)
   634  	}
   635  	defer f.Close()
   636  
   637  	stateFile, err := statefile.Read(f)
   638  	if err != nil {
   639  		t.Fatalf("err: %s", err)
   640  	}
   641  	if stateFile == nil || stateFile.State == nil {
   642  		t.Fatal("state should not be nil")
   643  	}
   644  
   645  	actualStr := strings.TrimSpace(stateFile.State.String())
   646  	expectedStr := strings.TrimSpace(wantState.String())
   647  	if actualStr != expectedStr {
   648  		t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\nb%s", actualStr, expectedStr)
   649  	}
   650  
   651  	// Should have a backup file
   652  	f, err = os.Open(statePath + DefaultBackupExtension)
   653  	if err != nil {
   654  		t.Fatalf("err: %s", err)
   655  	}
   656  
   657  	backupStateFile, err := statefile.Read(f)
   658  	f.Close()
   659  	if err != nil {
   660  		t.Fatalf("err: %s", err)
   661  	}
   662  
   663  	backupActualStr := strings.TrimSpace(backupStateFile.State.String())
   664  	backupExpectedStr := strings.TrimSpace(originalState.String())
   665  	if backupActualStr != backupExpectedStr {
   666  		t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\nb%s", backupActualStr, backupExpectedStr)
   667  	}
   668  }
   669  
   670  const testApplyDestroyStr = `
   671  <no state>
   672  `