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 }