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 }