github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/command/format/state_test.go (about)

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