diff --git a/internal/fromproto6/readdatasource.go b/internal/fromproto6/readdatasource.go index e6b71ef4b..ec9031c4c 100644 --- a/internal/fromproto6/readdatasource.go +++ b/internal/fromproto6/readdatasource.go @@ -18,8 +18,23 @@ func ReadDataSourceRequest(ctx context.Context, proto6 *tfprotov6.ReadDataSource var diags diag.Diagnostics + // Panic prevention here to simplify the calling implementations. + // This should not happen, but just in case. + if dataSourceSchema == nil { + diags.AddError( + "Missing DataSource Schema", + "An unexpected error was encountered when handling the request. "+ + "This is always an issue in the Terraform Provider SDK used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Missing schema.", + ) + + return nil, diags + } + fw := &fwserver.ReadDataSourceRequest{ - DataSourceType: dataSourceType, + DataSourceSchema: *dataSourceSchema, + DataSourceType: dataSourceType, } config, configDiags := Config(ctx, proto6.Config, dataSourceSchema) diff --git a/internal/fromproto6/readdatasource_test.go b/internal/fromproto6/readdatasource_test.go index 3d96a2b12..c40edbe15 100644 --- a/internal/fromproto6/readdatasource_test.go +++ b/internal/fromproto6/readdatasource_test.go @@ -56,17 +56,26 @@ func TestReadDataSourceRequest(t *testing.T) { }, "empty": { input: &tfprotov6.ReadDataSourceRequest{}, - expected: &fwserver.ReadDataSourceRequest{}, + expected: nil, + expectedDiagnostics: diag.Diagnostics{ + diag.NewErrorDiagnostic( + "Missing DataSource Schema", + "An unexpected error was encountered when handling the request. "+ + "This is always an issue in the Terraform Provider SDK used to implement the provider and should be reported to the provider developers.\n\n"+ + "Please report this to the provider developer:\n\n"+ + "Missing schema.", + ), + }, }, "config-missing-schema": { input: &tfprotov6.ReadDataSourceRequest{ Config: &testProto6DynamicValue, }, - expected: &fwserver.ReadDataSourceRequest{}, + expected: nil, expectedDiagnostics: diag.Diagnostics{ diag.NewErrorDiagnostic( - "Unable to Convert Configuration", - "An unexpected error was encountered when converting the configuration from the protocol type. "+ + "Missing DataSource Schema", + "An unexpected error was encountered when handling the request. "+ "This is always an issue in the Terraform Provider SDK used to implement the provider and should be reported to the provider developers.\n\n"+ "Please report this to the provider developer:\n\n"+ "Missing schema.", @@ -83,12 +92,15 @@ func TestReadDataSourceRequest(t *testing.T) { Raw: testProto6Value, Schema: *testFwSchema, }, + DataSourceSchema: *testFwSchema, }, }, "providermeta-missing-data": { input: &tfprotov6.ReadDataSourceRequest{}, + dataSourceSchema: testFwSchema, providerMetaSchema: testFwSchema, expected: &fwserver.ReadDataSourceRequest{ + DataSourceSchema: *testFwSchema, ProviderMeta: &tfsdk.Config{ Raw: tftypes.NewValue(testProto6Type, nil), Schema: *testFwSchema, @@ -99,7 +111,9 @@ func TestReadDataSourceRequest(t *testing.T) { input: &tfprotov6.ReadDataSourceRequest{ ProviderMeta: &testProto6DynamicValue, }, + dataSourceSchema: testFwSchema, expected: &fwserver.ReadDataSourceRequest{ + DataSourceSchema: *testFwSchema, // This intentionally should not include ProviderMeta }, }, @@ -107,8 +121,10 @@ func TestReadDataSourceRequest(t *testing.T) { input: &tfprotov6.ReadDataSourceRequest{ ProviderMeta: &testProto6DynamicValue, }, + dataSourceSchema: testFwSchema, providerMetaSchema: testFwSchema, expected: &fwserver.ReadDataSourceRequest{ + DataSourceSchema: *testFwSchema, ProviderMeta: &tfsdk.Config{ Raw: testProto6Value, Schema: *testFwSchema, diff --git a/internal/fwserver/server_readdatasource.go b/internal/fwserver/server_readdatasource.go index f0a55d4d3..1640ce3ce 100644 --- a/internal/fwserver/server_readdatasource.go +++ b/internal/fwserver/server_readdatasource.go @@ -11,9 +11,10 @@ import ( // ReadDataSourceRequest is the framework server request for the // ReadDataSource RPC. type ReadDataSourceRequest struct { - Config *tfsdk.Config - DataSourceType tfsdk.DataSourceType - ProviderMeta *tfsdk.Config + Config *tfsdk.Config + DataSourceSchema tfsdk.Schema + DataSourceType tfsdk.DataSourceType + ProviderMeta *tfsdk.Config } // ReadDataSourceResponse is the framework server response for the @@ -40,10 +41,14 @@ func (s *Server) ReadDataSource(ctx context.Context, req *ReadDataSourceRequest, return } - readReq := tfsdk.ReadDataSourceRequest{} + readReq := tfsdk.ReadDataSourceRequest{ + Config: tfsdk.Config{ + Schema: req.DataSourceSchema, + }, + } readResp := tfsdk.ReadDataSourceResponse{ State: tfsdk.State{ - Schema: req.Config.Schema, + Schema: req.DataSourceSchema, }, } diff --git a/internal/fwserver/server_readdatasource_test.go b/internal/fwserver/server_readdatasource_test.go index 846870d70..c7d49eb1e 100644 --- a/internal/fwserver/server_readdatasource_test.go +++ b/internal/fwserver/server_readdatasource_test.go @@ -5,32 +5,208 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" - "github.com/hashicorp/terraform-plugin-framework/internal/testing/emptyprovider" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testprovider" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "github.com/hashicorp/terraform-plugin-go/tftypes" ) -// TODO: Migrate tfsdk.Provider bits of proto6server.testProviderServer to -// new internal/testing/provider.Provider that allows customization of all -// method implementations via struct fields. Then, create additional test -// cases in this unit test. -// -// For now this testing is covered by proto6server.ReadDataSource. -// -// Reference: https://github.com/hashicorp/terraform-plugin-framework/issues/215 func TestServerReadDataSource(t *testing.T) { t.Parallel() + testType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_computed": tftypes.String, + "test_required": tftypes.String, + }, + } + + testConfigValue := tftypes.NewValue(testType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }) + + testStateValue := tftypes.NewValue(testType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "test-state-value"), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }) + + testSchema := tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test_computed": { + Computed: true, + Type: types.StringType, + }, + "test_required": { + Required: true, + Type: types.StringType, + }, + }, + } + + testConfig := &tfsdk.Config{ + Raw: testConfigValue, + Schema: testSchema, + } + + testStateUnchanged := &tfsdk.State{ + Raw: testConfigValue, + Schema: testSchema, + } + + testState := &tfsdk.State{ + Raw: testStateValue, + Schema: testSchema, + } + testCases := map[string]struct { server *fwserver.Server request *fwserver.ReadDataSourceRequest expectedResponse *fwserver.ReadDataSourceResponse }{ - "empty-provider": { + "nil": { server: &fwserver.Server{ - Provider: &emptyprovider.Provider{}, + Provider: &testprovider.Provider{}, }, expectedResponse: &fwserver.ReadDataSourceResponse{}, }, + "request-config": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadDataSourceRequest{ + Config: testConfig, + DataSourceSchema: testSchema, + DataSourceType: &testprovider.DataSourceType{ + GetSchemaMethod: func(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return testSchema, nil + }, + NewDataSourceMethod: func(_ context.Context, _ tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return &testprovider.DataSource{ + ReadMethod: func(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { + var config struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) + + if config.TestRequired.Value != "test-config-value" { + resp.Diagnostics.AddError("unexpected req.Config value: %s", config.TestRequired.Value) + } + }, + }, nil + }, + }, + }, + expectedResponse: &fwserver.ReadDataSourceResponse{ + State: testStateUnchanged, + }, + }, + "request-providermeta": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadDataSourceRequest{ + Config: testConfig, + DataSourceSchema: testSchema, + DataSourceType: &testprovider.DataSourceType{ + GetSchemaMethod: func(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return testSchema, nil + }, + NewDataSourceMethod: func(_ context.Context, _ tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return &testprovider.DataSource{ + ReadMethod: func(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { + var config struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.ProviderMeta.Get(ctx, &config)...) + + if config.TestRequired.Value != "test-config-value" { + resp.Diagnostics.AddError("unexpected req.ProviderMeta value: %s", config.TestRequired.Value) + } + }, + }, nil + }, + }, + ProviderMeta: testConfig, + }, + expectedResponse: &fwserver.ReadDataSourceResponse{ + State: testStateUnchanged, + }, + }, + "response-diagnostics": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadDataSourceRequest{ + Config: testConfig, + DataSourceSchema: testSchema, + DataSourceType: &testprovider.DataSourceType{ + GetSchemaMethod: func(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return testSchema, nil + }, + NewDataSourceMethod: func(_ context.Context, _ tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return &testprovider.DataSource{ + ReadMethod: func(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { + resp.Diagnostics.AddWarning("warning summary", "warning detail") + resp.Diagnostics.AddError("error summary", "error detail") + }, + }, nil + }, + }, + }, + expectedResponse: &fwserver.ReadDataSourceResponse{ + Diagnostics: diag.Diagnostics{ + diag.NewWarningDiagnostic( + "warning summary", + "warning detail", + ), + diag.NewErrorDiagnostic( + "error summary", + "error detail", + ), + }, + State: testStateUnchanged, + }, + }, + "response-state": { + server: &fwserver.Server{ + Provider: &testprovider.Provider{}, + }, + request: &fwserver.ReadDataSourceRequest{ + Config: testConfig, + DataSourceSchema: testSchema, + DataSourceType: &testprovider.DataSourceType{ + GetSchemaMethod: func(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return testSchema, nil + }, + NewDataSourceMethod: func(_ context.Context, _ tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return &testprovider.DataSource{ + ReadMethod: func(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { + var data struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } + + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) + + data.TestComputed = types.String{Value: "test-state-value"} + + resp.Diagnostics.Append(resp.State.Set(ctx, &data)...) + }, + }, nil + }, + }, + }, + expectedResponse: &fwserver.ReadDataSourceResponse{ + State: testState, + }, + }, } for name, testCase := range testCases { diff --git a/internal/proto6server/serve_data_source_one_test.go b/internal/proto6server/serve_data_source_one_test.go deleted file mode 100644 index 802e73dab..000000000 --- a/internal/proto6server/serve_data_source_one_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package proto6server - -import ( - "context" - "fmt" - - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" -) - -type testServeDataSourceTypeOne struct{} - -func (dt testServeDataSourceTypeOne) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "current_time": { - Type: types.StringType, - Computed: true, - }, - "current_date": { - Type: types.StringType, - Computed: true, - }, - "is_dst": { - Type: types.BoolType, - Computed: true, - }, - }, - }, nil -} - -func (dt testServeDataSourceTypeOne) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { - provider, ok := p.(*testServeProvider) - if !ok { - prov, ok := p.(*testServeProviderWithMetaSchema) - if !ok { - panic(fmt.Sprintf("unexpected provider type %T", p)) - } - provider = prov.testServeProvider - } - return testServeDataSourceOne{ - provider: provider, - }, nil -} - -var testServeDataSourceTypeOneType = tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "current_date": tftypes.String, - "current_time": tftypes.String, - "is_dst": tftypes.Bool, - }, -} - -type testServeDataSourceOne struct { - provider *testServeProvider -} - -func (r testServeDataSourceOne) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { - r.provider.readDataSourceConfigValue = req.Config.Raw - r.provider.readDataSourceConfigSchema = req.Config.Schema - r.provider.readDataSourceProviderMetaValue = req.ProviderMeta.Raw - r.provider.readDataSourceProviderMetaSchema = req.ProviderMeta.Schema - r.provider.readDataSourceCalledDataSourceType = "test_one" - r.provider.readDataSourceImpl(ctx, req, resp) -} diff --git a/internal/proto6server/serve_data_source_two_test.go b/internal/proto6server/serve_data_source_two_test.go deleted file mode 100644 index 33ae711e0..000000000 --- a/internal/proto6server/serve_data_source_two_test.go +++ /dev/null @@ -1,69 +0,0 @@ -package proto6server - -import ( - "context" - "fmt" - - "github.com/hashicorp/terraform-plugin-framework/diag" - "github.com/hashicorp/terraform-plugin-framework/tfsdk" - "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/hashicorp/terraform-plugin-go/tftypes" -) - -type testServeDataSourceTypeTwo struct{} - -func (dt testServeDataSourceTypeTwo) GetSchema(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { - return tfsdk.Schema{ - Attributes: map[string]tfsdk.Attribute{ - "family": { - Type: types.StringType, - Optional: true, - Computed: true, - }, - "name": { - Type: types.StringType, - Optional: true, - Computed: true, - }, - "id": { - Type: types.StringType, - Computed: true, - }, - }, - }, nil -} - -func (dt testServeDataSourceTypeTwo) NewDataSource(_ context.Context, p tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { - provider, ok := p.(*testServeProvider) - if !ok { - prov, ok := p.(*testServeProviderWithMetaSchema) - if !ok { - panic(fmt.Sprintf("unexpected provider type %T", p)) - } - provider = prov.testServeProvider - } - return testServeDataSourceTwo{ - provider: provider, - }, nil -} - -var testServeDataSourceTypeTwoType = tftypes.Object{ - AttributeTypes: map[string]tftypes.Type{ - "family": tftypes.String, - "name": tftypes.String, - "id": tftypes.String, - }, -} - -type testServeDataSourceTwo struct { - provider *testServeProvider -} - -func (r testServeDataSourceTwo) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { - r.provider.readDataSourceConfigValue = req.Config.Raw - r.provider.readDataSourceConfigSchema = req.Config.Schema - r.provider.readDataSourceProviderMetaValue = req.ProviderMeta.Raw - r.provider.readDataSourceProviderMetaSchema = req.ProviderMeta.Schema - r.provider.readDataSourceCalledDataSourceType = "test_two" - r.provider.readDataSourceImpl(ctx, req, resp) -} diff --git a/internal/proto6server/serve_provider_test.go b/internal/proto6server/serve_provider_test.go index 4c230ccb0..0ff09b510 100644 --- a/internal/proto6server/serve_provider_test.go +++ b/internal/proto6server/serve_provider_test.go @@ -307,10 +307,7 @@ func (t *testServeProvider) GetResources(_ context.Context) (map[string]tfsdk.Re } func (t *testServeProvider) GetDataSources(_ context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { - return map[string]tfsdk.DataSourceType{ - "test_one": testServeDataSourceTypeOne{}, - "test_two": testServeDataSourceTypeTwo{}, - }, nil + return map[string]tfsdk.DataSourceType{}, nil } func (t *testServeProvider) Configure(_ context.Context, req tfsdk.ConfigureProviderRequest, _ *tfsdk.ConfigureProviderResponse) { diff --git a/internal/proto6server/server_readdatasource_test.go b/internal/proto6server/server_readdatasource_test.go index b9ba79a4c..37e97004d 100644 --- a/internal/proto6server/server_readdatasource_test.go +++ b/internal/proto6server/server_readdatasource_test.go @@ -5,8 +5,11 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testprovider" "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-go/tfprotov6" "github.com/hashicorp/terraform-plugin-go/tftypes" ) @@ -14,248 +17,257 @@ import ( func TestServerReadDataSource(t *testing.T) { t.Parallel() - type testCase struct { - // request input - config tftypes.Value - providerMeta tftypes.Value - dataSource string - dataSourceType tftypes.Type + testType := tftypes.Object{ + AttributeTypes: map[string]tftypes.Type{ + "test_computed": tftypes.String, + "test_required": tftypes.String, + }, + } - impl func(context.Context, tfsdk.ReadDataSourceRequest, *tfsdk.ReadDataSourceResponse) + testConfigDynamicValue := testNewDynamicValue(t, testType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, nil), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }) - // response expectations - expectedNewState tftypes.Value - expectedDiags []*tfprotov6.Diagnostic - } + testEmptyDynamicValue := testNewDynamicValue(t, tftypes.Object{}, nil) - tests := map[string]testCase{ - "one_basic": { - config: tftypes.NewValue(testServeDataSourceTypeOneType, map[string]tftypes.Value{ - "current_date": tftypes.NewValue(tftypes.String, nil), - "current_time": tftypes.NewValue(tftypes.String, nil), - "is_dst": tftypes.NewValue(tftypes.Bool, nil), - }), - dataSource: "test_one", - dataSourceType: testServeDataSourceTypeOneType, + testStateDynamicValue := testNewDynamicValue(t, testType, map[string]tftypes.Value{ + "test_computed": tftypes.NewValue(tftypes.String, "test-state-value"), + "test_required": tftypes.NewValue(tftypes.String, "test-config-value"), + }) - impl: func(_ context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { - resp.State.Raw = tftypes.NewValue(testServeDataSourceTypeOneType, map[string]tftypes.Value{ - "current_date": tftypes.NewValue(tftypes.String, "today"), - "current_time": tftypes.NewValue(tftypes.String, "now"), - "is_dst": tftypes.NewValue(tftypes.Bool, true), - }) + testSchema := tfsdk.Schema{ + Attributes: map[string]tfsdk.Attribute{ + "test_computed": { + Computed: true, + Type: types.StringType, + }, + "test_required": { + Required: true, + Type: types.StringType, }, + }, + } - expectedNewState: tftypes.NewValue(testServeDataSourceTypeOneType, map[string]tftypes.Value{ - "current_date": tftypes.NewValue(tftypes.String, "today"), - "current_time": tftypes.NewValue(tftypes.String, "now"), - "is_dst": tftypes.NewValue(tftypes.Bool, true), - }), + testCases := map[string]struct { + server *Server + request *tfprotov6.ReadDataSourceRequest + expectedError error + expectedResponse *tfprotov6.ReadDataSourceResponse + }{ + "no-schema": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + GetDataSourcesMethod: func(_ context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { + return map[string]tfsdk.DataSourceType{ + "test_data_source": &testprovider.DataSourceType{ + GetSchemaMethod: func(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{}, nil + }, + NewDataSourceMethod: func(_ context.Context, _ tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return &testprovider.DataSource{}, nil + }, + }, + }, nil + }, + }, + }, + }, + request: &tfprotov6.ReadDataSourceRequest{ + Config: testEmptyDynamicValue, + TypeName: "test_data_source", + }, + expectedResponse: &tfprotov6.ReadDataSourceResponse{ + State: testEmptyDynamicValue, + }, }, - "one_provider_meta": { - config: tftypes.NewValue(testServeDataSourceTypeOneType, map[string]tftypes.Value{ - "current_date": tftypes.NewValue(tftypes.String, nil), - "current_time": tftypes.NewValue(tftypes.String, nil), - "is_dst": tftypes.NewValue(tftypes.Bool, nil), - }), - dataSource: "test_one", - dataSourceType: testServeDataSourceTypeOneType, + "request-config": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + GetDataSourcesMethod: func(_ context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { + return map[string]tfsdk.DataSourceType{ + "test_data_source": &testprovider.DataSourceType{ + GetSchemaMethod: func(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return testSchema, nil + }, + NewDataSourceMethod: func(_ context.Context, _ tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return &testprovider.DataSource{ + ReadMethod: func(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { + var config struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } - providerMeta: tftypes.NewValue(testServeProviderMetaType, map[string]tftypes.Value{ - "foo": tftypes.NewValue(tftypes.String, "my provider_meta value"), - }), + resp.Diagnostics.Append(req.Config.Get(ctx, &config)...) - impl: func(_ context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { - resp.State.Raw = tftypes.NewValue(testServeDataSourceTypeOneType, map[string]tftypes.Value{ - "current_date": tftypes.NewValue(tftypes.String, "today"), - "current_time": tftypes.NewValue(tftypes.String, "now"), - "is_dst": tftypes.NewValue(tftypes.Bool, true), - }) + if config.TestRequired.Value != "test-config-value" { + resp.Diagnostics.AddError("unexpected req.Config value: %s", config.TestRequired.Value) + } + }, + }, nil + }, + }, + }, nil + }, + }, + }, + }, + request: &tfprotov6.ReadDataSourceRequest{ + Config: testConfigDynamicValue, + TypeName: "test_data_source", + }, + expectedResponse: &tfprotov6.ReadDataSourceResponse{ + State: testConfigDynamicValue, }, - - expectedNewState: tftypes.NewValue(testServeDataSourceTypeOneType, map[string]tftypes.Value{ - "current_date": tftypes.NewValue(tftypes.String, "today"), - "current_time": tftypes.NewValue(tftypes.String, "now"), - "is_dst": tftypes.NewValue(tftypes.Bool, true), - }), }, - "one_remove": { - config: tftypes.NewValue(testServeDataSourceTypeOneType, map[string]tftypes.Value{ - "current_date": tftypes.NewValue(tftypes.String, nil), - "current_time": tftypes.NewValue(tftypes.String, nil), - "is_dst": tftypes.NewValue(tftypes.Bool, nil), - }), - dataSource: "test_one", - dataSourceType: testServeDataSourceTypeOneType, + "request-providermeta": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.ProviderWithProviderMeta{ + Provider: &testprovider.Provider{ + GetDataSourcesMethod: func(_ context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { + return map[string]tfsdk.DataSourceType{ + "test_data_source": &testprovider.DataSourceType{ + GetSchemaMethod: func(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return tfsdk.Schema{}, nil + }, + NewDataSourceMethod: func(_ context.Context, _ tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return &testprovider.DataSource{ + ReadMethod: func(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { + var config struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } - impl: func(_ context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { - resp.State.Raw = tftypes.NewValue(testServeDataSourceTypeOneType, nil) - }, + resp.Diagnostics.Append(req.ProviderMeta.Get(ctx, &config)...) - expectedNewState: tftypes.NewValue(testServeDataSourceTypeOneType, nil), + if config.TestRequired.Value != "test-config-value" { + resp.Diagnostics.AddError("unexpected req.ProviderMeta value: %s", config.TestRequired.Value) + } + }, + }, nil + }, + }, + }, nil + }, + }, + GetMetaSchemaMethod: func(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return testSchema, nil + }, + }, + }, + }, + request: &tfprotov6.ReadDataSourceRequest{ + Config: testEmptyDynamicValue, + ProviderMeta: testConfigDynamicValue, + TypeName: "test_data_source", + }, + expectedResponse: &tfprotov6.ReadDataSourceResponse{ + State: testEmptyDynamicValue, + }, }, - "two_basic": { - config: tftypes.NewValue(testServeDataSourceTypeTwoType, map[string]tftypes.Value{ - "family": tftypes.NewValue(tftypes.String, "123foo"), - "name": tftypes.NewValue(tftypes.String, "123foo-askjgsio"), - "id": tftypes.NewValue(tftypes.String, nil), - }), - dataSource: "test_two", - dataSourceType: testServeDataSourceTypeTwoType, - - impl: func(_ context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { - resp.State.Raw = tftypes.NewValue(testServeDataSourceTypeTwoType, map[string]tftypes.Value{ - "family": tftypes.NewValue(tftypes.String, "123foo"), - "name": tftypes.NewValue(tftypes.String, "123foo-askjgsio"), - "id": tftypes.NewValue(tftypes.String, "a random id or something I dunno"), - }) + "response-diagnostics": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + GetDataSourcesMethod: func(_ context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { + return map[string]tfsdk.DataSourceType{ + "test_data_source": &testprovider.DataSourceType{ + GetSchemaMethod: func(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return testSchema, nil + }, + NewDataSourceMethod: func(_ context.Context, _ tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return &testprovider.DataSource{ + ReadMethod: func(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { + resp.Diagnostics.AddWarning("warning summary", "warning detail") + resp.Diagnostics.AddError("error summary", "error detail") + }, + }, nil + }, + }, + }, nil + }, + }, + }, + }, + request: &tfprotov6.ReadDataSourceRequest{ + Config: testConfigDynamicValue, + TypeName: "test_data_source", + }, + expectedResponse: &tfprotov6.ReadDataSourceResponse{ + Diagnostics: []*tfprotov6.Diagnostic{ + { + Severity: tfprotov6.DiagnosticSeverityWarning, + Summary: "warning summary", + Detail: "warning detail", + }, + { + Severity: tfprotov6.DiagnosticSeverityError, + Summary: "error summary", + Detail: "error detail", + }, + }, + State: testConfigDynamicValue, }, - - expectedNewState: tftypes.NewValue(testServeDataSourceTypeTwoType, map[string]tftypes.Value{ - "family": tftypes.NewValue(tftypes.String, "123foo"), - "name": tftypes.NewValue(tftypes.String, "123foo-askjgsio"), - "id": tftypes.NewValue(tftypes.String, "a random id or something I dunno"), - }), }, - "two_diags": { - config: tftypes.NewValue(testServeDataSourceTypeTwoType, map[string]tftypes.Value{ - "family": tftypes.NewValue(tftypes.String, "123foo"), - "name": tftypes.NewValue(tftypes.String, "123foo-askjgsio"), - "id": tftypes.NewValue(tftypes.String, nil), - }), - dataSource: "test_two", - dataSourceType: testServeDataSourceTypeTwoType, + "response-state": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + GetDataSourcesMethod: func(_ context.Context) (map[string]tfsdk.DataSourceType, diag.Diagnostics) { + return map[string]tfsdk.DataSourceType{ + "test_data_source": &testprovider.DataSourceType{ + GetSchemaMethod: func(_ context.Context) (tfsdk.Schema, diag.Diagnostics) { + return testSchema, nil + }, + NewDataSourceMethod: func(_ context.Context, _ tfsdk.Provider) (tfsdk.DataSource, diag.Diagnostics) { + return &testprovider.DataSource{ + ReadMethod: func(ctx context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { + var data struct { + TestComputed types.String `tfsdk:"test_computed"` + TestRequired types.String `tfsdk:"test_required"` + } - impl: func(_ context.Context, req tfsdk.ReadDataSourceRequest, resp *tfsdk.ReadDataSourceResponse) { - resp.State.Raw = tftypes.NewValue(testServeDataSourceTypeTwoType, map[string]tftypes.Value{ - "family": tftypes.NewValue(tftypes.String, "123foo"), - "name": tftypes.NewValue(tftypes.String, "123foo-askjgsio"), - "id": tftypes.NewValue(tftypes.String, "a random id or something I dunno"), - }) - resp.Diagnostics.AddAttributeWarning( - tftypes.NewAttributePath().WithAttributeName("disks").WithElementKeyInt(0), - "This is a warning", - "This is your final warning", - ) - resp.Diagnostics.AddError( - "This is an error", - "Oops.", - ) - }, + resp.Diagnostics.Append(req.Config.Get(ctx, &data)...) - expectedNewState: tftypes.NewValue(testServeDataSourceTypeTwoType, map[string]tftypes.Value{ - "family": tftypes.NewValue(tftypes.String, "123foo"), - "name": tftypes.NewValue(tftypes.String, "123foo-askjgsio"), - "id": tftypes.NewValue(tftypes.String, "a random id or something I dunno"), - }), + data.TestComputed = types.String{Value: "test-state-value"} - expectedDiags: []*tfprotov6.Diagnostic{ - { - Summary: "This is a warning", - Severity: tfprotov6.DiagnosticSeverityWarning, - Detail: "This is your final warning", - Attribute: tftypes.NewAttributePath().WithAttributeName("disks").WithElementKeyInt(0), - }, - { - Summary: "This is an error", - Severity: tfprotov6.DiagnosticSeverityError, - Detail: "Oops.", + resp.Diagnostics.Append(resp.State.Set(ctx, data)...) + }, + }, nil + }, + }, + }, nil + }, + }, }, }, + request: &tfprotov6.ReadDataSourceRequest{ + Config: testConfigDynamicValue, + TypeName: "test_data_source", + }, + expectedResponse: &tfprotov6.ReadDataSourceResponse{ + State: testStateDynamicValue, + }, }, } - for name, tc := range tests { - name, tc := name, tc + for name, testCase := range testCases { + name, testCase := name, testCase t.Run(name, func(t *testing.T) { t.Parallel() - s := &testServeProvider{ - readDataSourceImpl: tc.impl, - } - testServer := &Server{ - FrameworkServer: fwserver.Server{ - Provider: s, - }, - } - var pmSchema tfsdk.Schema - if tc.providerMeta.Type() != nil { - testServer.FrameworkServer.Provider = &testServeProviderWithMetaSchema{s} - schema, diags := testServer.FrameworkServer.ProviderMetaSchema(context.Background()) - if len(diags) > 0 { - t.Errorf("Unexpected diags: %+v", diags) - return - } - pmSchema = *schema - } + got, err := testCase.server.ReadDataSource(context.Background(), testCase.request) - rt, diags := testServer.FrameworkServer.DataSourceType(context.Background(), tc.dataSource) - if len(diags) > 0 { - t.Errorf("Unexpected diags: %+v", diags) - return - } - schema, diags := rt.GetSchema(context.Background()) - if len(diags) > 0 { - t.Errorf("Unexpected diags: %+v", diags) - return + if diff := cmp.Diff(testCase.expectedError, err); diff != "" { + t.Errorf("unexpected error difference: %s", diff) } - dv, err := tfprotov6.NewDynamicValue(tc.dataSourceType, tc.config) - if err != nil { - t.Errorf("Unexpected error: %s", err) - return - } - req := &tfprotov6.ReadDataSourceRequest{ - TypeName: tc.dataSource, - Config: &dv, - } - if tc.providerMeta.Type() != nil { - providerMeta, err := tfprotov6.NewDynamicValue(testServeProviderMetaType, tc.providerMeta) - if err != nil { - t.Errorf("Unexpected error: %s", err) - return - } - req.ProviderMeta = &providerMeta - } - got, err := testServer.ReadDataSource(context.Background(), req) - if err != nil { - t.Errorf("Unexpected error: %s", err) - return - } - if s.readDataSourceCalledDataSourceType != tc.dataSource { - t.Errorf("Called wrong dataSource. Expected to call %q, actually called %q", tc.dataSource, s.readDataSourceCalledDataSourceType) - return - } - if diff := cmp.Diff(got.Diagnostics, tc.expectedDiags); diff != "" { - t.Errorf("Unexpected diff in diagnostics (+wanted, -got): %s", diff) - } - if diff := cmp.Diff(s.readDataSourceConfigValue, tc.config); diff != "" { - t.Errorf("Unexpected diff in config (+wanted, -got): %s", diff) - return - } - if diff := cmp.Diff(s.readDataSourceConfigSchema, schema); diff != "" { - t.Errorf("Unexpected diff in config schema (+wanted, -got): %s", diff) - return - } - if tc.providerMeta.Type() != nil { - if diff := cmp.Diff(s.readDataSourceProviderMetaValue, tc.providerMeta); diff != "" { - t.Errorf("Unexpected diff in provider meta (+wanted, -got): %s", diff) - return - } - if diff := cmp.Diff(s.readDataSourceProviderMetaSchema, pmSchema); diff != "" { - t.Errorf("Unexpected diff in provider meta schema (+wanted, -got): %s", diff) - return - } - } - gotNewState, err := got.State.Unmarshal(tc.dataSourceType) - if err != nil { - t.Errorf("Unexpected error: %s", err) - return - } - if diff := cmp.Diff(gotNewState, tc.expectedNewState); diff != "" { - t.Errorf("Unexpected diff in new state (+wanted, -got): %s", diff) - return + if diff := cmp.Diff(testCase.expectedResponse, got); diff != "" { + t.Errorf("unexpected response difference: %s", diff) } }) } diff --git a/internal/testing/testprovider/datasource.go b/internal/testing/testprovider/datasource.go index a04eb2fd0..061004dc0 100644 --- a/internal/testing/testprovider/datasource.go +++ b/internal/testing/testprovider/datasource.go @@ -20,5 +20,5 @@ func (d *DataSource) Read(ctx context.Context, req tfsdk.ReadDataSourceRequest, return } - d.Read(ctx, req, resp) + d.ReadMethod(ctx, req, resp) }