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  }