github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/jsonformat/state_test.go (about)

     1  package jsonformat
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/google/go-cmp/cmp"
     8  	"github.com/mitchellh/colorstring"
     9  
    10  	"github.com/hashicorp/terraform/internal/command/format"
    11  	"github.com/hashicorp/terraform/internal/command/jsonprovider"
    12  	"github.com/hashicorp/terraform/internal/command/jsonstate"
    13  	"github.com/hashicorp/terraform/internal/states/statefile"
    14  	"github.com/hashicorp/terraform/internal/terminal"
    15  
    16  	"github.com/zclconf/go-cty/cty"
    17  
    18  	"github.com/hashicorp/terraform/internal/addrs"
    19  	"github.com/hashicorp/terraform/internal/configs/configschema"
    20  	"github.com/hashicorp/terraform/internal/providers"
    21  	"github.com/hashicorp/terraform/internal/states"
    22  	"github.com/hashicorp/terraform/internal/terraform"
    23  )
    24  
    25  func TestState(t *testing.T) {
    26  	color := &colorstring.Colorize{Colors: colorstring.DefaultColors, Disable: true}
    27  
    28  	tests := []struct {
    29  		State *format.StateOpts
    30  		Want  string
    31  	}{
    32  		{
    33  			&format.StateOpts{
    34  				State:   &states.State{},
    35  				Color:   color,
    36  				Schemas: &terraform.Schemas{},
    37  			},
    38  			"The state file is empty. No resources are represented.\n",
    39  		},
    40  		{
    41  			&format.StateOpts{
    42  				State:   basicState(t),
    43  				Color:   color,
    44  				Schemas: testSchemas(),
    45  			},
    46  			basicStateOutput,
    47  		},
    48  		{
    49  			&format.StateOpts{
    50  				State:   nestedState(t),
    51  				Color:   color,
    52  				Schemas: testSchemas(),
    53  			},
    54  			nestedStateOutput,
    55  		},
    56  		{
    57  			&format.StateOpts{
    58  				State:   deposedState(t),
    59  				Color:   color,
    60  				Schemas: testSchemas(),
    61  			},
    62  			deposedNestedStateOutput,
    63  		},
    64  		{
    65  			&format.StateOpts{
    66  				State:   onlyDeposedState(t),
    67  				Color:   color,
    68  				Schemas: testSchemas(),
    69  			},
    70  			onlyDeposedOutput,
    71  		},
    72  		{
    73  			&format.StateOpts{
    74  				State:   stateWithMoreOutputs(t),
    75  				Color:   color,
    76  				Schemas: testSchemas(),
    77  			},
    78  			stateWithMoreOutputsOutput,
    79  		},
    80  	}
    81  
    82  	for i, tt := range tests {
    83  		t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
    84  
    85  			root, outputs, err := jsonstate.MarshalForRenderer(&statefile.File{
    86  				State: tt.State.State,
    87  			}, tt.State.Schemas)
    88  
    89  			if err != nil {
    90  				t.Errorf("found err: %v", err)
    91  				return
    92  			}
    93  
    94  			streams, done := terminal.StreamsForTesting(t)
    95  			renderer := Renderer{
    96  				Colorize: color,
    97  				Streams:  streams,
    98  			}
    99  
   100  			renderer.RenderHumanState(State{
   101  				StateFormatVersion:    jsonstate.FormatVersion,
   102  				RootModule:            root,
   103  				RootModuleOutputs:     outputs,
   104  				ProviderFormatVersion: jsonprovider.FormatVersion,
   105  				ProviderSchemas:       jsonprovider.MarshalForRenderer(tt.State.Schemas),
   106  			})
   107  
   108  			result := done(t).All()
   109  			if diff := cmp.Diff(result, tt.Want); diff != "" {
   110  				t.Errorf("wrong output\nexpected:\n%s\nactual:\n%s\ndiff:\n%s\n", tt.Want, result, diff)
   111  			}
   112  		})
   113  	}
   114  }
   115  
   116  func testProvider() *terraform.MockProvider {
   117  	p := new(terraform.MockProvider)
   118  	p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
   119  		return providers.ReadResourceResponse{NewState: req.PriorState}
   120  	}
   121  
   122  	p.GetProviderSchemaResponse = testProviderSchema()
   123  
   124  	return p
   125  }
   126  
   127  func testProviderSchema() *providers.GetProviderSchemaResponse {
   128  	return &providers.GetProviderSchemaResponse{
   129  		Provider: providers.Schema{
   130  			Block: &configschema.Block{
   131  				Attributes: map[string]*configschema.Attribute{
   132  					"region": {Type: cty.String, Optional: true},
   133  				},
   134  			},
   135  		},
   136  		ResourceTypes: map[string]providers.Schema{
   137  			"test_resource": {
   138  				Block: &configschema.Block{
   139  					Attributes: map[string]*configschema.Attribute{
   140  						"id":      {Type: cty.String, Computed: true},
   141  						"foo":     {Type: cty.String, Optional: true},
   142  						"woozles": {Type: cty.String, Optional: true},
   143  					},
   144  					BlockTypes: map[string]*configschema.NestedBlock{
   145  						"nested": {
   146  							Nesting: configschema.NestingList,
   147  							Block: configschema.Block{
   148  								Attributes: map[string]*configschema.Attribute{
   149  									"compute": {Type: cty.String, Optional: true},
   150  									"value":   {Type: cty.String, Optional: true},
   151  								},
   152  							},
   153  						},
   154  					},
   155  				},
   156  			},
   157  		},
   158  		DataSources: map[string]providers.Schema{
   159  			"test_data_source": {
   160  				Block: &configschema.Block{
   161  					Attributes: map[string]*configschema.Attribute{
   162  						"compute": {Type: cty.String, Optional: true},
   163  						"value":   {Type: cty.String, Computed: true},
   164  					},
   165  				},
   166  			},
   167  		},
   168  	}
   169  }
   170  
   171  func testSchemas() *terraform.Schemas {
   172  	provider := testProvider()
   173  	return &terraform.Schemas{
   174  		Providers: map[addrs.Provider]*terraform.ProviderSchema{
   175  			addrs.NewDefaultProvider("test"): provider.ProviderSchema(),
   176  		},
   177  	}
   178  }
   179  
   180  const basicStateOutput = `# data.test_data_source.data:
   181  data "test_data_source" "data" {
   182      compute = "sure"
   183  }
   184  
   185  # test_resource.baz[0]:
   186  resource "test_resource" "baz" {
   187      woozles = "confuzles"
   188  }
   189  
   190  
   191  Outputs:
   192  
   193  bar = "bar value"
   194  `
   195  
   196  const nestedStateOutput = `# test_resource.baz[0]:
   197  resource "test_resource" "baz" {
   198      woozles = "confuzles"
   199  
   200      nested {
   201          value = "42"
   202      }
   203  }
   204  `
   205  
   206  const deposedNestedStateOutput = `# test_resource.baz[0]:
   207  resource "test_resource" "baz" {
   208      woozles = "confuzles"
   209  
   210      nested {
   211          value = "42"
   212      }
   213  }
   214  
   215  # test_resource.baz[0]: (deposed object 1234)
   216  resource "test_resource" "baz" {
   217      woozles = "confuzles"
   218  
   219      nested {
   220          value = "42"
   221      }
   222  }
   223  `
   224  
   225  const onlyDeposedOutput = `# test_resource.baz[0]: (deposed object 1234)
   226  resource "test_resource" "baz" {
   227      woozles = "confuzles"
   228  
   229      nested {
   230          value = "42"
   231      }
   232  }
   233  
   234  # test_resource.baz[0]: (deposed object 5678)
   235  resource "test_resource" "baz" {
   236      woozles = "confuzles"
   237  
   238      nested {
   239          value = "42"
   240      }
   241  }
   242  `
   243  
   244  const stateWithMoreOutputsOutput = `# test_resource.baz[0]:
   245  resource "test_resource" "baz" {
   246      woozles = "confuzles"
   247  }
   248  
   249  
   250  Outputs:
   251  
   252  bool_var = true
   253  int_var = 42
   254  map_var = {
   255      "first"  = "foo"
   256      "second" = "bar"
   257  }
   258  sensitive_var = (sensitive value)
   259  string_var = "string value"
   260  `
   261  
   262  func basicState(t *testing.T) *states.State {
   263  	state := states.NewState()
   264  
   265  	rootModule := state.RootModule()
   266  	if rootModule == nil {
   267  		t.Errorf("root module is nil; want valid object")
   268  	}
   269  
   270  	rootModule.SetLocalValue("foo", cty.StringVal("foo value"))
   271  	rootModule.SetOutputValue("bar", cty.StringVal("bar value"), false)
   272  	rootModule.SetResourceInstanceCurrent(
   273  		addrs.Resource{
   274  			Mode: addrs.ManagedResourceMode,
   275  			Type: "test_resource",
   276  			Name: "baz",
   277  		}.Instance(addrs.IntKey(0)),
   278  		&states.ResourceInstanceObjectSrc{
   279  			Status:        states.ObjectReady,
   280  			SchemaVersion: 0,
   281  			AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   282  		},
   283  		addrs.AbsProviderConfig{
   284  			Provider: addrs.NewDefaultProvider("test"),
   285  			Module:   addrs.RootModule,
   286  		},
   287  	)
   288  	rootModule.SetResourceInstanceCurrent(
   289  		addrs.Resource{
   290  			Mode: addrs.DataResourceMode,
   291  			Type: "test_data_source",
   292  			Name: "data",
   293  		}.Instance(addrs.NoKey),
   294  		&states.ResourceInstanceObjectSrc{
   295  			Status:        states.ObjectReady,
   296  			SchemaVersion: 0,
   297  			AttrsJSON:     []byte(`{"compute":"sure"}`),
   298  		},
   299  		addrs.AbsProviderConfig{
   300  			Provider: addrs.NewDefaultProvider("test"),
   301  			Module:   addrs.RootModule,
   302  		},
   303  	)
   304  	return state
   305  }
   306  
   307  func stateWithMoreOutputs(t *testing.T) *states.State {
   308  	state := states.NewState()
   309  
   310  	rootModule := state.RootModule()
   311  	if rootModule == nil {
   312  		t.Errorf("root module is nil; want valid object")
   313  	}
   314  
   315  	rootModule.SetOutputValue("string_var", cty.StringVal("string value"), false)
   316  	rootModule.SetOutputValue("int_var", cty.NumberIntVal(42), false)
   317  	rootModule.SetOutputValue("bool_var", cty.BoolVal(true), false)
   318  	rootModule.SetOutputValue("sensitive_var", cty.StringVal("secret!!!"), true)
   319  	rootModule.SetOutputValue("map_var", cty.MapVal(map[string]cty.Value{
   320  		"first":  cty.StringVal("foo"),
   321  		"second": cty.StringVal("bar"),
   322  	}), false)
   323  
   324  	rootModule.SetResourceInstanceCurrent(
   325  		addrs.Resource{
   326  			Mode: addrs.ManagedResourceMode,
   327  			Type: "test_resource",
   328  			Name: "baz",
   329  		}.Instance(addrs.IntKey(0)),
   330  		&states.ResourceInstanceObjectSrc{
   331  			Status:        states.ObjectReady,
   332  			SchemaVersion: 0,
   333  			AttrsJSON:     []byte(`{"woozles":"confuzles"}`),
   334  		},
   335  		addrs.AbsProviderConfig{
   336  			Provider: addrs.NewDefaultProvider("test"),
   337  			Module:   addrs.RootModule,
   338  		},
   339  	)
   340  	return state
   341  }
   342  
   343  func nestedState(t *testing.T) *states.State {
   344  	state := states.NewState()
   345  
   346  	rootModule := state.RootModule()
   347  	if rootModule == nil {
   348  		t.Errorf("root module is nil; want valid object")
   349  	}
   350  
   351  	rootModule.SetResourceInstanceCurrent(
   352  		addrs.Resource{
   353  			Mode: addrs.ManagedResourceMode,
   354  			Type: "test_resource",
   355  			Name: "baz",
   356  		}.Instance(addrs.IntKey(0)),
   357  		&states.ResourceInstanceObjectSrc{
   358  			Status:        states.ObjectReady,
   359  			SchemaVersion: 0,
   360  			AttrsJSON:     []byte(`{"woozles":"confuzles","nested": [{"value": "42"}]}`),
   361  		},
   362  		addrs.AbsProviderConfig{
   363  			Provider: addrs.NewDefaultProvider("test"),
   364  			Module:   addrs.RootModule,
   365  		},
   366  	)
   367  	return state
   368  }
   369  
   370  func deposedState(t *testing.T) *states.State {
   371  	state := nestedState(t)
   372  	rootModule := state.RootModule()
   373  	rootModule.SetResourceInstanceDeposed(
   374  		addrs.Resource{
   375  			Mode: addrs.ManagedResourceMode,
   376  			Type: "test_resource",
   377  			Name: "baz",
   378  		}.Instance(addrs.IntKey(0)),
   379  		states.DeposedKey("1234"),
   380  		&states.ResourceInstanceObjectSrc{
   381  			Status:        states.ObjectReady,
   382  			SchemaVersion: 0,
   383  			AttrsJSON:     []byte(`{"woozles":"confuzles","nested": [{"value": "42"}]}`),
   384  		},
   385  		addrs.AbsProviderConfig{
   386  			Provider: addrs.NewDefaultProvider("test"),
   387  			Module:   addrs.RootModule,
   388  		},
   389  	)
   390  	return state
   391  }
   392  
   393  // replicate a corrupt resource where only a deposed exists
   394  func onlyDeposedState(t *testing.T) *states.State {
   395  	state := states.NewState()
   396  
   397  	rootModule := state.RootModule()
   398  	if rootModule == nil {
   399  		t.Errorf("root module is nil; want valid object")
   400  	}
   401  
   402  	rootModule.SetResourceInstanceDeposed(
   403  		addrs.Resource{
   404  			Mode: addrs.ManagedResourceMode,
   405  			Type: "test_resource",
   406  			Name: "baz",
   407  		}.Instance(addrs.IntKey(0)),
   408  		states.DeposedKey("1234"),
   409  		&states.ResourceInstanceObjectSrc{
   410  			Status:        states.ObjectReady,
   411  			SchemaVersion: 0,
   412  			AttrsJSON:     []byte(`{"woozles":"confuzles","nested": [{"value": "42"}]}`),
   413  		},
   414  		addrs.AbsProviderConfig{
   415  			Provider: addrs.NewDefaultProvider("test"),
   416  			Module:   addrs.RootModule,
   417  		},
   418  	)
   419  	rootModule.SetResourceInstanceDeposed(
   420  		addrs.Resource{
   421  			Mode: addrs.ManagedResourceMode,
   422  			Type: "test_resource",
   423  			Name: "baz",
   424  		}.Instance(addrs.IntKey(0)),
   425  		states.DeposedKey("5678"),
   426  		&states.ResourceInstanceObjectSrc{
   427  			Status:        states.ObjectReady,
   428  			SchemaVersion: 0,
   429  			AttrsJSON:     []byte(`{"woozles":"confuzles","nested": [{"value": "42"}]}`),
   430  		},
   431  		addrs.AbsProviderConfig{
   432  			Provider: addrs.NewDefaultProvider("test"),
   433  			Module:   addrs.RootModule,
   434  		},
   435  	)
   436  	return state
   437  }