github.com/opentofu/opentofu@v1.7.1/internal/plugin/grpc_provider_test.go (about) 1 // Copyright (c) The OpenTofu Authors 2 // SPDX-License-Identifier: MPL-2.0 3 // Copyright (c) 2023 HashiCorp, Inc. 4 // SPDX-License-Identifier: MPL-2.0 5 6 package plugin 7 8 import ( 9 "bytes" 10 "fmt" 11 "testing" 12 13 "github.com/opentofu/opentofu/internal/addrs" 14 15 "github.com/golang/mock/gomock" 16 "github.com/google/go-cmp/cmp" 17 "github.com/opentofu/opentofu/internal/configs/hcl2shim" 18 "github.com/opentofu/opentofu/internal/providers" 19 "github.com/opentofu/opentofu/internal/tfdiags" 20 "github.com/zclconf/go-cty/cty" 21 22 mockproto "github.com/opentofu/opentofu/internal/plugin/mock_proto" 23 proto "github.com/opentofu/opentofu/internal/tfplugin5" 24 ) 25 26 var _ providers.Interface = (*GRPCProvider)(nil) 27 28 func mockProviderClient(t *testing.T) *mockproto.MockProviderClient { 29 ctrl := gomock.NewController(t) 30 client := mockproto.NewMockProviderClient(ctrl) 31 32 // we always need a GetSchema method 33 client.EXPECT().GetSchema( 34 gomock.Any(), 35 gomock.Any(), 36 gomock.Any(), 37 ).Return(providerProtoSchema(), nil) 38 39 return client 40 } 41 42 func checkDiags(t *testing.T, d tfdiags.Diagnostics) { 43 t.Helper() 44 if d.HasErrors() { 45 t.Fatal(d.Err()) 46 } 47 } 48 49 // checkDiagsHasError ensures error diagnostics are present or fails the test. 50 func checkDiagsHasError(t *testing.T, d tfdiags.Diagnostics) { 51 t.Helper() 52 53 if !d.HasErrors() { 54 t.Fatal("expected error diagnostics") 55 } 56 } 57 58 func providerProtoSchema() *proto.GetProviderSchema_Response { 59 return &proto.GetProviderSchema_Response{ 60 Provider: &proto.Schema{ 61 Block: &proto.Schema_Block{ 62 Attributes: []*proto.Schema_Attribute{ 63 { 64 Name: "attr", 65 Type: []byte(`"string"`), 66 Required: true, 67 }, 68 }, 69 }, 70 }, 71 ResourceSchemas: map[string]*proto.Schema{ 72 "resource": &proto.Schema{ 73 Version: 1, 74 Block: &proto.Schema_Block{ 75 Attributes: []*proto.Schema_Attribute{ 76 { 77 Name: "attr", 78 Type: []byte(`"string"`), 79 Required: true, 80 }, 81 }, 82 }, 83 }, 84 }, 85 DataSourceSchemas: map[string]*proto.Schema{ 86 "data": &proto.Schema{ 87 Version: 1, 88 Block: &proto.Schema_Block{ 89 Attributes: []*proto.Schema_Attribute{ 90 { 91 Name: "attr", 92 Type: []byte(`"string"`), 93 Required: true, 94 }, 95 }, 96 }, 97 }, 98 }, 99 Functions: map[string]*proto.Function{ 100 "fn": &proto.Function{ 101 Parameters: []*proto.Function_Parameter{{ 102 Name: "par_a", 103 Type: []byte(`"string"`), 104 AllowNullValue: false, 105 AllowUnknownValues: false, 106 }}, 107 VariadicParameter: &proto.Function_Parameter{ 108 Name: "par_var", 109 Type: []byte(`"string"`), 110 AllowNullValue: true, 111 AllowUnknownValues: false, 112 }, 113 Return: &proto.Function_Return{ 114 Type: []byte(`"string"`), 115 }, 116 }, 117 }, 118 } 119 } 120 121 func TestGRPCProvider_GetSchema(t *testing.T) { 122 p := &GRPCProvider{ 123 client: mockProviderClient(t), 124 } 125 126 resp := p.GetProviderSchema() 127 checkDiags(t, resp.Diagnostics) 128 } 129 130 // Ensure that gRPC errors are returned early. 131 // Reference: https://github.com/hashicorp/terraform/issues/31047 132 func TestGRPCProvider_GetSchema_GRPCError(t *testing.T) { 133 ctrl := gomock.NewController(t) 134 client := mockproto.NewMockProviderClient(ctrl) 135 136 client.EXPECT().GetSchema( 137 gomock.Any(), 138 gomock.Any(), 139 gomock.Any(), 140 ).Return(&proto.GetProviderSchema_Response{}, fmt.Errorf("test error")) 141 142 p := &GRPCProvider{ 143 client: client, 144 } 145 146 resp := p.GetProviderSchema() 147 148 checkDiagsHasError(t, resp.Diagnostics) 149 } 150 151 func TestGRPCProvider_GetSchema_GlobalCacheEnabled(t *testing.T) { 152 ctrl := gomock.NewController(t) 153 client := mockproto.NewMockProviderClient(ctrl) 154 // The SchemaCache is global and is saved between test runs 155 providers.SchemaCache = providers.NewMockSchemaCache() 156 157 providerAddr := addrs.Provider{ 158 Namespace: "namespace", 159 Type: "type", 160 } 161 162 mockedProviderResponse := &proto.Schema{Version: 2, Block: &proto.Schema_Block{}} 163 164 client.EXPECT().GetSchema( 165 gomock.Any(), 166 gomock.Any(), 167 gomock.Any(), 168 ).Times(1).Return(&proto.GetProviderSchema_Response{ 169 Provider: mockedProviderResponse, 170 ServerCapabilities: &proto.ServerCapabilities{GetProviderSchemaOptional: true}, 171 }, nil) 172 173 // Run GetProviderTwice, expect GetSchema to be called once 174 // Re-initialize the provider before each run to avoid usage of the local cache 175 p := &GRPCProvider{ 176 client: client, 177 Addr: providerAddr, 178 } 179 resp := p.GetProviderSchema() 180 181 checkDiags(t, resp.Diagnostics) 182 if !cmp.Equal(resp.Provider.Version, mockedProviderResponse.Version) { 183 t.Fatal(cmp.Diff(resp.Provider.Version, mockedProviderResponse.Version)) 184 } 185 186 p = &GRPCProvider{ 187 client: client, 188 Addr: providerAddr, 189 } 190 resp = p.GetProviderSchema() 191 192 checkDiags(t, resp.Diagnostics) 193 if !cmp.Equal(resp.Provider.Version, mockedProviderResponse.Version) { 194 t.Fatal(cmp.Diff(resp.Provider.Version, mockedProviderResponse.Version)) 195 } 196 } 197 198 func TestGRPCProvider_GetSchema_GlobalCacheDisabled(t *testing.T) { 199 ctrl := gomock.NewController(t) 200 client := mockproto.NewMockProviderClient(ctrl) 201 // The SchemaCache is global and is saved between test runs 202 providers.SchemaCache = providers.NewMockSchemaCache() 203 204 providerAddr := addrs.Provider{ 205 Namespace: "namespace", 206 Type: "type", 207 } 208 209 mockedProviderResponse := &proto.Schema{Version: 2, Block: &proto.Schema_Block{}} 210 211 client.EXPECT().GetSchema( 212 gomock.Any(), 213 gomock.Any(), 214 gomock.Any(), 215 ).Times(2).Return(&proto.GetProviderSchema_Response{ 216 Provider: mockedProviderResponse, 217 ServerCapabilities: &proto.ServerCapabilities{GetProviderSchemaOptional: false}, 218 }, nil) 219 220 // Run GetProviderTwice, expect GetSchema to be called once 221 // Re-initialize the provider before each run to avoid usage of the local cache 222 p := &GRPCProvider{ 223 client: client, 224 Addr: providerAddr, 225 } 226 resp := p.GetProviderSchema() 227 228 checkDiags(t, resp.Diagnostics) 229 if !cmp.Equal(resp.Provider.Version, mockedProviderResponse.Version) { 230 t.Fatal(cmp.Diff(resp.Provider.Version, mockedProviderResponse.Version)) 231 } 232 233 p = &GRPCProvider{ 234 client: client, 235 Addr: providerAddr, 236 } 237 resp = p.GetProviderSchema() 238 239 checkDiags(t, resp.Diagnostics) 240 if !cmp.Equal(resp.Provider.Version, mockedProviderResponse.Version) { 241 t.Fatal(cmp.Diff(resp.Provider.Version, mockedProviderResponse.Version)) 242 } 243 } 244 245 // Ensure that provider error diagnostics are returned early. 246 // Reference: https://github.com/hashicorp/terraform/issues/31047 247 func TestGRPCProvider_GetSchema_ResponseErrorDiagnostic(t *testing.T) { 248 ctrl := gomock.NewController(t) 249 client := mockproto.NewMockProviderClient(ctrl) 250 251 client.EXPECT().GetSchema( 252 gomock.Any(), 253 gomock.Any(), 254 gomock.Any(), 255 ).Return(&proto.GetProviderSchema_Response{ 256 Diagnostics: []*proto.Diagnostic{ 257 { 258 Severity: proto.Diagnostic_ERROR, 259 Summary: "error summary", 260 Detail: "error detail", 261 }, 262 }, 263 // Trigger potential panics 264 Provider: &proto.Schema{}, 265 }, nil) 266 267 p := &GRPCProvider{ 268 client: client, 269 } 270 271 resp := p.GetProviderSchema() 272 273 checkDiagsHasError(t, resp.Diagnostics) 274 } 275 276 func TestGRPCProvider_PrepareProviderConfig(t *testing.T) { 277 client := mockProviderClient(t) 278 p := &GRPCProvider{ 279 client: client, 280 } 281 282 client.EXPECT().PrepareProviderConfig( 283 gomock.Any(), 284 gomock.Any(), 285 ).Return(&proto.PrepareProviderConfig_Response{}, nil) 286 287 cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"}) 288 resp := p.ValidateProviderConfig(providers.ValidateProviderConfigRequest{Config: cfg}) 289 checkDiags(t, resp.Diagnostics) 290 } 291 292 func TestGRPCProvider_ValidateResourceConfig(t *testing.T) { 293 client := mockProviderClient(t) 294 p := &GRPCProvider{ 295 client: client, 296 } 297 298 client.EXPECT().ValidateResourceTypeConfig( 299 gomock.Any(), 300 gomock.Any(), 301 ).Return(&proto.ValidateResourceTypeConfig_Response{}, nil) 302 303 cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"}) 304 resp := p.ValidateResourceConfig(providers.ValidateResourceConfigRequest{ 305 TypeName: "resource", 306 Config: cfg, 307 }) 308 checkDiags(t, resp.Diagnostics) 309 } 310 311 func TestGRPCProvider_ValidateDataSourceConfig(t *testing.T) { 312 client := mockProviderClient(t) 313 p := &GRPCProvider{ 314 client: client, 315 } 316 317 client.EXPECT().ValidateDataSourceConfig( 318 gomock.Any(), 319 gomock.Any(), 320 ).Return(&proto.ValidateDataSourceConfig_Response{}, nil) 321 322 cfg := hcl2shim.HCL2ValueFromConfigValue(map[string]interface{}{"attr": "value"}) 323 resp := p.ValidateDataResourceConfig(providers.ValidateDataResourceConfigRequest{ 324 TypeName: "data", 325 Config: cfg, 326 }) 327 checkDiags(t, resp.Diagnostics) 328 } 329 330 func TestGRPCProvider_UpgradeResourceState(t *testing.T) { 331 client := mockProviderClient(t) 332 p := &GRPCProvider{ 333 client: client, 334 } 335 336 client.EXPECT().UpgradeResourceState( 337 gomock.Any(), 338 gomock.Any(), 339 ).Return(&proto.UpgradeResourceState_Response{ 340 UpgradedState: &proto.DynamicValue{ 341 Msgpack: []byte("\x81\xa4attr\xa3bar"), 342 }, 343 }, nil) 344 345 resp := p.UpgradeResourceState(providers.UpgradeResourceStateRequest{ 346 TypeName: "resource", 347 Version: 0, 348 RawStateJSON: []byte(`{"old_attr":"bar"}`), 349 }) 350 checkDiags(t, resp.Diagnostics) 351 352 expected := cty.ObjectVal(map[string]cty.Value{ 353 "attr": cty.StringVal("bar"), 354 }) 355 356 if !cmp.Equal(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty) { 357 t.Fatal(cmp.Diff(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty)) 358 } 359 } 360 361 func TestGRPCProvider_UpgradeResourceStateJSON(t *testing.T) { 362 client := mockProviderClient(t) 363 p := &GRPCProvider{ 364 client: client, 365 } 366 367 client.EXPECT().UpgradeResourceState( 368 gomock.Any(), 369 gomock.Any(), 370 ).Return(&proto.UpgradeResourceState_Response{ 371 UpgradedState: &proto.DynamicValue{ 372 Json: []byte(`{"attr":"bar"}`), 373 }, 374 }, nil) 375 376 resp := p.UpgradeResourceState(providers.UpgradeResourceStateRequest{ 377 TypeName: "resource", 378 Version: 0, 379 RawStateJSON: []byte(`{"old_attr":"bar"}`), 380 }) 381 checkDiags(t, resp.Diagnostics) 382 383 expected := cty.ObjectVal(map[string]cty.Value{ 384 "attr": cty.StringVal("bar"), 385 }) 386 387 if !cmp.Equal(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty) { 388 t.Fatal(cmp.Diff(expected, resp.UpgradedState, typeComparer, valueComparer, equateEmpty)) 389 } 390 } 391 392 func TestGRPCProvider_Configure(t *testing.T) { 393 client := mockProviderClient(t) 394 p := &GRPCProvider{ 395 client: client, 396 } 397 398 client.EXPECT().Configure( 399 gomock.Any(), 400 gomock.Any(), 401 ).Return(&proto.Configure_Response{}, nil) 402 403 resp := p.ConfigureProvider(providers.ConfigureProviderRequest{ 404 Config: cty.ObjectVal(map[string]cty.Value{ 405 "attr": cty.StringVal("foo"), 406 }), 407 }) 408 checkDiags(t, resp.Diagnostics) 409 } 410 411 func TestGRPCProvider_Stop(t *testing.T) { 412 ctrl := gomock.NewController(t) 413 client := mockproto.NewMockProviderClient(ctrl) 414 p := &GRPCProvider{ 415 client: client, 416 } 417 418 client.EXPECT().Stop( 419 gomock.Any(), 420 gomock.Any(), 421 ).Return(&proto.Stop_Response{}, nil) 422 423 err := p.Stop() 424 if err != nil { 425 t.Fatal(err) 426 } 427 } 428 429 func TestGRPCProvider_ReadResource(t *testing.T) { 430 client := mockProviderClient(t) 431 p := &GRPCProvider{ 432 client: client, 433 } 434 435 client.EXPECT().ReadResource( 436 gomock.Any(), 437 gomock.Any(), 438 ).Return(&proto.ReadResource_Response{ 439 NewState: &proto.DynamicValue{ 440 Msgpack: []byte("\x81\xa4attr\xa3bar"), 441 }, 442 }, nil) 443 444 resp := p.ReadResource(providers.ReadResourceRequest{ 445 TypeName: "resource", 446 PriorState: cty.ObjectVal(map[string]cty.Value{ 447 "attr": cty.StringVal("foo"), 448 }), 449 }) 450 451 checkDiags(t, resp.Diagnostics) 452 453 expected := cty.ObjectVal(map[string]cty.Value{ 454 "attr": cty.StringVal("bar"), 455 }) 456 457 if !cmp.Equal(expected, resp.NewState, typeComparer, valueComparer, equateEmpty) { 458 t.Fatal(cmp.Diff(expected, resp.NewState, typeComparer, valueComparer, equateEmpty)) 459 } 460 } 461 462 func TestGRPCProvider_ReadResourceJSON(t *testing.T) { 463 client := mockProviderClient(t) 464 p := &GRPCProvider{ 465 client: client, 466 } 467 468 client.EXPECT().ReadResource( 469 gomock.Any(), 470 gomock.Any(), 471 ).Return(&proto.ReadResource_Response{ 472 NewState: &proto.DynamicValue{ 473 Json: []byte(`{"attr":"bar"}`), 474 }, 475 }, nil) 476 477 resp := p.ReadResource(providers.ReadResourceRequest{ 478 TypeName: "resource", 479 PriorState: cty.ObjectVal(map[string]cty.Value{ 480 "attr": cty.StringVal("foo"), 481 }), 482 }) 483 484 checkDiags(t, resp.Diagnostics) 485 486 expected := cty.ObjectVal(map[string]cty.Value{ 487 "attr": cty.StringVal("bar"), 488 }) 489 490 if !cmp.Equal(expected, resp.NewState, typeComparer, valueComparer, equateEmpty) { 491 t.Fatal(cmp.Diff(expected, resp.NewState, typeComparer, valueComparer, equateEmpty)) 492 } 493 } 494 495 func TestGRPCProvider_ReadEmptyJSON(t *testing.T) { 496 client := mockProviderClient(t) 497 p := &GRPCProvider{ 498 client: client, 499 } 500 501 client.EXPECT().ReadResource( 502 gomock.Any(), 503 gomock.Any(), 504 ).Return(&proto.ReadResource_Response{ 505 NewState: &proto.DynamicValue{ 506 Json: []byte(``), 507 }, 508 }, nil) 509 510 obj := cty.ObjectVal(map[string]cty.Value{ 511 "attr": cty.StringVal("foo"), 512 }) 513 resp := p.ReadResource(providers.ReadResourceRequest{ 514 TypeName: "resource", 515 PriorState: obj, 516 }) 517 518 checkDiags(t, resp.Diagnostics) 519 520 expected := cty.NullVal(obj.Type()) 521 522 if !cmp.Equal(expected, resp.NewState, typeComparer, valueComparer, equateEmpty) { 523 t.Fatal(cmp.Diff(expected, resp.NewState, typeComparer, valueComparer, equateEmpty)) 524 } 525 } 526 527 func TestGRPCProvider_PlanResourceChange(t *testing.T) { 528 client := mockProviderClient(t) 529 p := &GRPCProvider{ 530 client: client, 531 } 532 533 expectedPrivate := []byte(`{"meta": "data"}`) 534 535 client.EXPECT().PlanResourceChange( 536 gomock.Any(), 537 gomock.Any(), 538 ).Return(&proto.PlanResourceChange_Response{ 539 PlannedState: &proto.DynamicValue{ 540 Msgpack: []byte("\x81\xa4attr\xa3bar"), 541 }, 542 RequiresReplace: []*proto.AttributePath{ 543 { 544 Steps: []*proto.AttributePath_Step{ 545 { 546 Selector: &proto.AttributePath_Step_AttributeName{ 547 AttributeName: "attr", 548 }, 549 }, 550 }, 551 }, 552 }, 553 PlannedPrivate: expectedPrivate, 554 }, nil) 555 556 resp := p.PlanResourceChange(providers.PlanResourceChangeRequest{ 557 TypeName: "resource", 558 PriorState: cty.ObjectVal(map[string]cty.Value{ 559 "attr": cty.StringVal("foo"), 560 }), 561 ProposedNewState: cty.ObjectVal(map[string]cty.Value{ 562 "attr": cty.StringVal("bar"), 563 }), 564 Config: cty.ObjectVal(map[string]cty.Value{ 565 "attr": cty.StringVal("bar"), 566 }), 567 }) 568 569 checkDiags(t, resp.Diagnostics) 570 571 expectedState := cty.ObjectVal(map[string]cty.Value{ 572 "attr": cty.StringVal("bar"), 573 }) 574 575 if !cmp.Equal(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty) { 576 t.Fatal(cmp.Diff(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty)) 577 } 578 579 expectedReplace := `[]cty.Path{cty.Path{cty.GetAttrStep{Name:"attr"}}}` 580 replace := fmt.Sprintf("%#v", resp.RequiresReplace) 581 if expectedReplace != replace { 582 t.Fatalf("expected %q, got %q", expectedReplace, replace) 583 } 584 585 if !bytes.Equal(expectedPrivate, resp.PlannedPrivate) { 586 t.Fatalf("expected %q, got %q", expectedPrivate, resp.PlannedPrivate) 587 } 588 } 589 590 func TestGRPCProvider_PlanResourceChangeJSON(t *testing.T) { 591 client := mockProviderClient(t) 592 p := &GRPCProvider{ 593 client: client, 594 } 595 596 expectedPrivate := []byte(`{"meta": "data"}`) 597 598 client.EXPECT().PlanResourceChange( 599 gomock.Any(), 600 gomock.Any(), 601 ).Return(&proto.PlanResourceChange_Response{ 602 PlannedState: &proto.DynamicValue{ 603 Json: []byte(`{"attr":"bar"}`), 604 }, 605 RequiresReplace: []*proto.AttributePath{ 606 { 607 Steps: []*proto.AttributePath_Step{ 608 { 609 Selector: &proto.AttributePath_Step_AttributeName{ 610 AttributeName: "attr", 611 }, 612 }, 613 }, 614 }, 615 }, 616 PlannedPrivate: expectedPrivate, 617 }, nil) 618 619 resp := p.PlanResourceChange(providers.PlanResourceChangeRequest{ 620 TypeName: "resource", 621 PriorState: cty.ObjectVal(map[string]cty.Value{ 622 "attr": cty.StringVal("foo"), 623 }), 624 ProposedNewState: cty.ObjectVal(map[string]cty.Value{ 625 "attr": cty.StringVal("bar"), 626 }), 627 Config: cty.ObjectVal(map[string]cty.Value{ 628 "attr": cty.StringVal("bar"), 629 }), 630 }) 631 632 checkDiags(t, resp.Diagnostics) 633 634 expectedState := cty.ObjectVal(map[string]cty.Value{ 635 "attr": cty.StringVal("bar"), 636 }) 637 638 if !cmp.Equal(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty) { 639 t.Fatal(cmp.Diff(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty)) 640 } 641 642 expectedReplace := `[]cty.Path{cty.Path{cty.GetAttrStep{Name:"attr"}}}` 643 replace := fmt.Sprintf("%#v", resp.RequiresReplace) 644 if expectedReplace != replace { 645 t.Fatalf("expected %q, got %q", expectedReplace, replace) 646 } 647 648 if !bytes.Equal(expectedPrivate, resp.PlannedPrivate) { 649 t.Fatalf("expected %q, got %q", expectedPrivate, resp.PlannedPrivate) 650 } 651 } 652 653 func TestGRPCProvider_ApplyResourceChange(t *testing.T) { 654 client := mockProviderClient(t) 655 p := &GRPCProvider{ 656 client: client, 657 } 658 659 expectedPrivate := []byte(`{"meta": "data"}`) 660 661 client.EXPECT().ApplyResourceChange( 662 gomock.Any(), 663 gomock.Any(), 664 ).Return(&proto.ApplyResourceChange_Response{ 665 NewState: &proto.DynamicValue{ 666 Msgpack: []byte("\x81\xa4attr\xa3bar"), 667 }, 668 Private: expectedPrivate, 669 }, nil) 670 671 resp := p.ApplyResourceChange(providers.ApplyResourceChangeRequest{ 672 TypeName: "resource", 673 PriorState: cty.ObjectVal(map[string]cty.Value{ 674 "attr": cty.StringVal("foo"), 675 }), 676 PlannedState: cty.ObjectVal(map[string]cty.Value{ 677 "attr": cty.StringVal("bar"), 678 }), 679 Config: cty.ObjectVal(map[string]cty.Value{ 680 "attr": cty.StringVal("bar"), 681 }), 682 PlannedPrivate: expectedPrivate, 683 }) 684 685 checkDiags(t, resp.Diagnostics) 686 687 expectedState := cty.ObjectVal(map[string]cty.Value{ 688 "attr": cty.StringVal("bar"), 689 }) 690 691 if !cmp.Equal(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty) { 692 t.Fatal(cmp.Diff(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty)) 693 } 694 695 if !bytes.Equal(expectedPrivate, resp.Private) { 696 t.Fatalf("expected %q, got %q", expectedPrivate, resp.Private) 697 } 698 } 699 func TestGRPCProvider_ApplyResourceChangeJSON(t *testing.T) { 700 client := mockProviderClient(t) 701 p := &GRPCProvider{ 702 client: client, 703 } 704 705 expectedPrivate := []byte(`{"meta": "data"}`) 706 707 client.EXPECT().ApplyResourceChange( 708 gomock.Any(), 709 gomock.Any(), 710 ).Return(&proto.ApplyResourceChange_Response{ 711 NewState: &proto.DynamicValue{ 712 Json: []byte(`{"attr":"bar"}`), 713 }, 714 Private: expectedPrivate, 715 }, nil) 716 717 resp := p.ApplyResourceChange(providers.ApplyResourceChangeRequest{ 718 TypeName: "resource", 719 PriorState: cty.ObjectVal(map[string]cty.Value{ 720 "attr": cty.StringVal("foo"), 721 }), 722 PlannedState: cty.ObjectVal(map[string]cty.Value{ 723 "attr": cty.StringVal("bar"), 724 }), 725 Config: cty.ObjectVal(map[string]cty.Value{ 726 "attr": cty.StringVal("bar"), 727 }), 728 PlannedPrivate: expectedPrivate, 729 }) 730 731 checkDiags(t, resp.Diagnostics) 732 733 expectedState := cty.ObjectVal(map[string]cty.Value{ 734 "attr": cty.StringVal("bar"), 735 }) 736 737 if !cmp.Equal(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty) { 738 t.Fatal(cmp.Diff(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty)) 739 } 740 741 if !bytes.Equal(expectedPrivate, resp.Private) { 742 t.Fatalf("expected %q, got %q", expectedPrivate, resp.Private) 743 } 744 } 745 746 func TestGRPCProvider_ImportResourceState(t *testing.T) { 747 client := mockProviderClient(t) 748 p := &GRPCProvider{ 749 client: client, 750 } 751 752 expectedPrivate := []byte(`{"meta": "data"}`) 753 754 client.EXPECT().ImportResourceState( 755 gomock.Any(), 756 gomock.Any(), 757 ).Return(&proto.ImportResourceState_Response{ 758 ImportedResources: []*proto.ImportResourceState_ImportedResource{ 759 { 760 TypeName: "resource", 761 State: &proto.DynamicValue{ 762 Msgpack: []byte("\x81\xa4attr\xa3bar"), 763 }, 764 Private: expectedPrivate, 765 }, 766 }, 767 }, nil) 768 769 resp := p.ImportResourceState(providers.ImportResourceStateRequest{ 770 TypeName: "resource", 771 ID: "foo", 772 }) 773 774 checkDiags(t, resp.Diagnostics) 775 776 expectedResource := providers.ImportedResource{ 777 TypeName: "resource", 778 State: cty.ObjectVal(map[string]cty.Value{ 779 "attr": cty.StringVal("bar"), 780 }), 781 Private: expectedPrivate, 782 } 783 784 imported := resp.ImportedResources[0] 785 if !cmp.Equal(expectedResource, imported, typeComparer, valueComparer, equateEmpty) { 786 t.Fatal(cmp.Diff(expectedResource, imported, typeComparer, valueComparer, equateEmpty)) 787 } 788 } 789 func TestGRPCProvider_ImportResourceStateJSON(t *testing.T) { 790 client := mockProviderClient(t) 791 p := &GRPCProvider{ 792 client: client, 793 } 794 795 expectedPrivate := []byte(`{"meta": "data"}`) 796 797 client.EXPECT().ImportResourceState( 798 gomock.Any(), 799 gomock.Any(), 800 ).Return(&proto.ImportResourceState_Response{ 801 ImportedResources: []*proto.ImportResourceState_ImportedResource{ 802 { 803 TypeName: "resource", 804 State: &proto.DynamicValue{ 805 Json: []byte(`{"attr":"bar"}`), 806 }, 807 Private: expectedPrivate, 808 }, 809 }, 810 }, nil) 811 812 resp := p.ImportResourceState(providers.ImportResourceStateRequest{ 813 TypeName: "resource", 814 ID: "foo", 815 }) 816 817 checkDiags(t, resp.Diagnostics) 818 819 expectedResource := providers.ImportedResource{ 820 TypeName: "resource", 821 State: cty.ObjectVal(map[string]cty.Value{ 822 "attr": cty.StringVal("bar"), 823 }), 824 Private: expectedPrivate, 825 } 826 827 imported := resp.ImportedResources[0] 828 if !cmp.Equal(expectedResource, imported, typeComparer, valueComparer, equateEmpty) { 829 t.Fatal(cmp.Diff(expectedResource, imported, typeComparer, valueComparer, equateEmpty)) 830 } 831 } 832 833 func TestGRPCProvider_ReadDataSource(t *testing.T) { 834 client := mockProviderClient(t) 835 p := &GRPCProvider{ 836 client: client, 837 } 838 839 client.EXPECT().ReadDataSource( 840 gomock.Any(), 841 gomock.Any(), 842 ).Return(&proto.ReadDataSource_Response{ 843 State: &proto.DynamicValue{ 844 Msgpack: []byte("\x81\xa4attr\xa3bar"), 845 }, 846 }, nil) 847 848 resp := p.ReadDataSource(providers.ReadDataSourceRequest{ 849 TypeName: "data", 850 Config: cty.ObjectVal(map[string]cty.Value{ 851 "attr": cty.StringVal("foo"), 852 }), 853 }) 854 855 checkDiags(t, resp.Diagnostics) 856 857 expected := cty.ObjectVal(map[string]cty.Value{ 858 "attr": cty.StringVal("bar"), 859 }) 860 861 if !cmp.Equal(expected, resp.State, typeComparer, valueComparer, equateEmpty) { 862 t.Fatal(cmp.Diff(expected, resp.State, typeComparer, valueComparer, equateEmpty)) 863 } 864 } 865 866 func TestGRPCProvider_ReadDataSourceJSON(t *testing.T) { 867 client := mockProviderClient(t) 868 p := &GRPCProvider{ 869 client: client, 870 } 871 872 client.EXPECT().ReadDataSource( 873 gomock.Any(), 874 gomock.Any(), 875 ).Return(&proto.ReadDataSource_Response{ 876 State: &proto.DynamicValue{ 877 Json: []byte(`{"attr":"bar"}`), 878 }, 879 }, nil) 880 881 resp := p.ReadDataSource(providers.ReadDataSourceRequest{ 882 TypeName: "data", 883 Config: cty.ObjectVal(map[string]cty.Value{ 884 "attr": cty.StringVal("foo"), 885 }), 886 }) 887 888 checkDiags(t, resp.Diagnostics) 889 890 expected := cty.ObjectVal(map[string]cty.Value{ 891 "attr": cty.StringVal("bar"), 892 }) 893 894 if !cmp.Equal(expected, resp.State, typeComparer, valueComparer, equateEmpty) { 895 t.Fatal(cmp.Diff(expected, resp.State, typeComparer, valueComparer, equateEmpty)) 896 } 897 } 898 899 func TestGRPCProvider_CallFunction(t *testing.T) { 900 client := mockProviderClient(t) 901 p := &GRPCProvider{ 902 client: client, 903 } 904 905 client.EXPECT().CallFunction( 906 gomock.Any(), 907 gomock.Any(), 908 ).Return(&proto.CallFunction_Response{ 909 Result: &proto.DynamicValue{Json: []byte(`"foo"`)}, 910 }, nil) 911 912 resp := p.CallFunction(providers.CallFunctionRequest{ 913 Name: "fn", 914 Arguments: []cty.Value{cty.StringVal("bar"), cty.NilVal}, 915 }) 916 917 if resp.Error != nil { 918 t.Fatal(resp.Error) 919 } 920 if resp.Result != cty.StringVal("foo") { 921 t.Fatalf("%v", resp.Result) 922 } 923 }