github.com/iaas-resource-provision/iaas-rpc@v1.0.7-0.20211021023331-ed21f798c408/internal/plugin/grpc_provider_test.go (about) 1 package plugin 2 3 import ( 4 "bytes" 5 "fmt" 6 "testing" 7 8 "github.com/golang/mock/gomock" 9 "github.com/google/go-cmp/cmp" 10 "github.com/iaas-resource-provision/iaas-rpc/internal/configs/hcl2shim" 11 "github.com/iaas-resource-provision/iaas-rpc/internal/providers" 12 "github.com/iaas-resource-provision/iaas-rpc/internal/tfdiags" 13 "github.com/zclconf/go-cty/cty" 14 15 mockproto "github.com/iaas-resource-provision/iaas-rpc/internal/plugin/mock_proto" 16 proto "github.com/iaas-resource-provision/iaas-rpc/internal/tfplugin5" 17 ) 18 19 var _ providers.Interface = (*GRPCProvider)(nil) 20 21 func mockProviderClient(t *testing.T) *mockproto.MockProviderClient { 22 ctrl := gomock.NewController(t) 23 client := mockproto.NewMockProviderClient(ctrl) 24 25 // we always need a GetSchema method 26 client.EXPECT().GetSchema( 27 gomock.Any(), 28 gomock.Any(), 29 gomock.Any(), 30 ).Return(providerProtoSchema(), nil) 31 32 return client 33 } 34 35 func checkDiags(t *testing.T, d tfdiags.Diagnostics) { 36 t.Helper() 37 if d.HasErrors() { 38 t.Fatal(d.Err()) 39 } 40 } 41 42 func providerProtoSchema() *proto.GetProviderSchema_Response { 43 return &proto.GetProviderSchema_Response{ 44 Provider: &proto.Schema{ 45 Block: &proto.Schema_Block{ 46 Attributes: []*proto.Schema_Attribute{ 47 { 48 Name: "attr", 49 Type: []byte(`"string"`), 50 Required: true, 51 }, 52 }, 53 }, 54 }, 55 ResourceSchemas: map[string]*proto.Schema{ 56 "resource": &proto.Schema{ 57 Version: 1, 58 Block: &proto.Schema_Block{ 59 Attributes: []*proto.Schema_Attribute{ 60 { 61 Name: "attr", 62 Type: []byte(`"string"`), 63 Required: true, 64 }, 65 }, 66 }, 67 }, 68 }, 69 DataSourceSchemas: map[string]*proto.Schema{ 70 "data": &proto.Schema{ 71 Version: 1, 72 Block: &proto.Schema_Block{ 73 Attributes: []*proto.Schema_Attribute{ 74 { 75 Name: "attr", 76 Type: []byte(`"string"`), 77 Required: true, 78 }, 79 }, 80 }, 81 }, 82 }, 83 } 84 } 85 86 func TestGRPCProvider_GetSchema(t *testing.T) { 87 p := &GRPCProvider{ 88 client: mockProviderClient(t), 89 } 90 91 resp := p.GetProviderSchema() 92 checkDiags(t, resp.Diagnostics) 93 } 94 95 func TestGRPCProvider_PrepareProviderConfig(t *testing.T) { 96 client := mockProviderClient(t) 97 p := &GRPCProvider{ 98 client: client, 99 } 100 101 client.EXPECT().PrepareProviderConfig( 102 gomock.Any(), 103 gomock.Any(), 104 ).Return(&proto.PrepareProviderConfig_Response{}, nil) 105 106 cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"}) 107 resp := p.ValidateProviderConfig(providers.ValidateProviderConfigRequest{Config: cfg}) 108 checkDiags(t, resp.Diagnostics) 109 } 110 111 func TestGRPCProvider_ValidateResourceConfig(t *testing.T) { 112 client := mockProviderClient(t) 113 p := &GRPCProvider{ 114 client: client, 115 } 116 117 client.EXPECT().ValidateResourceTypeConfig( 118 gomock.Any(), 119 gomock.Any(), 120 ).Return(&proto.ValidateResourceTypeConfig_Response{}, nil) 121 122 cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"}) 123 resp := p.ValidateResourceConfig(providers.ValidateResourceConfigRequest{ 124 TypeName: "resource", 125 Config: cfg, 126 }) 127 checkDiags(t, resp.Diagnostics) 128 } 129 130 func TestGRPCProvider_ValidateDataSourceConfig(t *testing.T) { 131 client := mockProviderClient(t) 132 p := &GRPCProvider{ 133 client: client, 134 } 135 136 client.EXPECT().ValidateDataSourceConfig( 137 gomock.Any(), 138 gomock.Any(), 139 ).Return(&proto.ValidateDataSourceConfig_Response{}, nil) 140 141 cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"}) 142 resp := p.ValidateDataResourceConfig(providers.ValidateDataResourceConfigRequest{ 143 TypeName: "data", 144 Config: cfg, 145 }) 146 checkDiags(t, resp.Diagnostics) 147 } 148 149 func TestGRPCProvider_UpgradeResourceState(t *testing.T) { 150 client := mockProviderClient(t) 151 p := &GRPCProvider{ 152 client: client, 153 } 154 155 client.EXPECT().UpgradeResourceState( 156 gomock.Any(), 157 gomock.Any(), 158 ).Return(&proto.UpgradeResourceState_Response{ 159 UpgradedState: &proto.DynamicValue{ 160 Msgpack: []byte("\x81\xa4attr\xa3bar"), 161 }, 162 }, nil) 163 164 resp := p.UpgradeResourceState(providers.UpgradeResourceStateRequest{ 165 TypeName: "resource", 166 Version: 0, 167 RawStateJSON: []byte(`{"old_attr":"bar"}`), 168 }) 169 checkDiags(t, resp.Diagnostics) 170 171 expected := cty.ObjectVal(map[string]cty.Value{ 172 "attr": cty.StringVal("bar"), 173 }) 174 175 if !cmp.Equal(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty) { 176 t.Fatal(cmp.Diff(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty)) 177 } 178 } 179 180 func TestGRPCProvider_UpgradeResourceStateJSON(t *testing.T) { 181 client := mockProviderClient(t) 182 p := &GRPCProvider{ 183 client: client, 184 } 185 186 client.EXPECT().UpgradeResourceState( 187 gomock.Any(), 188 gomock.Any(), 189 ).Return(&proto.UpgradeResourceState_Response{ 190 UpgradedState: &proto.DynamicValue{ 191 Json: []byte(`{"attr":"bar"}`), 192 }, 193 }, nil) 194 195 resp := p.UpgradeResourceState(providers.UpgradeResourceStateRequest{ 196 TypeName: "resource", 197 Version: 0, 198 RawStateJSON: []byte(`{"old_attr":"bar"}`), 199 }) 200 checkDiags(t, resp.Diagnostics) 201 202 expected := cty.ObjectVal(map[string]cty.Value{ 203 "attr": cty.StringVal("bar"), 204 }) 205 206 if !cmp.Equal(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty) { 207 t.Fatal(cmp.Diff(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty)) 208 } 209 } 210 211 func TestGRPCProvider_Configure(t *testing.T) { 212 client := mockProviderClient(t) 213 p := &GRPCProvider{ 214 client: client, 215 } 216 217 client.EXPECT().Configure( 218 gomock.Any(), 219 gomock.Any(), 220 ).Return(&proto.Configure_Response{}, nil) 221 222 resp := p.ConfigureProvider(providers.ConfigureProviderRequest{ 223 Config: cty.ObjectVal(map[string]cty.Value{ 224 "attr": cty.StringVal("foo"), 225 }), 226 }) 227 checkDiags(t, resp.Diagnostics) 228 } 229 230 func TestGRPCProvider_Stop(t *testing.T) { 231 client := mockProviderClient(t) 232 p := &GRPCProvider{ 233 client: client, 234 } 235 236 client.EXPECT().Stop( 237 gomock.Any(), 238 gomock.Any(), 239 ).Return(&proto.Stop_Response{}, nil) 240 241 err := p.Stop() 242 if err != nil { 243 t.Fatal(err) 244 } 245 } 246 247 func TestGRPCProvider_ReadResource(t *testing.T) { 248 client := mockProviderClient(t) 249 p := &GRPCProvider{ 250 client: client, 251 } 252 253 client.EXPECT().ReadResource( 254 gomock.Any(), 255 gomock.Any(), 256 ).Return(&proto.ReadResource_Response{ 257 NewState: &proto.DynamicValue{ 258 Msgpack: []byte("\x81\xa4attr\xa3bar"), 259 }, 260 }, nil) 261 262 resp := p.ReadResource(providers.ReadResourceRequest{ 263 TypeName: "resource", 264 PriorState: cty.ObjectVal(map[string]cty.Value{ 265 "attr": cty.StringVal("foo"), 266 }), 267 }) 268 269 checkDiags(t, resp.Diagnostics) 270 271 expected := cty.ObjectVal(map[string]cty.Value{ 272 "attr": cty.StringVal("bar"), 273 }) 274 275 if !cmp.Equal(expected, resp.NewState, typeComparer, valueComparer, equateEmpty) { 276 t.Fatal(cmp.Diff(expected, resp.NewState, typeComparer, valueComparer, equateEmpty)) 277 } 278 } 279 280 func TestGRPCProvider_ReadResourceJSON(t *testing.T) { 281 client := mockProviderClient(t) 282 p := &GRPCProvider{ 283 client: client, 284 } 285 286 client.EXPECT().ReadResource( 287 gomock.Any(), 288 gomock.Any(), 289 ).Return(&proto.ReadResource_Response{ 290 NewState: &proto.DynamicValue{ 291 Json: []byte(`{"attr":"bar"}`), 292 }, 293 }, nil) 294 295 resp := p.ReadResource(providers.ReadResourceRequest{ 296 TypeName: "resource", 297 PriorState: cty.ObjectVal(map[string]cty.Value{ 298 "attr": cty.StringVal("foo"), 299 }), 300 }) 301 302 checkDiags(t, resp.Diagnostics) 303 304 expected := cty.ObjectVal(map[string]cty.Value{ 305 "attr": cty.StringVal("bar"), 306 }) 307 308 if !cmp.Equal(expected, resp.NewState, typeComparer, valueComparer, equateEmpty) { 309 t.Fatal(cmp.Diff(expected, resp.NewState, typeComparer, valueComparer, equateEmpty)) 310 } 311 } 312 313 func TestGRPCProvider_ReadEmptyJSON(t *testing.T) { 314 client := mockProviderClient(t) 315 p := &GRPCProvider{ 316 client: client, 317 } 318 319 client.EXPECT().ReadResource( 320 gomock.Any(), 321 gomock.Any(), 322 ).Return(&proto.ReadResource_Response{ 323 NewState: &proto.DynamicValue{ 324 Json: []byte(``), 325 }, 326 }, nil) 327 328 obj := cty.ObjectVal(map[string]cty.Value{ 329 "attr": cty.StringVal("foo"), 330 }) 331 resp := p.ReadResource(providers.ReadResourceRequest{ 332 TypeName: "resource", 333 PriorState: obj, 334 }) 335 336 checkDiags(t, resp.Diagnostics) 337 338 expected := cty.NullVal(obj.Type()) 339 340 if !cmp.Equal(expected, resp.NewState, typeComparer, valueComparer, equateEmpty) { 341 t.Fatal(cmp.Diff(expected, resp.NewState, typeComparer, valueComparer, equateEmpty)) 342 } 343 } 344 345 func TestGRPCProvider_PlanResourceChange(t *testing.T) { 346 client := mockProviderClient(t) 347 p := &GRPCProvider{ 348 client: client, 349 } 350 351 expectedPrivate := []byte(`{"meta": "data"}`) 352 353 client.EXPECT().PlanResourceChange( 354 gomock.Any(), 355 gomock.Any(), 356 ).Return(&proto.PlanResourceChange_Response{ 357 PlannedState: &proto.DynamicValue{ 358 Msgpack: []byte("\x81\xa4attr\xa3bar"), 359 }, 360 RequiresReplace: []*proto.AttributePath{ 361 { 362 Steps: []*proto.AttributePath_Step{ 363 { 364 Selector: &proto.AttributePath_Step_AttributeName{ 365 AttributeName: "attr", 366 }, 367 }, 368 }, 369 }, 370 }, 371 PlannedPrivate: expectedPrivate, 372 }, nil) 373 374 resp := p.PlanResourceChange(providers.PlanResourceChangeRequest{ 375 TypeName: "resource", 376 PriorState: cty.ObjectVal(map[string]cty.Value{ 377 "attr": cty.StringVal("foo"), 378 }), 379 ProposedNewState: cty.ObjectVal(map[string]cty.Value{ 380 "attr": cty.StringVal("bar"), 381 }), 382 Config: cty.ObjectVal(map[string]cty.Value{ 383 "attr": cty.StringVal("bar"), 384 }), 385 }) 386 387 checkDiags(t, resp.Diagnostics) 388 389 expectedState := cty.ObjectVal(map[string]cty.Value{ 390 "attr": cty.StringVal("bar"), 391 }) 392 393 if !cmp.Equal(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty) { 394 t.Fatal(cmp.Diff(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty)) 395 } 396 397 expectedReplace := `[]cty.Path{cty.Path{cty.GetAttrStep{Name:"attr"}}}` 398 replace := fmt.Sprintf("%#v", resp.RequiresReplace) 399 if expectedReplace != replace { 400 t.Fatalf("expected %q, got %q", expectedReplace, replace) 401 } 402 403 if !bytes.Equal(expectedPrivate, resp.PlannedPrivate) { 404 t.Fatalf("expected %q, got %q", expectedPrivate, resp.PlannedPrivate) 405 } 406 } 407 408 func TestGRPCProvider_PlanResourceChangeJSON(t *testing.T) { 409 client := mockProviderClient(t) 410 p := &GRPCProvider{ 411 client: client, 412 } 413 414 expectedPrivate := []byte(`{"meta": "data"}`) 415 416 client.EXPECT().PlanResourceChange( 417 gomock.Any(), 418 gomock.Any(), 419 ).Return(&proto.PlanResourceChange_Response{ 420 PlannedState: &proto.DynamicValue{ 421 Json: []byte(`{"attr":"bar"}`), 422 }, 423 RequiresReplace: []*proto.AttributePath{ 424 { 425 Steps: []*proto.AttributePath_Step{ 426 { 427 Selector: &proto.AttributePath_Step_AttributeName{ 428 AttributeName: "attr", 429 }, 430 }, 431 }, 432 }, 433 }, 434 PlannedPrivate: expectedPrivate, 435 }, nil) 436 437 resp := p.PlanResourceChange(providers.PlanResourceChangeRequest{ 438 TypeName: "resource", 439 PriorState: cty.ObjectVal(map[string]cty.Value{ 440 "attr": cty.StringVal("foo"), 441 }), 442 ProposedNewState: cty.ObjectVal(map[string]cty.Value{ 443 "attr": cty.StringVal("bar"), 444 }), 445 Config: cty.ObjectVal(map[string]cty.Value{ 446 "attr": cty.StringVal("bar"), 447 }), 448 }) 449 450 checkDiags(t, resp.Diagnostics) 451 452 expectedState := cty.ObjectVal(map[string]cty.Value{ 453 "attr": cty.StringVal("bar"), 454 }) 455 456 if !cmp.Equal(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty) { 457 t.Fatal(cmp.Diff(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty)) 458 } 459 460 expectedReplace := `[]cty.Path{cty.Path{cty.GetAttrStep{Name:"attr"}}}` 461 replace := fmt.Sprintf("%#v", resp.RequiresReplace) 462 if expectedReplace != replace { 463 t.Fatalf("expected %q, got %q", expectedReplace, replace) 464 } 465 466 if !bytes.Equal(expectedPrivate, resp.PlannedPrivate) { 467 t.Fatalf("expected %q, got %q", expectedPrivate, resp.PlannedPrivate) 468 } 469 } 470 471 func TestGRPCProvider_ApplyResourceChange(t *testing.T) { 472 client := mockProviderClient(t) 473 p := &GRPCProvider{ 474 client: client, 475 } 476 477 expectedPrivate := []byte(`{"meta": "data"}`) 478 479 client.EXPECT().ApplyResourceChange( 480 gomock.Any(), 481 gomock.Any(), 482 ).Return(&proto.ApplyResourceChange_Response{ 483 NewState: &proto.DynamicValue{ 484 Msgpack: []byte("\x81\xa4attr\xa3bar"), 485 }, 486 Private: expectedPrivate, 487 }, nil) 488 489 resp := p.ApplyResourceChange(providers.ApplyResourceChangeRequest{ 490 TypeName: "resource", 491 PriorState: cty.ObjectVal(map[string]cty.Value{ 492 "attr": cty.StringVal("foo"), 493 }), 494 PlannedState: cty.ObjectVal(map[string]cty.Value{ 495 "attr": cty.StringVal("bar"), 496 }), 497 Config: cty.ObjectVal(map[string]cty.Value{ 498 "attr": cty.StringVal("bar"), 499 }), 500 PlannedPrivate: expectedPrivate, 501 }) 502 503 checkDiags(t, resp.Diagnostics) 504 505 expectedState := cty.ObjectVal(map[string]cty.Value{ 506 "attr": cty.StringVal("bar"), 507 }) 508 509 if !cmp.Equal(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty) { 510 t.Fatal(cmp.Diff(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty)) 511 } 512 513 if !bytes.Equal(expectedPrivate, resp.Private) { 514 t.Fatalf("expected %q, got %q", expectedPrivate, resp.Private) 515 } 516 } 517 func TestGRPCProvider_ApplyResourceChangeJSON(t *testing.T) { 518 client := mockProviderClient(t) 519 p := &GRPCProvider{ 520 client: client, 521 } 522 523 expectedPrivate := []byte(`{"meta": "data"}`) 524 525 client.EXPECT().ApplyResourceChange( 526 gomock.Any(), 527 gomock.Any(), 528 ).Return(&proto.ApplyResourceChange_Response{ 529 NewState: &proto.DynamicValue{ 530 Json: []byte(`{"attr":"bar"}`), 531 }, 532 Private: expectedPrivate, 533 }, nil) 534 535 resp := p.ApplyResourceChange(providers.ApplyResourceChangeRequest{ 536 TypeName: "resource", 537 PriorState: cty.ObjectVal(map[string]cty.Value{ 538 "attr": cty.StringVal("foo"), 539 }), 540 PlannedState: cty.ObjectVal(map[string]cty.Value{ 541 "attr": cty.StringVal("bar"), 542 }), 543 Config: cty.ObjectVal(map[string]cty.Value{ 544 "attr": cty.StringVal("bar"), 545 }), 546 PlannedPrivate: expectedPrivate, 547 }) 548 549 checkDiags(t, resp.Diagnostics) 550 551 expectedState := cty.ObjectVal(map[string]cty.Value{ 552 "attr": cty.StringVal("bar"), 553 }) 554 555 if !cmp.Equal(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty) { 556 t.Fatal(cmp.Diff(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty)) 557 } 558 559 if !bytes.Equal(expectedPrivate, resp.Private) { 560 t.Fatalf("expected %q, got %q", expectedPrivate, resp.Private) 561 } 562 } 563 564 func TestGRPCProvider_ImportResourceState(t *testing.T) { 565 client := mockProviderClient(t) 566 p := &GRPCProvider{ 567 client: client, 568 } 569 570 expectedPrivate := []byte(`{"meta": "data"}`) 571 572 client.EXPECT().ImportResourceState( 573 gomock.Any(), 574 gomock.Any(), 575 ).Return(&proto.ImportResourceState_Response{ 576 ImportedResources: []*proto.ImportResourceState_ImportedResource{ 577 { 578 TypeName: "resource", 579 State: &proto.DynamicValue{ 580 Msgpack: []byte("\x81\xa4attr\xa3bar"), 581 }, 582 Private: expectedPrivate, 583 }, 584 }, 585 }, nil) 586 587 resp := p.ImportResourceState(providers.ImportResourceStateRequest{ 588 TypeName: "resource", 589 ID: "foo", 590 }) 591 592 checkDiags(t, resp.Diagnostics) 593 594 expectedResource := providers.ImportedResource{ 595 TypeName: "resource", 596 State: cty.ObjectVal(map[string]cty.Value{ 597 "attr": cty.StringVal("bar"), 598 }), 599 Private: expectedPrivate, 600 } 601 602 imported := resp.ImportedResources[0] 603 if !cmp.Equal(expectedResource, imported, typeComparer, valueComparer, equateEmpty) { 604 t.Fatal(cmp.Diff(expectedResource, imported, typeComparer, valueComparer, equateEmpty)) 605 } 606 } 607 func TestGRPCProvider_ImportResourceStateJSON(t *testing.T) { 608 client := mockProviderClient(t) 609 p := &GRPCProvider{ 610 client: client, 611 } 612 613 expectedPrivate := []byte(`{"meta": "data"}`) 614 615 client.EXPECT().ImportResourceState( 616 gomock.Any(), 617 gomock.Any(), 618 ).Return(&proto.ImportResourceState_Response{ 619 ImportedResources: []*proto.ImportResourceState_ImportedResource{ 620 { 621 TypeName: "resource", 622 State: &proto.DynamicValue{ 623 Json: []byte(`{"attr":"bar"}`), 624 }, 625 Private: expectedPrivate, 626 }, 627 }, 628 }, nil) 629 630 resp := p.ImportResourceState(providers.ImportResourceStateRequest{ 631 TypeName: "resource", 632 ID: "foo", 633 }) 634 635 checkDiags(t, resp.Diagnostics) 636 637 expectedResource := providers.ImportedResource{ 638 TypeName: "resource", 639 State: cty.ObjectVal(map[string]cty.Value{ 640 "attr": cty.StringVal("bar"), 641 }), 642 Private: expectedPrivate, 643 } 644 645 imported := resp.ImportedResources[0] 646 if !cmp.Equal(expectedResource, imported, typeComparer, valueComparer, equateEmpty) { 647 t.Fatal(cmp.Diff(expectedResource, imported, typeComparer, valueComparer, equateEmpty)) 648 } 649 } 650 651 func TestGRPCProvider_ReadDataSource(t *testing.T) { 652 client := mockProviderClient(t) 653 p := &GRPCProvider{ 654 client: client, 655 } 656 657 client.EXPECT().ReadDataSource( 658 gomock.Any(), 659 gomock.Any(), 660 ).Return(&proto.ReadDataSource_Response{ 661 State: &proto.DynamicValue{ 662 Msgpack: []byte("\x81\xa4attr\xa3bar"), 663 }, 664 }, nil) 665 666 resp := p.ReadDataSource(providers.ReadDataSourceRequest{ 667 TypeName: "data", 668 Config: cty.ObjectVal(map[string]cty.Value{ 669 "attr": cty.StringVal("foo"), 670 }), 671 }) 672 673 checkDiags(t, resp.Diagnostics) 674 675 expected := cty.ObjectVal(map[string]cty.Value{ 676 "attr": cty.StringVal("bar"), 677 }) 678 679 if !cmp.Equal(expected, resp.State, typeComparer, valueComparer, equateEmpty) { 680 t.Fatal(cmp.Diff(expected, resp.State, typeComparer, valueComparer, equateEmpty)) 681 } 682 } 683 684 func TestGRPCProvider_ReadDataSourceJSON(t *testing.T) { 685 client := mockProviderClient(t) 686 p := &GRPCProvider{ 687 client: client, 688 } 689 690 client.EXPECT().ReadDataSource( 691 gomock.Any(), 692 gomock.Any(), 693 ).Return(&proto.ReadDataSource_Response{ 694 State: &proto.DynamicValue{ 695 Json: []byte(`{"attr":"bar"}`), 696 }, 697 }, nil) 698 699 resp := p.ReadDataSource(providers.ReadDataSourceRequest{ 700 TypeName: "data", 701 Config: cty.ObjectVal(map[string]cty.Value{ 702 "attr": cty.StringVal("foo"), 703 }), 704 }) 705 706 checkDiags(t, resp.Diagnostics) 707 708 expected := cty.ObjectVal(map[string]cty.Value{ 709 "attr": cty.StringVal("bar"), 710 }) 711 712 if !cmp.Equal(expected, resp.State, typeComparer, valueComparer, equateEmpty) { 713 t.Fatal(cmp.Diff(expected, resp.State, typeComparer, valueComparer, equateEmpty)) 714 } 715 }