github.com/hashicorp/terraform-plugin-sdk@v1.17.2/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/hashicorp/terraform-plugin-sdk/internal/configs/hcl2shim"
    11  	"github.com/hashicorp/terraform-plugin-sdk/internal/providers"
    12  	"github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags"
    13  	"github.com/zclconf/go-cty/cty"
    14  
    15  	mockproto "github.com/hashicorp/terraform-plugin-sdk/internal/plugin/mock_proto"
    16  	proto "github.com/hashicorp/terraform-plugin-sdk/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": {
    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": {
    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.GetSchema()
    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.PrepareProviderConfig(providers.PrepareProviderConfigRequest{Config: cfg})
   108  	checkDiags(t, resp.Diagnostics)
   109  }
   110  
   111  func TestGRPCProvider_ValidateResourceTypeConfig(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.ValidateResourceTypeConfig(providers.ValidateResourceTypeConfigRequest{
   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.ValidateDataSourceConfig(providers.ValidateDataSourceConfigRequest{
   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_Configure(t *testing.T) {
   181  	client := mockProviderClient(t)
   182  	p := &GRPCProvider{
   183  		client: client,
   184  	}
   185  
   186  	client.EXPECT().Configure(
   187  		gomock.Any(),
   188  		gomock.Any(),
   189  	).Return(&proto.Configure_Response{}, nil)
   190  
   191  	resp := p.Configure(providers.ConfigureRequest{
   192  		Config: cty.ObjectVal(map[string]cty.Value{
   193  			"attr": cty.StringVal("foo"),
   194  		}),
   195  	})
   196  	checkDiags(t, resp.Diagnostics)
   197  }
   198  
   199  func TestGRPCProvider_Stop(t *testing.T) {
   200  	client := mockProviderClient(t)
   201  	p := &GRPCProvider{
   202  		client: client,
   203  	}
   204  
   205  	client.EXPECT().Stop(
   206  		gomock.Any(),
   207  		gomock.Any(),
   208  	).Return(&proto.Stop_Response{}, nil)
   209  
   210  	err := p.Stop()
   211  	if err != nil {
   212  		t.Fatal(err)
   213  	}
   214  }
   215  
   216  func TestGRPCProvider_ReadResource(t *testing.T) {
   217  	client := mockProviderClient(t)
   218  	p := &GRPCProvider{
   219  		client: client,
   220  	}
   221  
   222  	client.EXPECT().ReadResource(
   223  		gomock.Any(),
   224  		gomock.Any(),
   225  	).Return(&proto.ReadResource_Response{
   226  		NewState: &proto.DynamicValue{
   227  			Msgpack: []byte("\x81\xa4attr\xa3bar"),
   228  		},
   229  	}, nil)
   230  
   231  	resp := p.ReadResource(providers.ReadResourceRequest{
   232  		TypeName: "resource",
   233  		PriorState: cty.ObjectVal(map[string]cty.Value{
   234  			"attr": cty.StringVal("foo"),
   235  		}),
   236  	})
   237  
   238  	checkDiags(t, resp.Diagnostics)
   239  
   240  	expected := cty.ObjectVal(map[string]cty.Value{
   241  		"attr": cty.StringVal("bar"),
   242  	})
   243  
   244  	if !cmp.Equal(expected, resp.NewState, typeComparer, valueComparer, equateEmpty) {
   245  		t.Fatal(cmp.Diff(expected, resp.NewState, typeComparer, valueComparer, equateEmpty))
   246  	}
   247  }
   248  
   249  func TestGRPCProvider_PlanResourceChange(t *testing.T) {
   250  	client := mockProviderClient(t)
   251  	p := &GRPCProvider{
   252  		client: client,
   253  	}
   254  
   255  	expectedPrivate := []byte(`{"meta": "data"}`)
   256  
   257  	client.EXPECT().PlanResourceChange(
   258  		gomock.Any(),
   259  		gomock.Any(),
   260  	).Return(&proto.PlanResourceChange_Response{
   261  		PlannedState: &proto.DynamicValue{
   262  			Msgpack: []byte("\x81\xa4attr\xa3bar"),
   263  		},
   264  		RequiresReplace: []*proto.AttributePath{
   265  			{
   266  				Steps: []*proto.AttributePath_Step{
   267  					{
   268  						Selector: &proto.AttributePath_Step_AttributeName{
   269  							AttributeName: "attr",
   270  						},
   271  					},
   272  				},
   273  			},
   274  		},
   275  		PlannedPrivate: expectedPrivate,
   276  	}, nil)
   277  
   278  	resp := p.PlanResourceChange(providers.PlanResourceChangeRequest{
   279  		TypeName: "resource",
   280  		PriorState: cty.ObjectVal(map[string]cty.Value{
   281  			"attr": cty.StringVal("foo"),
   282  		}),
   283  		ProposedNewState: cty.ObjectVal(map[string]cty.Value{
   284  			"attr": cty.StringVal("bar"),
   285  		}),
   286  		Config: cty.ObjectVal(map[string]cty.Value{
   287  			"attr": cty.StringVal("bar"),
   288  		}),
   289  	})
   290  
   291  	checkDiags(t, resp.Diagnostics)
   292  
   293  	expectedState := cty.ObjectVal(map[string]cty.Value{
   294  		"attr": cty.StringVal("bar"),
   295  	})
   296  
   297  	if !cmp.Equal(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty) {
   298  		t.Fatal(cmp.Diff(expectedState, resp.PlannedState, typeComparer, valueComparer, equateEmpty))
   299  	}
   300  
   301  	expectedReplace := `[]cty.Path{cty.Path{cty.GetAttrStep{Name:"attr"}}}`
   302  	replace := fmt.Sprintf("%#v", resp.RequiresReplace)
   303  	if expectedReplace != replace {
   304  		t.Fatalf("expected %q, got %q", expectedReplace, replace)
   305  	}
   306  
   307  	if !bytes.Equal(expectedPrivate, resp.PlannedPrivate) {
   308  		t.Fatalf("expected %q, got %q", expectedPrivate, resp.PlannedPrivate)
   309  	}
   310  }
   311  
   312  func TestGRPCProvider_ApplyResourceChange(t *testing.T) {
   313  	client := mockProviderClient(t)
   314  	p := &GRPCProvider{
   315  		client: client,
   316  	}
   317  
   318  	expectedPrivate := []byte(`{"meta": "data"}`)
   319  
   320  	client.EXPECT().ApplyResourceChange(
   321  		gomock.Any(),
   322  		gomock.Any(),
   323  	).Return(&proto.ApplyResourceChange_Response{
   324  		NewState: &proto.DynamicValue{
   325  			Msgpack: []byte("\x81\xa4attr\xa3bar"),
   326  		},
   327  		Private: expectedPrivate,
   328  	}, nil)
   329  
   330  	resp := p.ApplyResourceChange(providers.ApplyResourceChangeRequest{
   331  		TypeName: "resource",
   332  		PriorState: cty.ObjectVal(map[string]cty.Value{
   333  			"attr": cty.StringVal("foo"),
   334  		}),
   335  		PlannedState: cty.ObjectVal(map[string]cty.Value{
   336  			"attr": cty.StringVal("bar"),
   337  		}),
   338  		Config: cty.ObjectVal(map[string]cty.Value{
   339  			"attr": cty.StringVal("bar"),
   340  		}),
   341  		PlannedPrivate: expectedPrivate,
   342  	})
   343  
   344  	checkDiags(t, resp.Diagnostics)
   345  
   346  	expectedState := cty.ObjectVal(map[string]cty.Value{
   347  		"attr": cty.StringVal("bar"),
   348  	})
   349  
   350  	if !cmp.Equal(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty) {
   351  		t.Fatal(cmp.Diff(expectedState, resp.NewState, typeComparer, valueComparer, equateEmpty))
   352  	}
   353  
   354  	if !bytes.Equal(expectedPrivate, resp.Private) {
   355  		t.Fatalf("expected %q, got %q", expectedPrivate, resp.Private)
   356  	}
   357  }
   358  
   359  func TestGRPCProvider_ImportResourceState(t *testing.T) {
   360  	client := mockProviderClient(t)
   361  	p := &GRPCProvider{
   362  		client: client,
   363  	}
   364  
   365  	expectedPrivate := []byte(`{"meta": "data"}`)
   366  
   367  	client.EXPECT().ImportResourceState(
   368  		gomock.Any(),
   369  		gomock.Any(),
   370  	).Return(&proto.ImportResourceState_Response{
   371  		ImportedResources: []*proto.ImportResourceState_ImportedResource{
   372  			{
   373  				TypeName: "resource",
   374  				State: &proto.DynamicValue{
   375  					Msgpack: []byte("\x81\xa4attr\xa3bar"),
   376  				},
   377  				Private: expectedPrivate,
   378  			},
   379  		},
   380  	}, nil)
   381  
   382  	resp := p.ImportResourceState(providers.ImportResourceStateRequest{
   383  		TypeName: "resource",
   384  		ID:       "foo",
   385  	})
   386  
   387  	checkDiags(t, resp.Diagnostics)
   388  
   389  	expectedResource := providers.ImportedResource{
   390  		TypeName: "resource",
   391  		State: cty.ObjectVal(map[string]cty.Value{
   392  			"attr": cty.StringVal("bar"),
   393  		}),
   394  		Private: expectedPrivate,
   395  	}
   396  
   397  	imported := resp.ImportedResources[0]
   398  	if !cmp.Equal(expectedResource, imported, typeComparer, valueComparer, equateEmpty) {
   399  		t.Fatal(cmp.Diff(expectedResource, imported, typeComparer, valueComparer, equateEmpty))
   400  	}
   401  }
   402  
   403  func TestGRPCProvider_ReadDataSource(t *testing.T) {
   404  	client := mockProviderClient(t)
   405  	p := &GRPCProvider{
   406  		client: client,
   407  	}
   408  
   409  	client.EXPECT().ReadDataSource(
   410  		gomock.Any(),
   411  		gomock.Any(),
   412  	).Return(&proto.ReadDataSource_Response{
   413  		State: &proto.DynamicValue{
   414  			Msgpack: []byte("\x81\xa4attr\xa3bar"),
   415  		},
   416  	}, nil)
   417  
   418  	resp := p.ReadDataSource(providers.ReadDataSourceRequest{
   419  		TypeName: "data",
   420  		Config: cty.ObjectVal(map[string]cty.Value{
   421  			"attr": cty.StringVal("foo"),
   422  		}),
   423  	})
   424  
   425  	checkDiags(t, resp.Diagnostics)
   426  
   427  	expected := cty.ObjectVal(map[string]cty.Value{
   428  		"attr": cty.StringVal("bar"),
   429  	})
   430  
   431  	if !cmp.Equal(expected, resp.State, typeComparer, valueComparer, equateEmpty) {
   432  		t.Fatal(cmp.Diff(expected, resp.State, typeComparer, valueComparer, equateEmpty))
   433  	}
   434  }