github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/terraform/diff_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"reflect"
     5  	"strings"
     6  	"testing"
     7  )
     8  
     9  func TestDiffEmpty(t *testing.T) {
    10  	diff := new(Diff)
    11  	if !diff.Empty() {
    12  		t.Fatal("should be empty")
    13  	}
    14  
    15  	mod := diff.AddModule(rootModulePath)
    16  	mod.Resources["nodeA"] = &InstanceDiff{
    17  		Attributes: map[string]*ResourceAttrDiff{
    18  			"foo": &ResourceAttrDiff{
    19  				Old: "foo",
    20  				New: "bar",
    21  			},
    22  		},
    23  	}
    24  
    25  	if diff.Empty() {
    26  		t.Fatal("should not be empty")
    27  	}
    28  }
    29  
    30  func TestModuleDiff_ChangeType(t *testing.T) {
    31  	cases := []struct {
    32  		Diff   *ModuleDiff
    33  		Result DiffChangeType
    34  	}{
    35  		{
    36  			&ModuleDiff{},
    37  			DiffNone,
    38  		},
    39  		{
    40  			&ModuleDiff{
    41  				Resources: map[string]*InstanceDiff{
    42  					"foo": &InstanceDiff{Destroy: true},
    43  				},
    44  			},
    45  			DiffDestroy,
    46  		},
    47  		{
    48  			&ModuleDiff{
    49  				Resources: map[string]*InstanceDiff{
    50  					"foo": &InstanceDiff{
    51  						Attributes: map[string]*ResourceAttrDiff{
    52  							"foo": &ResourceAttrDiff{
    53  								Old: "",
    54  								New: "bar",
    55  							},
    56  						},
    57  					},
    58  				},
    59  			},
    60  			DiffUpdate,
    61  		},
    62  		{
    63  			&ModuleDiff{
    64  				Resources: map[string]*InstanceDiff{
    65  					"foo": &InstanceDiff{
    66  						Attributes: map[string]*ResourceAttrDiff{
    67  							"foo": &ResourceAttrDiff{
    68  								Old:         "",
    69  								New:         "bar",
    70  								RequiresNew: true,
    71  							},
    72  						},
    73  					},
    74  				},
    75  			},
    76  			DiffCreate,
    77  		},
    78  		{
    79  			&ModuleDiff{
    80  				Resources: map[string]*InstanceDiff{
    81  					"foo": &InstanceDiff{
    82  						Destroy: true,
    83  						Attributes: map[string]*ResourceAttrDiff{
    84  							"foo": &ResourceAttrDiff{
    85  								Old:         "",
    86  								New:         "bar",
    87  								RequiresNew: true,
    88  							},
    89  						},
    90  					},
    91  				},
    92  			},
    93  			DiffUpdate,
    94  		},
    95  	}
    96  
    97  	for i, tc := range cases {
    98  		actual := tc.Diff.ChangeType()
    99  		if actual != tc.Result {
   100  			t.Fatalf("%d: %#v", i, actual)
   101  		}
   102  	}
   103  }
   104  
   105  func TestModuleDiff_Empty(t *testing.T) {
   106  	diff := new(ModuleDiff)
   107  	if !diff.Empty() {
   108  		t.Fatal("should be empty")
   109  	}
   110  
   111  	diff.Resources = map[string]*InstanceDiff{
   112  		"nodeA": &InstanceDiff{},
   113  	}
   114  
   115  	if !diff.Empty() {
   116  		t.Fatal("should be empty")
   117  	}
   118  
   119  	diff.Resources["nodeA"].Attributes = map[string]*ResourceAttrDiff{
   120  		"foo": &ResourceAttrDiff{
   121  			Old: "foo",
   122  			New: "bar",
   123  		},
   124  	}
   125  
   126  	if diff.Empty() {
   127  		t.Fatal("should not be empty")
   128  	}
   129  
   130  	diff.Resources["nodeA"].Attributes = nil
   131  	diff.Resources["nodeA"].Destroy = true
   132  
   133  	if diff.Empty() {
   134  		t.Fatal("should not be empty")
   135  	}
   136  }
   137  
   138  func TestModuleDiff_String(t *testing.T) {
   139  	diff := &ModuleDiff{
   140  		Resources: map[string]*InstanceDiff{
   141  			"nodeA": &InstanceDiff{
   142  				Attributes: map[string]*ResourceAttrDiff{
   143  					"foo": &ResourceAttrDiff{
   144  						Old: "foo",
   145  						New: "bar",
   146  					},
   147  					"bar": &ResourceAttrDiff{
   148  						Old:         "foo",
   149  						NewComputed: true,
   150  					},
   151  					"longfoo": &ResourceAttrDiff{
   152  						Old:         "foo",
   153  						New:         "bar",
   154  						RequiresNew: true,
   155  					},
   156  				},
   157  			},
   158  		},
   159  	}
   160  
   161  	actual := strings.TrimSpace(diff.String())
   162  	expected := strings.TrimSpace(moduleDiffStrBasic)
   163  	if actual != expected {
   164  		t.Fatalf("bad:\n%s", actual)
   165  	}
   166  }
   167  
   168  func TestInstanceDiff_ChangeType(t *testing.T) {
   169  	cases := []struct {
   170  		Diff   *InstanceDiff
   171  		Result DiffChangeType
   172  	}{
   173  		{
   174  			&InstanceDiff{},
   175  			DiffNone,
   176  		},
   177  		{
   178  			&InstanceDiff{Destroy: true},
   179  			DiffDestroy,
   180  		},
   181  		{
   182  			&InstanceDiff{
   183  				Attributes: map[string]*ResourceAttrDiff{
   184  					"foo": &ResourceAttrDiff{
   185  						Old: "",
   186  						New: "bar",
   187  					},
   188  				},
   189  			},
   190  			DiffUpdate,
   191  		},
   192  		{
   193  			&InstanceDiff{
   194  				Attributes: map[string]*ResourceAttrDiff{
   195  					"foo": &ResourceAttrDiff{
   196  						Old:         "",
   197  						New:         "bar",
   198  						RequiresNew: true,
   199  					},
   200  				},
   201  			},
   202  			DiffCreate,
   203  		},
   204  		{
   205  			&InstanceDiff{
   206  				Destroy: true,
   207  				Attributes: map[string]*ResourceAttrDiff{
   208  					"foo": &ResourceAttrDiff{
   209  						Old:         "",
   210  						New:         "bar",
   211  						RequiresNew: true,
   212  					},
   213  				},
   214  			},
   215  			DiffDestroyCreate,
   216  		},
   217  		{
   218  			&InstanceDiff{
   219  				DestroyTainted: true,
   220  				Attributes: map[string]*ResourceAttrDiff{
   221  					"foo": &ResourceAttrDiff{
   222  						Old:         "",
   223  						New:         "bar",
   224  						RequiresNew: true,
   225  					},
   226  				},
   227  			},
   228  			DiffDestroyCreate,
   229  		},
   230  	}
   231  
   232  	for i, tc := range cases {
   233  		actual := tc.Diff.ChangeType()
   234  		if actual != tc.Result {
   235  			t.Fatalf("%d: %#v", i, actual)
   236  		}
   237  	}
   238  }
   239  
   240  func TestInstanceDiff_Empty(t *testing.T) {
   241  	var rd *InstanceDiff
   242  
   243  	if !rd.Empty() {
   244  		t.Fatal("should be empty")
   245  	}
   246  
   247  	rd = new(InstanceDiff)
   248  
   249  	if !rd.Empty() {
   250  		t.Fatal("should be empty")
   251  	}
   252  
   253  	rd = &InstanceDiff{Destroy: true}
   254  
   255  	if rd.Empty() {
   256  		t.Fatal("should not be empty")
   257  	}
   258  
   259  	rd = &InstanceDiff{
   260  		Attributes: map[string]*ResourceAttrDiff{
   261  			"foo": &ResourceAttrDiff{
   262  				New: "bar",
   263  			},
   264  		},
   265  	}
   266  
   267  	if rd.Empty() {
   268  		t.Fatal("should not be empty")
   269  	}
   270  }
   271  
   272  func TestModuleDiff_Instances(t *testing.T) {
   273  	yesDiff := &InstanceDiff{Destroy: true}
   274  	noDiff := &InstanceDiff{Destroy: true, DestroyTainted: true}
   275  
   276  	cases := []struct {
   277  		Diff   *ModuleDiff
   278  		Id     string
   279  		Result []*InstanceDiff
   280  	}{
   281  		{
   282  			&ModuleDiff{
   283  				Resources: map[string]*InstanceDiff{
   284  					"foo": yesDiff,
   285  					"bar": noDiff,
   286  				},
   287  			},
   288  			"foo",
   289  			[]*InstanceDiff{
   290  				yesDiff,
   291  			},
   292  		},
   293  
   294  		{
   295  			&ModuleDiff{
   296  				Resources: map[string]*InstanceDiff{
   297  					"foo":   yesDiff,
   298  					"foo.0": yesDiff,
   299  					"bar":   noDiff,
   300  				},
   301  			},
   302  			"foo",
   303  			[]*InstanceDiff{
   304  				yesDiff,
   305  				yesDiff,
   306  			},
   307  		},
   308  
   309  		{
   310  			&ModuleDiff{
   311  				Resources: map[string]*InstanceDiff{
   312  					"foo":     yesDiff,
   313  					"foo.0":   yesDiff,
   314  					"foo_bar": noDiff,
   315  					"bar":     noDiff,
   316  				},
   317  			},
   318  			"foo",
   319  			[]*InstanceDiff{
   320  				yesDiff,
   321  				yesDiff,
   322  			},
   323  		},
   324  	}
   325  
   326  	for i, tc := range cases {
   327  		actual := tc.Diff.Instances(tc.Id)
   328  		if !reflect.DeepEqual(actual, tc.Result) {
   329  			t.Fatalf("%d: %#v", i, actual)
   330  		}
   331  	}
   332  }
   333  
   334  func TestInstanceDiff_RequiresNew(t *testing.T) {
   335  	rd := &InstanceDiff{
   336  		Attributes: map[string]*ResourceAttrDiff{
   337  			"foo": &ResourceAttrDiff{},
   338  		},
   339  	}
   340  
   341  	if rd.RequiresNew() {
   342  		t.Fatal("should not require new")
   343  	}
   344  
   345  	rd.Attributes["foo"].RequiresNew = true
   346  
   347  	if !rd.RequiresNew() {
   348  		t.Fatal("should require new")
   349  	}
   350  }
   351  
   352  func TestInstanceDiff_RequiresNew_nil(t *testing.T) {
   353  	var rd *InstanceDiff
   354  
   355  	if rd.RequiresNew() {
   356  		t.Fatal("should not require new")
   357  	}
   358  }
   359  
   360  func TestInstanceDiffSame(t *testing.T) {
   361  	cases := []struct {
   362  		One, Two *InstanceDiff
   363  		Same     bool
   364  		Reason   string
   365  	}{
   366  		{
   367  			&InstanceDiff{},
   368  			&InstanceDiff{},
   369  			true,
   370  			"",
   371  		},
   372  
   373  		{
   374  			nil,
   375  			nil,
   376  			true,
   377  			"",
   378  		},
   379  
   380  		{
   381  			&InstanceDiff{Destroy: false},
   382  			&InstanceDiff{Destroy: true},
   383  			false,
   384  			"diff: Destroy; old: false, new: true",
   385  		},
   386  
   387  		{
   388  			&InstanceDiff{Destroy: true},
   389  			&InstanceDiff{Destroy: true},
   390  			true,
   391  			"",
   392  		},
   393  
   394  		{
   395  			&InstanceDiff{
   396  				Attributes: map[string]*ResourceAttrDiff{
   397  					"foo": &ResourceAttrDiff{},
   398  				},
   399  			},
   400  			&InstanceDiff{
   401  				Attributes: map[string]*ResourceAttrDiff{
   402  					"foo": &ResourceAttrDiff{},
   403  				},
   404  			},
   405  			true,
   406  			"",
   407  		},
   408  
   409  		{
   410  			&InstanceDiff{
   411  				Attributes: map[string]*ResourceAttrDiff{
   412  					"bar": &ResourceAttrDiff{},
   413  				},
   414  			},
   415  			&InstanceDiff{
   416  				Attributes: map[string]*ResourceAttrDiff{
   417  					"foo": &ResourceAttrDiff{},
   418  				},
   419  			},
   420  			false,
   421  			"attribute mismatch: bar",
   422  		},
   423  
   424  		// Extra attributes
   425  		{
   426  			&InstanceDiff{
   427  				Attributes: map[string]*ResourceAttrDiff{
   428  					"foo": &ResourceAttrDiff{},
   429  				},
   430  			},
   431  			&InstanceDiff{
   432  				Attributes: map[string]*ResourceAttrDiff{
   433  					"foo": &ResourceAttrDiff{},
   434  					"bar": &ResourceAttrDiff{},
   435  				},
   436  			},
   437  			false,
   438  			"extra attributes: bar",
   439  		},
   440  
   441  		{
   442  			&InstanceDiff{
   443  				Attributes: map[string]*ResourceAttrDiff{
   444  					"foo": &ResourceAttrDiff{RequiresNew: true},
   445  				},
   446  			},
   447  			&InstanceDiff{
   448  				Attributes: map[string]*ResourceAttrDiff{
   449  					"foo": &ResourceAttrDiff{RequiresNew: false},
   450  				},
   451  			},
   452  			false,
   453  			"diff RequiresNew; old: true, new: false",
   454  		},
   455  
   456  		{
   457  			&InstanceDiff{
   458  				Attributes: map[string]*ResourceAttrDiff{
   459  					"foo.#": &ResourceAttrDiff{NewComputed: true},
   460  				},
   461  			},
   462  			&InstanceDiff{
   463  				Attributes: map[string]*ResourceAttrDiff{
   464  					"foo.#": &ResourceAttrDiff{
   465  						Old: "0",
   466  						New: "1",
   467  					},
   468  					"foo.0": &ResourceAttrDiff{
   469  						Old: "",
   470  						New: "12",
   471  					},
   472  				},
   473  			},
   474  			true,
   475  			"",
   476  		},
   477  
   478  		{
   479  			&InstanceDiff{
   480  				Attributes: map[string]*ResourceAttrDiff{
   481  					"foo.#": &ResourceAttrDiff{
   482  						Old: "0",
   483  						New: "1",
   484  					},
   485  					"foo.~35964334.bar": &ResourceAttrDiff{
   486  						Old: "",
   487  						New: "${var.foo}",
   488  					},
   489  				},
   490  			},
   491  			&InstanceDiff{
   492  				Attributes: map[string]*ResourceAttrDiff{
   493  					"foo.#": &ResourceAttrDiff{
   494  						Old: "0",
   495  						New: "1",
   496  					},
   497  					"foo.87654323.bar": &ResourceAttrDiff{
   498  						Old: "",
   499  						New: "12",
   500  					},
   501  				},
   502  			},
   503  			true,
   504  			"",
   505  		},
   506  
   507  		{
   508  			&InstanceDiff{
   509  				Attributes: map[string]*ResourceAttrDiff{
   510  					"foo.#": &ResourceAttrDiff{
   511  						Old:         "0",
   512  						NewComputed: true,
   513  					},
   514  				},
   515  			},
   516  			&InstanceDiff{
   517  				Attributes: map[string]*ResourceAttrDiff{},
   518  			},
   519  			true,
   520  			"",
   521  		},
   522  
   523  		// In a DESTROY/CREATE scenario, the plan diff will be run against the
   524  		// state of the old instance, while the apply diff will be run against an
   525  		// empty state (because the state is cleared when the destroy runs.)
   526  		// For complex attributes, this can result in keys that seem to disappear
   527  		// between the two diffs, when in reality everything is working just fine.
   528  		//
   529  		// Same() needs to take into account this scenario by analyzing NewRemoved
   530  		// and treating as "Same" a diff that does indeed have that key removed.
   531  		{
   532  			&InstanceDiff{
   533  				Attributes: map[string]*ResourceAttrDiff{
   534  					"somemap.oldkey": &ResourceAttrDiff{
   535  						Old:        "long ago",
   536  						New:        "",
   537  						NewRemoved: true,
   538  					},
   539  					"somemap.newkey": &ResourceAttrDiff{
   540  						Old: "",
   541  						New: "brave new world",
   542  					},
   543  				},
   544  			},
   545  			&InstanceDiff{
   546  				Attributes: map[string]*ResourceAttrDiff{
   547  					"somemap.newkey": &ResourceAttrDiff{
   548  						Old: "",
   549  						New: "brave new world",
   550  					},
   551  				},
   552  			},
   553  			true,
   554  			"",
   555  		},
   556  	}
   557  
   558  	for i, tc := range cases {
   559  		same, reason := tc.One.Same(tc.Two)
   560  		if same != tc.Same {
   561  			t.Fatalf("%d: expected same: %t, got %t (%s)\n\n one: %#v\n\ntwo: %#v",
   562  				i, tc.Same, same, reason, tc.One, tc.Two)
   563  		}
   564  		if reason != tc.Reason {
   565  			t.Fatalf(
   566  				"%d: bad reason\n\nexpected: %#v\n\ngot: %#v", i, tc.Reason, reason)
   567  		}
   568  	}
   569  }
   570  
   571  const moduleDiffStrBasic = `
   572  CREATE: nodeA
   573    bar:     "foo" => "<computed>"
   574    foo:     "foo" => "bar"
   575    longfoo: "foo" => "bar" (forces new resource)
   576  `