github.com/arvindram03/terraform@v0.3.7-0.20150212015210-408f838db36d/terraform/graph_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"reflect"
     5  	"sort"
     6  	"strings"
     7  	"testing"
     8  )
     9  
    10  func TestGraph_basic(t *testing.T) {
    11  	m := testModule(t, "graph-basic")
    12  
    13  	g, err := Graph(&GraphOpts{Module: m})
    14  	if err != nil {
    15  		t.Fatalf("err: %s", err)
    16  	}
    17  
    18  	actual := strings.TrimSpace(g.String())
    19  	expected := strings.TrimSpace(testTerraformGraphStr)
    20  	if actual != expected {
    21  		t.Fatalf("bad:\n\n%s", actual)
    22  	}
    23  }
    24  
    25  func TestGraph_configRequired(t *testing.T) {
    26  	if _, err := Graph(new(GraphOpts)); err == nil {
    27  		t.Fatal("should error")
    28  	}
    29  }
    30  
    31  func TestGraph_count(t *testing.T) {
    32  	m := testModule(t, "graph-count")
    33  
    34  	g, err := Graph(&GraphOpts{Module: m})
    35  	if err != nil {
    36  		t.Fatalf("err: %s", err)
    37  	}
    38  
    39  	actual := strings.TrimSpace(g.String())
    40  	expected := strings.TrimSpace(testTerraformGraphCountStr)
    41  	if actual != expected {
    42  		t.Fatalf("bad:\n\n%s", actual)
    43  	}
    44  }
    45  
    46  func TestGraph_countTainted(t *testing.T) {
    47  	m := testModule(t, "graph-count")
    48  	state := &State{
    49  		Modules: []*ModuleState{
    50  			&ModuleState{
    51  				Path: []string{"root"},
    52  				Resources: map[string]*ResourceState{
    53  					"aws_instance.web.0": &ResourceState{
    54  						Type: "aws_instance",
    55  						Tainted: []*InstanceState{
    56  							&InstanceState{
    57  								ID: "foo",
    58  							},
    59  						},
    60  					},
    61  				},
    62  			},
    63  		},
    64  	}
    65  
    66  	g, err := Graph(&GraphOpts{Module: m, State: state})
    67  	if err != nil {
    68  		t.Fatalf("err: %s", err)
    69  	}
    70  
    71  	actual := strings.TrimSpace(g.String())
    72  	expected := strings.TrimSpace(testTerraformGraphCountTaintedStr)
    73  	if actual != expected {
    74  		t.Fatalf("bad:\n\n%s", actual)
    75  	}
    76  }
    77  
    78  func TestGraph_varResource(t *testing.T) {
    79  	m := testModule(t, "graph-count-var-resource")
    80  
    81  	g, err := Graph(&GraphOpts{Module: m})
    82  	if err != nil {
    83  		t.Fatalf("err: %s", err)
    84  	}
    85  
    86  	actual := strings.TrimSpace(g.String())
    87  	expected := strings.TrimSpace(testTerraformGraphCountVarResourceStr)
    88  	if actual != expected {
    89  		t.Fatalf("bad:\n\n%s", actual)
    90  	}
    91  }
    92  
    93  func TestGraph_cycle(t *testing.T) {
    94  	m := testModule(t, "graph-cycle")
    95  
    96  	_, err := Graph(&GraphOpts{Module: m})
    97  	if err == nil {
    98  		t.Fatal("should error")
    99  	}
   100  }
   101  
   102  func TestGraph_dependsOn(t *testing.T) {
   103  	m := testModule(t, "graph-depends-on")
   104  
   105  	g, err := Graph(&GraphOpts{Module: m})
   106  	if err != nil {
   107  		t.Fatalf("err: %s", err)
   108  	}
   109  
   110  	actual := strings.TrimSpace(g.String())
   111  	expected := strings.TrimSpace(testTerraformGraphDependsStr)
   112  	if actual != expected {
   113  		t.Fatalf("bad:\n\n%s", actual)
   114  	}
   115  }
   116  
   117  func TestGraph_dependsOnCount(t *testing.T) {
   118  	m := testModule(t, "graph-depends-on-count")
   119  
   120  	g, err := Graph(&GraphOpts{Module: m})
   121  	if err != nil {
   122  		t.Fatalf("err: %s", err)
   123  	}
   124  
   125  	actual := strings.TrimSpace(g.String())
   126  	expected := strings.TrimSpace(testTerraformGraphDependsCountStr)
   127  	if actual != expected {
   128  		t.Fatalf("bad:\n\n%s", actual)
   129  	}
   130  }
   131  
   132  func TestGraph_dependsOnWithOrphan(t *testing.T) {
   133  	m := testModule(t, "graph-depends-on")
   134  
   135  	state := &State{
   136  		Modules: []*ModuleState{
   137  			&ModuleState{
   138  				Path: []string{"root"},
   139  				Resources: map[string]*ResourceState{
   140  					"aws_instance.old": &ResourceState{
   141  						Type: "aws_instance",
   142  						Primary: &InstanceState{
   143  							ID: "foo",
   144  						},
   145  					},
   146  				},
   147  			},
   148  		},
   149  	}
   150  
   151  	g, err := Graph(&GraphOpts{Module: m, State: state})
   152  	if err != nil {
   153  		t.Fatalf("err: %s", err)
   154  	}
   155  
   156  	actual := strings.TrimSpace(g.String())
   157  	expected := strings.TrimSpace(testTerraformGraphDependsOrphanStr)
   158  	if actual != expected {
   159  		t.Fatalf("bad:\n\n%s", actual)
   160  	}
   161  }
   162  
   163  func TestGraph_modules(t *testing.T) {
   164  	m := testModule(t, "graph-modules")
   165  
   166  	g, err := Graph(&GraphOpts{Module: m})
   167  	if err != nil {
   168  		t.Fatalf("err: %s", err)
   169  	}
   170  
   171  	actual := strings.TrimSpace(g.String())
   172  	expected := strings.TrimSpace(testTerraformGraphModulesStr)
   173  	if actual != expected {
   174  		t.Fatalf("bad:\n\n%s", actual)
   175  	}
   176  
   177  	n := g.Noun("module.consul")
   178  	if n == nil {
   179  		t.Fatal("can't find noun")
   180  	}
   181  	mn := n.Meta.(*GraphNodeModule)
   182  
   183  	if !reflect.DeepEqual(mn.Path, []string{"root", "consul"}) {
   184  		t.Fatalf("bad: %#v", mn.Path)
   185  	}
   186  
   187  	actual = strings.TrimSpace(mn.Graph.String())
   188  	expected = strings.TrimSpace(testTerraformGraphModulesConsulStr)
   189  	if actual != expected {
   190  		t.Fatalf("bad:\n\n%s", actual)
   191  	}
   192  }
   193  
   194  func TestGraph_moduleOrphan(t *testing.T) {
   195  	m := testModule(t, "graph-module-orphan")
   196  	state := &State{
   197  		Modules: []*ModuleState{
   198  			&ModuleState{
   199  				Path: []string{"root", "consul"},
   200  
   201  				Resources: map[string]*ResourceState{
   202  					"aws_instance.old": &ResourceState{
   203  						Type: "aws_instance",
   204  						Primary: &InstanceState{
   205  							ID: "foo",
   206  						},
   207  					},
   208  				},
   209  			},
   210  		},
   211  	}
   212  
   213  	g, err := Graph(&GraphOpts{Module: m, State: state})
   214  	if err != nil {
   215  		t.Fatalf("err: %s", err)
   216  	}
   217  
   218  	actual := strings.TrimSpace(g.String())
   219  	expected := strings.TrimSpace(testTerraformGraphModuleOrphanStr)
   220  	if actual != expected {
   221  		t.Fatalf("bad:\n\n%s", actual)
   222  	}
   223  
   224  	n := g.Noun("module.consul")
   225  	if n == nil {
   226  		t.Fatal("can't find noun")
   227  	}
   228  	mn := n.Meta.(*GraphNodeModule)
   229  
   230  	if !reflect.DeepEqual(mn.Path, []string{"root", "consul"}) {
   231  		t.Fatalf("bad: %#v", mn.Path)
   232  	}
   233  
   234  	actual = strings.TrimSpace(mn.Graph.String())
   235  	expected = strings.TrimSpace(testTerraformGraphModuleOrphanConsulStr)
   236  	if actual != expected {
   237  		t.Fatalf("bad:\n\n%s", actual)
   238  	}
   239  }
   240  
   241  func TestGraph_providerPrune(t *testing.T) {
   242  	m := testModule(t, "graph-provider-prune")
   243  
   244  	g, err := Graph(&GraphOpts{Module: m})
   245  	if err != nil {
   246  		t.Fatalf("err: %s", err)
   247  	}
   248  
   249  	actual := strings.TrimSpace(g.String())
   250  	expected := strings.TrimSpace(testTerraformGraphProviderPruneStr)
   251  	if actual != expected {
   252  		t.Fatalf("bad:\n\n%s", actual)
   253  	}
   254  }
   255  
   256  func TestGraph_state(t *testing.T) {
   257  	m := testModule(t, "graph-basic")
   258  	state := &State{
   259  		Modules: []*ModuleState{
   260  			&ModuleState{
   261  				Path: rootModulePath,
   262  
   263  				Resources: map[string]*ResourceState{
   264  					"aws_instance.old": &ResourceState{
   265  						Type: "aws_instance",
   266  						Primary: &InstanceState{
   267  							ID: "foo",
   268  						},
   269  					},
   270  				},
   271  			},
   272  		},
   273  	}
   274  
   275  	g, err := Graph(&GraphOpts{Module: m, State: state})
   276  	if err != nil {
   277  		t.Fatalf("err: %s", err)
   278  	}
   279  
   280  	actual := strings.TrimSpace(g.String())
   281  	expected := strings.TrimSpace(testTerraformGraphStateStr)
   282  	if actual != expected {
   283  		t.Fatalf("bad:\n\n%s", actual)
   284  	}
   285  }
   286  
   287  func TestGraph_tainted(t *testing.T) {
   288  	m := testModule(t, "graph-tainted")
   289  	state := &State{
   290  		Modules: []*ModuleState{
   291  			&ModuleState{
   292  				Path: rootModulePath,
   293  
   294  				Resources: map[string]*ResourceState{
   295  					"aws_instance.web": &ResourceState{
   296  						Type: "aws_instance",
   297  						Primary: &InstanceState{
   298  							ID: "foo",
   299  						},
   300  						Tainted: []*InstanceState{
   301  							&InstanceState{
   302  								ID: "bar",
   303  							},
   304  						},
   305  					},
   306  				},
   307  			},
   308  		},
   309  	}
   310  
   311  	g, err := Graph(&GraphOpts{Module: m, State: state})
   312  	if err != nil {
   313  		t.Fatalf("err: %s", err)
   314  	}
   315  
   316  	actual := strings.TrimSpace(g.String())
   317  	expected := strings.TrimSpace(testTerraformGraphTaintedStr)
   318  	if actual != expected {
   319  		t.Fatalf("bad:\n\n%s", actual)
   320  	}
   321  }
   322  
   323  func TestGraph_taintedMulti(t *testing.T) {
   324  	m := testModule(t, "graph-tainted")
   325  	state := &State{
   326  		Modules: []*ModuleState{
   327  			&ModuleState{
   328  				Path: rootModulePath,
   329  
   330  				Resources: map[string]*ResourceState{
   331  					"aws_instance.web": &ResourceState{
   332  						Type: "aws_instance",
   333  						Primary: &InstanceState{
   334  							ID: "foo",
   335  						},
   336  						Tainted: []*InstanceState{
   337  							&InstanceState{
   338  								ID: "bar",
   339  							},
   340  							&InstanceState{
   341  								ID: "baz",
   342  							},
   343  						},
   344  					},
   345  				},
   346  			},
   347  		},
   348  	}
   349  
   350  	g, err := Graph(&GraphOpts{Module: m, State: state})
   351  	if err != nil {
   352  		t.Fatalf("err: %s", err)
   353  	}
   354  
   355  	actual := strings.TrimSpace(g.String())
   356  	expected := strings.TrimSpace(testTerraformGraphTaintedMultiStr)
   357  	if actual != expected {
   358  		t.Fatalf("bad:\n\n%s", actual)
   359  	}
   360  }
   361  
   362  func TestGraphFull(t *testing.T) {
   363  	rpAws := new(MockResourceProvider)
   364  	rpOS := new(MockResourceProvider)
   365  
   366  	rpAws.ResourcesReturn = []ResourceType{
   367  		ResourceType{Name: "aws_instance"},
   368  		ResourceType{Name: "aws_load_balancer"},
   369  		ResourceType{Name: "aws_security_group"},
   370  	}
   371  	rpOS.ResourcesReturn = []ResourceType{
   372  		ResourceType{Name: "openstack_floating_ip"},
   373  	}
   374  
   375  	ps := map[string]ResourceProviderFactory{
   376  		"aws":  testProviderFuncFixed(rpAws),
   377  		"open": testProviderFuncFixed(rpOS),
   378  	}
   379  
   380  	m := testModule(t, "graph-basic")
   381  	g, err := Graph(&GraphOpts{Module: m, Providers: ps})
   382  	if err != nil {
   383  		t.Fatalf("err: %s", err)
   384  	}
   385  
   386  	// A helper to help get us the provider for a resource.
   387  	graphProvider := func(n string) ResourceProvider {
   388  		return g.Noun(n).Meta.(*GraphNodeResource).Resource.Provider
   389  	}
   390  
   391  	// Test a couple
   392  	if graphProvider("aws_instance.web") != rpAws {
   393  		t.Fatalf("bad: %#v", graphProvider("aws_instance.web"))
   394  	}
   395  	if graphProvider("openstack_floating_ip.random") != rpOS {
   396  		t.Fatalf("bad: %#v", graphProvider("openstack_floating_ip.random"))
   397  	}
   398  
   399  	// Test that all providers have been set
   400  	for _, n := range g.Nouns {
   401  		switch m := n.Meta.(type) {
   402  		case *GraphNodeResource:
   403  			if m.Resource.Provider == nil {
   404  				t.Fatalf("bad: %#v", m)
   405  			}
   406  		case *GraphNodeResourceProvider:
   407  			if len(m.Provider.Providers) == 0 {
   408  				t.Fatalf("bad: %#v", m)
   409  			}
   410  		default:
   411  			continue
   412  		}
   413  	}
   414  }
   415  
   416  func TestGraphProvisioners(t *testing.T) {
   417  	rpAws := new(MockResourceProvider)
   418  	provShell := new(MockResourceProvisioner)
   419  	provWinRM := new(MockResourceProvisioner)
   420  
   421  	rpAws.ResourcesReturn = []ResourceType{
   422  		ResourceType{Name: "aws_instance"},
   423  		ResourceType{Name: "aws_load_balancer"},
   424  		ResourceType{Name: "aws_security_group"},
   425  	}
   426  
   427  	ps := map[string]ResourceProvisionerFactory{
   428  		"shell": testProvisionerFuncFixed(provShell),
   429  		"winrm": testProvisionerFuncFixed(provWinRM),
   430  	}
   431  
   432  	pf := map[string]ResourceProviderFactory{
   433  		"aws": testProviderFuncFixed(rpAws),
   434  	}
   435  
   436  	m := testModule(t, "graph-provisioners")
   437  	g, err := Graph(&GraphOpts{Module: m, Providers: pf, Provisioners: ps})
   438  	if err != nil {
   439  		t.Fatalf("err: %s", err)
   440  	}
   441  
   442  	// A helper to help get us the provider for a resource.
   443  	graphProvisioner := func(n string, idx int) *ResourceProvisionerConfig {
   444  		return g.Noun(n).Meta.(*GraphNodeResource).Resource.Provisioners[idx]
   445  	}
   446  
   447  	// A helper to verify depedencies
   448  	depends := func(a, b string) bool {
   449  		aNoun := g.Noun(a)
   450  		bNoun := g.Noun(b)
   451  		for _, dep := range aNoun.Deps {
   452  			if dep.Source == aNoun && dep.Target == bNoun {
   453  				return true
   454  			}
   455  		}
   456  		return false
   457  	}
   458  
   459  	// Test a couple
   460  	prov := graphProvisioner("aws_instance.web", 0)
   461  	if prov.Provisioner != provWinRM {
   462  		t.Fatalf("bad: %#v", prov)
   463  	}
   464  	if prov.RawConfig.Config()["cmd"] != "echo foo" {
   465  		t.Fatalf("bad: %#v", prov)
   466  	}
   467  
   468  	prov = graphProvisioner("aws_instance.web", 1)
   469  	if prov.Provisioner != provWinRM {
   470  		t.Fatalf("bad: %#v", prov)
   471  	}
   472  	if prov.RawConfig.Config()["cmd"] != "echo bar" {
   473  		t.Fatalf("bad: %#v", prov)
   474  	}
   475  
   476  	prov = graphProvisioner("aws_load_balancer.weblb", 0)
   477  	if prov.Provisioner != provShell {
   478  		t.Fatalf("bad: %#v", prov)
   479  	}
   480  	if prov.RawConfig.Config()["cmd"] != "add ${aws_instance.web.id}" {
   481  		t.Fatalf("bad: %#v", prov)
   482  	}
   483  	if prov.ConnInfo == nil || len(prov.ConnInfo.Raw) != 2 {
   484  		t.Fatalf("bad: %#v", prov)
   485  	}
   486  
   487  	// Check that the variable dependency is handled
   488  	if !depends("aws_load_balancer.weblb", "aws_instance.web") {
   489  		t.Fatalf("missing dependency from provisioner variable")
   490  	}
   491  
   492  	// Check that the connection variable dependency is handled
   493  	if !depends("aws_load_balancer.weblb", "aws_security_group.firewall") {
   494  		t.Fatalf("missing dependency from provisioner connection")
   495  	}
   496  }
   497  
   498  func TestGraphAddDiff(t *testing.T) {
   499  	m := testModule(t, "graph-diff")
   500  	diff := &Diff{
   501  		Modules: []*ModuleDiff{
   502  			&ModuleDiff{
   503  				Path: rootModulePath,
   504  				Resources: map[string]*InstanceDiff{
   505  					"aws_instance.foo": &InstanceDiff{
   506  						Attributes: map[string]*ResourceAttrDiff{
   507  							"foo": &ResourceAttrDiff{
   508  								New: "bar",
   509  							},
   510  						},
   511  					},
   512  				},
   513  			},
   514  		},
   515  	}
   516  
   517  	g, err := Graph(&GraphOpts{Module: m, Diff: diff})
   518  	if err != nil {
   519  		t.Fatalf("err: %s", err)
   520  	}
   521  
   522  	actual := strings.TrimSpace(g.String())
   523  	expected := strings.TrimSpace(testTerraformGraphDiffStr)
   524  	if actual != expected {
   525  		t.Fatalf("bad:\n\n%s", actual)
   526  	}
   527  
   528  	/*
   529  		TODO: test this somewhere
   530  		// Verify that the state has been added
   531  		n := g.Noun("aws_instance.foo")
   532  		rn := n.Meta.(*GraphNodeResource)
   533  
   534  		expected2 := diff.RootModule().Resources["aws_instance.foo"]
   535  		actual2 := rn.Resource.Diff
   536  		if !reflect.DeepEqual(actual2, expected2) {
   537  			t.Fatalf("bad: %#v", actual2)
   538  		}
   539  	*/
   540  }
   541  
   542  func TestGraphAddDiff_destroy(t *testing.T) {
   543  	m := testModule(t, "graph-diff-destroy")
   544  	diff := &Diff{
   545  		Modules: []*ModuleDiff{
   546  			&ModuleDiff{
   547  				Path: rootModulePath,
   548  				Resources: map[string]*InstanceDiff{
   549  					"aws_instance.foo": &InstanceDiff{
   550  						Destroy: true,
   551  					},
   552  					"aws_instance.bar": &InstanceDiff{
   553  						Destroy: true,
   554  					},
   555  				},
   556  			},
   557  		},
   558  	}
   559  	state := &State{
   560  		Modules: []*ModuleState{
   561  			&ModuleState{
   562  				Path: rootModulePath,
   563  				Resources: map[string]*ResourceState{
   564  					"aws_instance.foo": &ResourceState{
   565  						Type: "aws_instance",
   566  						Primary: &InstanceState{
   567  							ID: "foo",
   568  						},
   569  					},
   570  
   571  					"aws_instance.bar": &ResourceState{
   572  						Type:         "aws_instance",
   573  						Dependencies: []string{"foo"},
   574  						Primary: &InstanceState{
   575  							ID: "bar",
   576  						},
   577  					},
   578  				},
   579  			},
   580  		},
   581  	}
   582  
   583  	diffHash := checksumStruct(t, diff)
   584  
   585  	g, err := Graph(&GraphOpts{
   586  		Module: m,
   587  		Diff:   diff,
   588  		State:  state,
   589  	})
   590  	if err != nil {
   591  		t.Fatalf("err: %s", err)
   592  	}
   593  
   594  	actual := strings.TrimSpace(g.String())
   595  	expected := strings.TrimSpace(testTerraformGraphDiffDestroyStr)
   596  	if actual != expected {
   597  		t.Fatalf("bad:\n\n%s\n\nexpected:\n\n%s", actual, expected)
   598  	}
   599  
   600  	// Verify that the state has been added
   601  	n := g.Noun("aws_instance.foo (destroy)")
   602  	rn := n.Meta.(*GraphNodeResource)
   603  
   604  	expected2 := &InstanceDiff{Destroy: true}
   605  	actual2 := rn.Resource.Diff
   606  	if !reflect.DeepEqual(actual2, expected2) {
   607  		t.Fatalf("bad: %#v", actual2)
   608  	}
   609  
   610  	// Verify that our original structure has not been modified
   611  	diffHash2 := checksumStruct(t, diff)
   612  	if diffHash != diffHash2 {
   613  		t.Fatal("diff has been modified")
   614  	}
   615  }
   616  
   617  func TestGraphAddDiff_destroy_counts(t *testing.T) {
   618  	m := testModule(t, "graph-count")
   619  	diff := &Diff{
   620  		Modules: []*ModuleDiff{
   621  			&ModuleDiff{
   622  				Path: rootModulePath,
   623  				Resources: map[string]*InstanceDiff{
   624  					"aws_instance.web.0": &InstanceDiff{
   625  						Destroy: true,
   626  					},
   627  					"aws_instance.web.1": &InstanceDiff{
   628  						Destroy: true,
   629  					},
   630  					"aws_instance.web.2": &InstanceDiff{
   631  						Destroy: true,
   632  					},
   633  					"aws_load_balancer.weblb": &InstanceDiff{
   634  						Destroy: true,
   635  					},
   636  				},
   637  			},
   638  		},
   639  	}
   640  	state := &State{
   641  		Modules: []*ModuleState{
   642  			&ModuleState{
   643  				Path: rootModulePath,
   644  				Resources: map[string]*ResourceState{
   645  					"aws_instance.web.0": &ResourceState{
   646  						Type: "aws_instance",
   647  						Primary: &InstanceState{
   648  							ID: "foo",
   649  						},
   650  					},
   651  					"aws_instance.web.1": &ResourceState{
   652  						Type: "aws_instance",
   653  						Primary: &InstanceState{
   654  							ID: "foo",
   655  						},
   656  					},
   657  					"aws_instance.web.2": &ResourceState{
   658  						Type: "aws_instance",
   659  						Primary: &InstanceState{
   660  							ID: "foo",
   661  						},
   662  					},
   663  					"aws_load_balancer.weblb": &ResourceState{
   664  						Type:         "aws_load_balancer",
   665  						Dependencies: []string{"aws_instance.web.0", "aws_instance.web.1", "aws_instance.web.2"},
   666  						Primary: &InstanceState{
   667  							ID: "bar",
   668  						},
   669  					},
   670  				},
   671  			},
   672  		},
   673  	}
   674  
   675  	diffHash := checksumStruct(t, diff)
   676  
   677  	g, err := Graph(&GraphOpts{
   678  		Module: m,
   679  		Diff:   diff,
   680  		State:  state,
   681  	})
   682  	if err != nil {
   683  		t.Fatalf("err: %s", err)
   684  	}
   685  
   686  	actual := strings.TrimSpace(g.String())
   687  	expected := strings.TrimSpace(testTerraformGraphDiffDestroyCountsStr)
   688  	if actual != expected {
   689  		t.Fatalf("bad:\n\n%s\n\nexpected:\n\n%s", actual, expected)
   690  	}
   691  
   692  	// Verify that the state has been added
   693  	n := g.Noun("aws_instance.web (destroy)")
   694  	rn := n.Meta.(*GraphNodeResource)
   695  
   696  	if rn.ExpandMode != ResourceExpandDestroy {
   697  		t.Fatalf("bad: %#v", rn)
   698  	}
   699  
   700  	// Verify that our original structure has not been modified
   701  	diffHash2 := checksumStruct(t, diff)
   702  	if diffHash != diffHash2 {
   703  		t.Fatal("diff has been modified")
   704  	}
   705  }
   706  
   707  func TestGraphAddDiff_module(t *testing.T) {
   708  	m := testModule(t, "graph-diff-module")
   709  	diff := &Diff{
   710  		Modules: []*ModuleDiff{
   711  			&ModuleDiff{
   712  				Path: rootModulePath,
   713  				Resources: map[string]*InstanceDiff{
   714  					"aws_instance.foo": &InstanceDiff{
   715  						Destroy: true,
   716  					},
   717  				},
   718  			},
   719  		},
   720  	}
   721  
   722  	g, err := Graph(&GraphOpts{Module: m, Diff: diff})
   723  	if err != nil {
   724  		t.Fatalf("err: %s", err)
   725  	}
   726  
   727  	actual := strings.TrimSpace(g.String())
   728  	expected := strings.TrimSpace(testTerraformGraphDiffModuleStr)
   729  	if actual != expected {
   730  		t.Fatalf("bad:\n\n%s", actual)
   731  	}
   732  }
   733  
   734  func TestGraphAddDiff_module_depends(t *testing.T) {
   735  	m := testModule(t, "graph-diff-module-dep")
   736  	diff := &Diff{
   737  		Modules: []*ModuleDiff{
   738  			&ModuleDiff{
   739  				Path: rootModulePath,
   740  				Resources: map[string]*InstanceDiff{
   741  					"aws_instance.foo": &InstanceDiff{
   742  						Destroy: true,
   743  					},
   744  				},
   745  			},
   746  			&ModuleDiff{
   747  				Path:    []string{"root", "child"},
   748  				Destroy: true,
   749  				Resources: map[string]*InstanceDiff{
   750  					"aws_instance.foo": &InstanceDiff{
   751  						Destroy: true,
   752  					},
   753  				},
   754  			},
   755  		},
   756  	}
   757  	state := &State{
   758  		Modules: []*ModuleState{
   759  			&ModuleState{
   760  				Path: []string{"root", "orphan"},
   761  				Resources: map[string]*ResourceState{
   762  					"aws_instance.dead": &ResourceState{
   763  						Type: "aws_instance",
   764  						Primary: &InstanceState{
   765  							ID: "dead",
   766  						},
   767  					},
   768  				},
   769  				Dependencies: []string{
   770  					"aws_instance.foo",
   771  					"module.child",
   772  				},
   773  			},
   774  		},
   775  	}
   776  
   777  	g, err := Graph(&GraphOpts{Module: m, Diff: diff, State: state})
   778  	if err != nil {
   779  		t.Fatalf("err: %s", err)
   780  	}
   781  
   782  	actual := strings.TrimSpace(g.String())
   783  	expected := strings.TrimSpace(testTerraformGraphDiffModuleDependsStr)
   784  	if actual != expected {
   785  		t.Fatalf("bad:\n\n%s", actual)
   786  	}
   787  }
   788  
   789  func TestGraphAddDiff_moduleDependsModule(t *testing.T) {
   790  	m := testModule(t, "graph-diff-module-dep-module")
   791  	diff := &Diff{
   792  		Modules: []*ModuleDiff{
   793  			&ModuleDiff{
   794  				Path:    []string{"root"},
   795  				Destroy: true,
   796  			},
   797  			&ModuleDiff{
   798  				Path:    []string{"root", "foo"},
   799  				Destroy: true,
   800  				Resources: map[string]*InstanceDiff{
   801  					"aws_instance.foo": &InstanceDiff{
   802  						Destroy: true,
   803  					},
   804  				},
   805  			},
   806  			&ModuleDiff{
   807  				Path:    []string{"root", "bar"},
   808  				Destroy: true,
   809  				Resources: map[string]*InstanceDiff{
   810  					"aws_instance.foo": &InstanceDiff{
   811  						Destroy: true,
   812  					},
   813  				},
   814  			},
   815  		},
   816  	}
   817  	state := &State{
   818  		Modules: []*ModuleState{
   819  			&ModuleState{
   820  				Path: []string{"root", "foo"},
   821  				Resources: map[string]*ResourceState{
   822  					"aws_instance.foo": &ResourceState{
   823  						Type: "aws_instance",
   824  						Primary: &InstanceState{
   825  							ID: "foo",
   826  						},
   827  					},
   828  				},
   829  			},
   830  
   831  			&ModuleState{
   832  				Path: []string{"root", "bar"},
   833  				Resources: map[string]*ResourceState{
   834  					"aws_instance.foo": &ResourceState{
   835  						Type: "aws_instance",
   836  						Primary: &InstanceState{
   837  							ID: "foo",
   838  						},
   839  					},
   840  				},
   841  				Dependencies: []string{
   842  					"module.foo",
   843  				},
   844  			},
   845  		},
   846  	}
   847  
   848  	g, err := Graph(&GraphOpts{Module: m, Diff: diff, State: state})
   849  	if err != nil {
   850  		t.Fatalf("err: %s", err)
   851  	}
   852  
   853  	actual := strings.TrimSpace(g.String())
   854  	expected := strings.TrimSpace(testTerraformGraphDiffModuleDependsModuleStr)
   855  	if actual != expected {
   856  		t.Fatalf("bad:\n\n%s", actual)
   857  	}
   858  }
   859  
   860  func TestGraphAddDiff_createBeforeDestroy(t *testing.T) {
   861  	m := testModule(t, "graph-diff-create-before")
   862  	diff := &Diff{
   863  		Modules: []*ModuleDiff{
   864  			&ModuleDiff{
   865  				Path: rootModulePath,
   866  				Resources: map[string]*InstanceDiff{
   867  					"aws_instance.bar": &InstanceDiff{
   868  						Destroy: true,
   869  						Attributes: map[string]*ResourceAttrDiff{
   870  							"ami": &ResourceAttrDiff{
   871  								Old:         "abc",
   872  								New:         "xyz",
   873  								RequiresNew: true,
   874  							},
   875  						},
   876  					},
   877  				},
   878  			},
   879  		},
   880  	}
   881  	state := &State{
   882  		Modules: []*ModuleState{
   883  			&ModuleState{
   884  				Path: rootModulePath,
   885  				Resources: map[string]*ResourceState{
   886  					"aws_instance.bar": &ResourceState{
   887  						Type: "aws_instance",
   888  						Primary: &InstanceState{
   889  							ID: "bar",
   890  							Attributes: map[string]string{
   891  								"ami": "abc",
   892  							},
   893  						},
   894  					},
   895  				},
   896  			},
   897  		},
   898  	}
   899  
   900  	diffHash := checksumStruct(t, diff)
   901  
   902  	g, err := Graph(&GraphOpts{
   903  		Module: m,
   904  		Diff:   diff,
   905  		State:  state,
   906  	})
   907  	if err != nil {
   908  		t.Fatalf("err: %s", err)
   909  	}
   910  
   911  	actual := strings.TrimSpace(g.String())
   912  	expected := strings.TrimSpace(testTerraformGraphDiffCreateBeforeDestroyStr)
   913  	if actual != expected {
   914  		t.Fatalf("bad:\n\n%s\n\nexpected:\n\n%s", actual, expected)
   915  	}
   916  
   917  	// Verify the flags are set
   918  	r := g.Noun("aws_instance.bar")
   919  	if r.Meta.(*GraphNodeResource).Resource.Flags&FlagReplacePrimary == 0 {
   920  		t.Fatalf("missing FlagReplacePrimary")
   921  	}
   922  
   923  	r = g.Noun("aws_instance.bar (destroy)")
   924  	if r.Meta.(*GraphNodeResource).Resource.Flags&FlagDeposed == 0 {
   925  		t.Fatalf("missing FlagDeposed")
   926  	}
   927  
   928  	// Verify that our original structure has not been modified
   929  	diffHash2 := checksumStruct(t, diff)
   930  	if diffHash != diffHash2 {
   931  		t.Fatal("diff has been modified")
   932  	}
   933  }
   934  
   935  func TestGraphAddDiff_moduleDestroy(t *testing.T) {
   936  	m := testModule(t, "graph-diff-module")
   937  	diff := &Diff{
   938  		Modules: []*ModuleDiff{
   939  			&ModuleDiff{
   940  				Path: rootModulePath,
   941  				Resources: map[string]*InstanceDiff{
   942  					"aws_instance.foo": &InstanceDiff{
   943  						Destroy: true,
   944  					},
   945  				},
   946  			},
   947  			&ModuleDiff{
   948  				Path: []string{"root", "child"},
   949  				Resources: map[string]*InstanceDiff{
   950  					"aws_instance.foo": &InstanceDiff{
   951  						Destroy: true,
   952  					},
   953  				},
   954  			},
   955  		},
   956  	}
   957  
   958  	g, err := Graph(&GraphOpts{Module: m, Diff: diff})
   959  	if err != nil {
   960  		t.Fatalf("err: %s", err)
   961  	}
   962  
   963  	actual := strings.TrimSpace(g.String())
   964  	expected := strings.TrimSpace(testTerraformGraphDiffModuleStr)
   965  	if actual != expected {
   966  		t.Fatalf("bad:\n\n%s", actual)
   967  	}
   968  }
   969  
   970  func TestGraphEncodeDependencies(t *testing.T) {
   971  	m := testModule(t, "graph-basic")
   972  
   973  	g, err := Graph(&GraphOpts{Module: m})
   974  	if err != nil {
   975  		t.Fatalf("err: %s", err)
   976  	}
   977  
   978  	// This should encode the dependency information into the state
   979  	graphEncodeDependencies(g)
   980  
   981  	web := g.Noun("aws_instance.web").Meta.(*GraphNodeResource).Resource
   982  	if len(web.Dependencies) != 1 || web.Dependencies[0] != "aws_security_group.firewall" {
   983  		t.Fatalf("bad: %#v", web)
   984  	}
   985  
   986  	weblb := g.Noun("aws_load_balancer.weblb").Meta.(*GraphNodeResource).Resource
   987  	if len(weblb.Dependencies) != 1 || weblb.Dependencies[0] != "aws_instance.web" {
   988  		t.Fatalf("bad: %#v", weblb)
   989  	}
   990  }
   991  
   992  func TestGraphEncodeDependencies_count(t *testing.T) {
   993  	m := testModule(t, "graph-count")
   994  	state := &State{
   995  		Modules: []*ModuleState{
   996  			&ModuleState{
   997  				Path: rootModulePath,
   998  				Resources: map[string]*ResourceState{
   999  					"aws_instance.web.0": &ResourceState{
  1000  						Type: "aws_instance",
  1001  						Primary: &InstanceState{
  1002  							ID: "foo",
  1003  						},
  1004  					},
  1005  					"aws_load_balancer.weblb": &ResourceState{
  1006  						Type: "aws_load_balancer",
  1007  						Primary: &InstanceState{
  1008  							ID: "foo",
  1009  						},
  1010  					},
  1011  				},
  1012  			},
  1013  		},
  1014  	}
  1015  
  1016  	g, err := Graph(&GraphOpts{Module: m, State: state})
  1017  	if err != nil {
  1018  		t.Fatalf("err: %s", err)
  1019  	}
  1020  
  1021  	// This should encode the dependency information into the state
  1022  	graphEncodeDependencies(g)
  1023  
  1024  	web := g.Noun("aws_instance.web").Meta.(*GraphNodeResource).Resource
  1025  	if len(web.Dependencies) != 0 {
  1026  		t.Fatalf("bad: %#v", web)
  1027  	}
  1028  
  1029  	weblb := g.Noun("aws_load_balancer.weblb").Meta.(*GraphNodeResource).Resource
  1030  	if len(weblb.Dependencies) != 1 {
  1031  		t.Fatalf("bad: %#v", weblb)
  1032  	}
  1033  }
  1034  
  1035  func TestGraphEncodeDependencies_module(t *testing.T) {
  1036  	m := testModule(t, "graph-modules")
  1037  
  1038  	g, err := Graph(&GraphOpts{Module: m, State: &State{}})
  1039  	if err != nil {
  1040  		t.Fatalf("err: %s", err)
  1041  	}
  1042  
  1043  	// This should encode the dependency information into the state
  1044  	graphEncodeDependencies(g)
  1045  
  1046  	web := g.Noun("aws_instance.web").Meta.(*GraphNodeResource).Resource
  1047  	sort.Strings(web.Dependencies)
  1048  	if len(web.Dependencies) != 2 {
  1049  		t.Fatalf("bad: %#v", web)
  1050  	}
  1051  	if web.Dependencies[0] != "aws_security_group.firewall" {
  1052  		t.Fatalf("bad: %#v", web)
  1053  	}
  1054  	if web.Dependencies[1] != "module.consul" {
  1055  		t.Fatalf("bad: %#v", web)
  1056  	}
  1057  
  1058  	mod := g.Noun("module.consul").Meta.(*GraphNodeModule)
  1059  	deps := mod.State.Dependencies
  1060  	if len(deps) != 1 {
  1061  		t.Fatalf("Bad: %#v", deps)
  1062  	}
  1063  	if deps[0] != "aws_security_group.firewall" {
  1064  		t.Fatalf("Bad: %#v", deps)
  1065  	}
  1066  }
  1067  
  1068  func TestGraph_orphan_dependencies(t *testing.T) {
  1069  	m := testModule(t, "graph-count")
  1070  	state := &State{
  1071  		Modules: []*ModuleState{
  1072  			&ModuleState{
  1073  				Path: rootModulePath,
  1074  
  1075  				Resources: map[string]*ResourceState{
  1076  					"aws_instance.web.0": &ResourceState{
  1077  						Type: "aws_instance",
  1078  						Primary: &InstanceState{
  1079  							ID: "foo",
  1080  						},
  1081  					},
  1082  					"aws_instance.web.1": &ResourceState{
  1083  						Type: "aws_instance",
  1084  						Primary: &InstanceState{
  1085  							ID: "foo",
  1086  						},
  1087  					},
  1088  					"aws_load_balancer.old": &ResourceState{
  1089  						Type: "aws_load_balancer",
  1090  						Primary: &InstanceState{
  1091  							ID: "foo",
  1092  						},
  1093  						Dependencies: []string{
  1094  							"aws_instance.web.0",
  1095  							"aws_instance.web.1",
  1096  							"aws_instance.web.2",
  1097  						},
  1098  					},
  1099  				},
  1100  			},
  1101  		},
  1102  	}
  1103  
  1104  	g, err := Graph(&GraphOpts{Module: m, State: state})
  1105  	if err != nil {
  1106  		t.Fatalf("err: %s", err)
  1107  	}
  1108  
  1109  	actual := strings.TrimSpace(g.String())
  1110  	expected := strings.TrimSpace(testTerraformGraphCountOrphanStr)
  1111  	if actual != expected {
  1112  		t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected)
  1113  	}
  1114  }
  1115  
  1116  func TestGraph_orphanDependenciesModules(t *testing.T) {
  1117  	m := testModule(t, "graph-modules")
  1118  	state := &State{
  1119  		Modules: []*ModuleState{
  1120  			&ModuleState{
  1121  				Path: rootModulePath,
  1122  
  1123  				Resources: map[string]*ResourceState{
  1124  					"aws_instance.foo": &ResourceState{
  1125  						Type: "aws_instance",
  1126  						Primary: &InstanceState{
  1127  							ID: "foo",
  1128  						},
  1129  						Dependencies: []string{
  1130  							"module.consul",
  1131  						},
  1132  					},
  1133  				},
  1134  			},
  1135  		},
  1136  	}
  1137  
  1138  	g, err := Graph(&GraphOpts{Module: m, State: state})
  1139  	if err != nil {
  1140  		t.Fatalf("err: %s", err)
  1141  	}
  1142  
  1143  	actual := strings.TrimSpace(g.String())
  1144  	expected := strings.TrimSpace(testTerraformGraphOrphanModuleDepsStr)
  1145  	if actual != expected {
  1146  		t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected)
  1147  	}
  1148  }
  1149  
  1150  func TestGraph_orphanModules_Dependencies(t *testing.T) {
  1151  	m := testModule(t, "graph-modules")
  1152  	state := &State{
  1153  		Modules: []*ModuleState{
  1154  			&ModuleState{
  1155  				Path: rootModulePath,
  1156  
  1157  				Resources: map[string]*ResourceState{
  1158  					"aws_instance.foo": &ResourceState{
  1159  						Type: "aws_instance",
  1160  						Primary: &InstanceState{
  1161  							ID: "foo",
  1162  						},
  1163  						Dependencies: []string{
  1164  							"module.consul",
  1165  						},
  1166  					},
  1167  				},
  1168  			},
  1169  
  1170  			// Add an orphan module
  1171  			&ModuleState{
  1172  				Path: []string{"root", "orphan"},
  1173  				Resources: map[string]*ResourceState{
  1174  					"aws_instance.bar": &ResourceState{
  1175  						Type: "aws_instance",
  1176  						Primary: &InstanceState{
  1177  							ID: "bar",
  1178  						},
  1179  					},
  1180  				},
  1181  				Dependencies: []string{
  1182  					"aws_instance.foo",
  1183  					"aws_instance.web",
  1184  				},
  1185  			},
  1186  		},
  1187  	}
  1188  
  1189  	g, err := Graph(&GraphOpts{Module: m, State: state})
  1190  	if err != nil {
  1191  		t.Fatalf("err: %s", err)
  1192  	}
  1193  
  1194  	actual := strings.TrimSpace(g.String())
  1195  	expected := strings.TrimSpace(testTerraformGraphOrphanedModuleDepsStr)
  1196  	if actual != expected {
  1197  		t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected)
  1198  	}
  1199  }
  1200  
  1201  func TestGraphNodeResourceExpand(t *testing.T) {
  1202  	m := testModule(t, "graph-resource-expand")
  1203  
  1204  	g, err := Graph(&GraphOpts{Module: m})
  1205  	if err != nil {
  1206  		t.Fatalf("err: %s", err)
  1207  	}
  1208  
  1209  	// Get the resource we care about expanding
  1210  	n := g.Noun("aws_instance.web")
  1211  	if n == nil {
  1212  		t.Fatal("could not find")
  1213  	}
  1214  	rn := n.Meta.(*GraphNodeResource)
  1215  
  1216  	g, err = rn.Expand()
  1217  	if err != nil {
  1218  		t.Fatalf("err: %s", err)
  1219  	}
  1220  
  1221  	actual := strings.TrimSpace(g.String())
  1222  	expected := strings.TrimSpace(testTerraformGraphResourceExpandStr)
  1223  	if actual != expected {
  1224  		t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected)
  1225  	}
  1226  }
  1227  
  1228  func TestGraphNodeResourceExpand_provDeps(t *testing.T) {
  1229  	m := testModule(t, "graph-resource-expand-prov-deps")
  1230  	provs := map[string]ResourceProvisionerFactory{
  1231  		"remote-exec": func() (ResourceProvisioner, error) {
  1232  			return new(MockResourceProvisioner), nil
  1233  		},
  1234  	}
  1235  
  1236  	g, err := Graph(&GraphOpts{Module: m, Provisioners: provs})
  1237  	if err != nil {
  1238  		t.Fatalf("err: %s", err)
  1239  	}
  1240  
  1241  	// Get the resource we care about expanding
  1242  	n := g.Noun("aws_instance.web")
  1243  	if n == nil {
  1244  		t.Fatal("could not find")
  1245  	}
  1246  	rn := n.Meta.(*GraphNodeResource)
  1247  
  1248  	g, err = rn.Expand()
  1249  	if err != nil {
  1250  		t.Fatalf("err: %s", err)
  1251  	}
  1252  
  1253  	actual := strings.TrimSpace(g.String())
  1254  	expected := strings.TrimSpace(testTerraformGraphResourceExpandProvDepsStr)
  1255  	if actual != expected {
  1256  		t.Fatalf("bad:\n\nactual:\n%s\n\nexpected:\n%s", actual, expected)
  1257  	}
  1258  }
  1259  
  1260  const testTerraformGraphStr = `
  1261  root: root
  1262  aws_instance.web
  1263    aws_instance.web -> aws_security_group.firewall
  1264    aws_instance.web -> provider.aws
  1265  aws_load_balancer.weblb
  1266    aws_load_balancer.weblb -> aws_instance.web
  1267    aws_load_balancer.weblb -> provider.aws
  1268  aws_security_group.firewall
  1269    aws_security_group.firewall -> provider.aws
  1270  openstack_floating_ip.random
  1271  provider.aws
  1272    provider.aws -> openstack_floating_ip.random
  1273  root
  1274    root -> aws_instance.web
  1275    root -> aws_load_balancer.weblb
  1276    root -> aws_security_group.firewall
  1277    root -> openstack_floating_ip.random
  1278  `
  1279  
  1280  const testTerraformGraphCountStr = `
  1281  root: root
  1282  aws_instance.web
  1283  aws_load_balancer.weblb
  1284    aws_load_balancer.weblb -> aws_instance.web
  1285  root
  1286    root -> aws_instance.web
  1287    root -> aws_load_balancer.weblb
  1288  `
  1289  
  1290  const testTerraformGraphCountTaintedStr = `
  1291  root: root
  1292  aws_instance.web
  1293    aws_instance.web -> aws_instance.web.0 (tainted #1)
  1294  aws_instance.web.0 (tainted #1)
  1295  aws_load_balancer.weblb
  1296    aws_load_balancer.weblb -> aws_instance.web
  1297  root
  1298    root -> aws_instance.web
  1299    root -> aws_instance.web.0 (tainted #1)
  1300    root -> aws_load_balancer.weblb
  1301  `
  1302  
  1303  const testTerraformGraphCountVarResourceStr = `
  1304  root: root
  1305  aws_instance.foo
  1306  aws_instance.web
  1307    aws_instance.web -> aws_instance.foo
  1308  aws_load_balancer.weblb
  1309    aws_load_balancer.weblb -> aws_instance.web
  1310  root
  1311    root -> aws_instance.foo
  1312    root -> aws_instance.web
  1313    root -> aws_load_balancer.weblb
  1314  `
  1315  
  1316  const testTerraformGraphDependsStr = `
  1317  root: root
  1318  aws_instance.db
  1319    aws_instance.db -> aws_instance.web
  1320  aws_instance.web
  1321  root
  1322    root -> aws_instance.db
  1323    root -> aws_instance.web
  1324  `
  1325  
  1326  const testTerraformGraphDependsCountStr = `
  1327  root: root
  1328  aws_instance.db
  1329    aws_instance.db -> aws_instance.web
  1330  aws_instance.web
  1331  root
  1332    root -> aws_instance.db
  1333    root -> aws_instance.web
  1334  `
  1335  
  1336  const testTerraformGraphDependsOrphanStr = `
  1337  root: root
  1338  aws_instance.db
  1339    aws_instance.db -> aws_instance.web
  1340  aws_instance.old
  1341  aws_instance.web
  1342  root
  1343    root -> aws_instance.db
  1344    root -> aws_instance.old
  1345    root -> aws_instance.web
  1346  `
  1347  
  1348  const testTerraformGraphDiffStr = `
  1349  root: root
  1350  aws_instance.foo
  1351  root
  1352    root -> aws_instance.foo
  1353  `
  1354  
  1355  const testTerraformGraphDiffDestroyStr = `
  1356  root: root
  1357  aws_instance.bar
  1358    aws_instance.bar -> aws_instance.bar (destroy)
  1359    aws_instance.bar -> aws_instance.foo
  1360    aws_instance.bar -> provider.aws
  1361  aws_instance.bar (destroy)
  1362    aws_instance.bar (destroy) -> provider.aws
  1363  aws_instance.foo
  1364    aws_instance.foo -> aws_instance.foo (destroy)
  1365    aws_instance.foo -> provider.aws
  1366  aws_instance.foo (destroy)
  1367    aws_instance.foo (destroy) -> aws_instance.bar (destroy)
  1368    aws_instance.foo (destroy) -> provider.aws
  1369  provider.aws
  1370  root
  1371    root -> aws_instance.bar
  1372    root -> aws_instance.foo
  1373  `
  1374  
  1375  const testTerraformGraphDiffDestroyCountsStr = `
  1376  root: root
  1377  aws_instance.web
  1378    aws_instance.web -> aws_instance.web (destroy)
  1379  aws_instance.web (destroy)
  1380    aws_instance.web (destroy) -> aws_load_balancer.weblb (destroy)
  1381  aws_load_balancer.weblb
  1382    aws_load_balancer.weblb -> aws_instance.web
  1383    aws_load_balancer.weblb -> aws_load_balancer.weblb (destroy)
  1384  aws_load_balancer.weblb (destroy)
  1385  root
  1386    root -> aws_instance.web
  1387    root -> aws_load_balancer.weblb
  1388  `
  1389  
  1390  const testTerraformGraphDiffModuleStr = `
  1391  root: root
  1392  aws_instance.foo
  1393    aws_instance.foo -> aws_instance.foo (destroy)
  1394    aws_instance.foo -> module.child
  1395  aws_instance.foo (destroy)
  1396  module.child
  1397    module.child -> aws_instance.foo (destroy)
  1398  root
  1399    root -> aws_instance.foo
  1400    root -> module.child
  1401  `
  1402  
  1403  const testTerraformGraphDiffModuleDependsStr = `
  1404  root: root
  1405  aws_instance.foo
  1406    aws_instance.foo -> aws_instance.foo (destroy)
  1407  aws_instance.foo (destroy)
  1408    aws_instance.foo (destroy) -> module.child
  1409    aws_instance.foo (destroy) -> module.orphan
  1410  module.child
  1411    module.child -> module.orphan
  1412  module.orphan
  1413  root
  1414    root -> aws_instance.foo
  1415    root -> module.child
  1416    root -> module.orphan
  1417  `
  1418  
  1419  const testTerraformGraphDiffModuleDependsModuleStr = `
  1420  root: root
  1421  module.bar
  1422  module.foo
  1423    module.foo -> module.bar
  1424  root
  1425    root -> module.bar
  1426    root -> module.foo
  1427  `
  1428  
  1429  const testTerraformGraphModulesStr = `
  1430  root: root
  1431  aws_instance.web
  1432    aws_instance.web -> aws_security_group.firewall
  1433    aws_instance.web -> module.consul
  1434    aws_instance.web -> provider.aws
  1435  aws_security_group.firewall
  1436    aws_security_group.firewall -> provider.aws
  1437  module.consul
  1438    module.consul -> aws_security_group.firewall
  1439    module.consul -> provider.aws
  1440  provider.aws
  1441  root
  1442    root -> aws_instance.web
  1443    root -> aws_security_group.firewall
  1444    root -> module.consul
  1445  `
  1446  
  1447  const testTerraformGraphModulesConsulStr = `
  1448  root: root
  1449  aws_instance.server
  1450    aws_instance.server -> provider.aws
  1451  provider.aws
  1452  root
  1453    root -> aws_instance.server
  1454  `
  1455  
  1456  const testTerraformGraphModuleOrphanStr = `
  1457  root: root
  1458  aws_instance.web
  1459    aws_instance.web -> aws_security_group.firewall
  1460    aws_instance.web -> provider.aws
  1461  aws_security_group.firewall
  1462    aws_security_group.firewall -> provider.aws
  1463  module.consul
  1464    module.consul -> provider.aws
  1465  provider.aws
  1466  root
  1467    root -> aws_instance.web
  1468    root -> aws_security_group.firewall
  1469    root -> module.consul
  1470  `
  1471  
  1472  const testTerraformGraphModuleOrphanConsulStr = `
  1473  root: root
  1474  aws_instance.old
  1475    aws_instance.old -> provider.aws
  1476  provider.aws
  1477  root
  1478    root -> aws_instance.old
  1479  `
  1480  
  1481  const testTerraformGraphProviderPruneStr = `
  1482  root: root
  1483  aws_load_balancer.weblb
  1484    aws_load_balancer.weblb -> provider.aws
  1485  provider.aws
  1486  root
  1487    root -> aws_load_balancer.weblb`
  1488  
  1489  const testTerraformGraphDiffCreateBeforeDestroyStr = `
  1490  root: root
  1491  aws_instance.bar
  1492    aws_instance.bar -> provider.aws
  1493  aws_instance.bar (destroy)
  1494    aws_instance.bar (destroy) -> aws_instance.bar
  1495    aws_instance.bar (destroy) -> provider.aws
  1496  provider.aws
  1497  root
  1498    root -> aws_instance.bar
  1499    root -> aws_instance.bar (destroy)`
  1500  
  1501  const testTerraformGraphStateStr = `
  1502  root: root
  1503  aws_instance.old
  1504    aws_instance.old -> provider.aws
  1505  aws_instance.web
  1506    aws_instance.web -> aws_security_group.firewall
  1507    aws_instance.web -> provider.aws
  1508  aws_load_balancer.weblb
  1509    aws_load_balancer.weblb -> aws_instance.web
  1510    aws_load_balancer.weblb -> provider.aws
  1511  aws_security_group.firewall
  1512    aws_security_group.firewall -> provider.aws
  1513  openstack_floating_ip.random
  1514  provider.aws
  1515    provider.aws -> openstack_floating_ip.random
  1516  root
  1517    root -> aws_instance.old
  1518    root -> aws_instance.web
  1519    root -> aws_load_balancer.weblb
  1520    root -> aws_security_group.firewall
  1521    root -> openstack_floating_ip.random
  1522  `
  1523  
  1524  const testTerraformGraphTaintedStr = `
  1525  root: root
  1526  aws_instance.web
  1527    aws_instance.web -> aws_instance.web (tainted #1)
  1528    aws_instance.web -> aws_security_group.firewall
  1529    aws_instance.web -> provider.aws
  1530  aws_instance.web (tainted #1)
  1531    aws_instance.web (tainted #1) -> provider.aws
  1532  aws_security_group.firewall
  1533    aws_security_group.firewall -> provider.aws
  1534  provider.aws
  1535  root
  1536    root -> aws_instance.web
  1537    root -> aws_instance.web (tainted #1)
  1538    root -> aws_security_group.firewall
  1539  `
  1540  
  1541  const testTerraformGraphTaintedMultiStr = `
  1542  root: root
  1543  aws_instance.web
  1544    aws_instance.web -> aws_instance.web (tainted #1)
  1545    aws_instance.web -> aws_instance.web (tainted #2)
  1546    aws_instance.web -> aws_security_group.firewall
  1547    aws_instance.web -> provider.aws
  1548  aws_instance.web (tainted #1)
  1549    aws_instance.web (tainted #1) -> provider.aws
  1550  aws_instance.web (tainted #2)
  1551    aws_instance.web (tainted #2) -> provider.aws
  1552  aws_security_group.firewall
  1553    aws_security_group.firewall -> provider.aws
  1554  provider.aws
  1555  root
  1556    root -> aws_instance.web
  1557    root -> aws_instance.web (tainted #1)
  1558    root -> aws_instance.web (tainted #2)
  1559    root -> aws_security_group.firewall
  1560  `
  1561  
  1562  const testTerraformGraphCountOrphanStr = `
  1563  root: root
  1564  aws_instance.web
  1565  aws_load_balancer.old
  1566    aws_load_balancer.old -> aws_instance.web
  1567  aws_load_balancer.weblb
  1568    aws_load_balancer.weblb -> aws_instance.web
  1569  root
  1570    root -> aws_instance.web
  1571    root -> aws_load_balancer.old
  1572    root -> aws_load_balancer.weblb
  1573  `
  1574  
  1575  const testTerraformGraphOrphanModuleDepsStr = `
  1576  root: root
  1577  aws_instance.foo
  1578    aws_instance.foo -> module.consul
  1579    aws_instance.foo -> provider.aws
  1580  aws_instance.web
  1581    aws_instance.web -> aws_security_group.firewall
  1582    aws_instance.web -> module.consul
  1583    aws_instance.web -> provider.aws
  1584  aws_security_group.firewall
  1585    aws_security_group.firewall -> provider.aws
  1586  module.consul
  1587    module.consul -> aws_security_group.firewall
  1588    module.consul -> provider.aws
  1589  provider.aws
  1590  root
  1591    root -> aws_instance.foo
  1592    root -> aws_instance.web
  1593    root -> aws_security_group.firewall
  1594    root -> module.consul
  1595  `
  1596  
  1597  const testTerraformGraphOrphanedModuleDepsStr = `
  1598  root: root
  1599  aws_instance.foo
  1600    aws_instance.foo -> module.consul
  1601    aws_instance.foo -> provider.aws
  1602  aws_instance.web
  1603    aws_instance.web -> aws_security_group.firewall
  1604    aws_instance.web -> module.consul
  1605    aws_instance.web -> provider.aws
  1606  aws_security_group.firewall
  1607    aws_security_group.firewall -> provider.aws
  1608  module.consul
  1609    module.consul -> aws_security_group.firewall
  1610    module.consul -> provider.aws
  1611  module.orphan
  1612    module.orphan -> aws_instance.foo
  1613    module.orphan -> aws_instance.web
  1614    module.orphan -> provider.aws
  1615  provider.aws
  1616  root
  1617    root -> aws_instance.foo
  1618    root -> aws_instance.web
  1619    root -> aws_security_group.firewall
  1620    root -> module.consul
  1621    root -> module.orphan
  1622  `
  1623  
  1624  const testTerraformGraphResourceExpandStr = `
  1625  root: root
  1626  aws_instance.web.0
  1627  aws_instance.web.1
  1628  aws_instance.web.2
  1629  root
  1630    root -> aws_instance.web.0
  1631    root -> aws_instance.web.1
  1632    root -> aws_instance.web.2
  1633  `
  1634  
  1635  const testTerraformGraphResourceExpandProvDepsStr = `
  1636  root: root
  1637  aws_instance.web.0
  1638  aws_instance.web.1
  1639    aws_instance.web.1 -> aws_instance.web.0
  1640  aws_instance.web.2
  1641    aws_instance.web.2 -> aws_instance.web.0
  1642  root
  1643    root -> aws_instance.web.0
  1644    root -> aws_instance.web.1
  1645    root -> aws_instance.web.2
  1646  `