From b4551c09541e0d59eab8b9e8555243e8a0b7ec43 Mon Sep 17 00:00:00 2001 From: Rain Date: Tue, 1 Jul 2025 00:59:35 -0400 Subject: [PATCH] Adding to GetMetadata for ListResources, tests not passing yet for `internal/proto5server/server_getmetadata_test.go`, something is missing to add ListResource here --- .../proto5server/server_getmetadata_test.go | 179 ++++++++++++++++-- .../toproto5/list_resource_result_test.go | 76 ++++++++ .../toproto5/listresourcemetadata_test.go | 44 +++++ 3 files changed, 285 insertions(+), 14 deletions(-) create mode 100644 internal/toproto5/list_resource_result_test.go create mode 100644 internal/toproto5/listresourcemetadata_test.go diff --git a/internal/proto5server/server_getmetadata_test.go b/internal/proto5server/server_getmetadata_test.go index 9f8baf1fc..3b7edf5a2 100644 --- a/internal/proto5server/server_getmetadata_test.go +++ b/internal/proto5server/server_getmetadata_test.go @@ -5,6 +5,7 @@ package proto5server import ( "context" + "github.com/hashicorp/terraform-plugin-framework/list" "sort" "testing" @@ -63,6 +64,7 @@ func TestServerGetMetadata(t *testing.T) { }, }, EphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{}, Functions: []tfprotov5.FunctionMetadata{}, Resources: []tfprotov5.ResourceMetadata{}, ServerCapabilities: &tfprotov5.ServerCapabilities{ @@ -110,8 +112,9 @@ func TestServerGetMetadata(t *testing.T) { "This is always an issue with the provider and should be reported to the provider developers.", }, }, - Functions: []tfprotov5.FunctionMetadata{}, - Resources: []tfprotov5.ResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{}, + Functions: []tfprotov5.FunctionMetadata{}, + Resources: []tfprotov5.ResourceMetadata{}, ServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -149,8 +152,9 @@ func TestServerGetMetadata(t *testing.T) { "This is always an issue with the provider and should be reported to the provider developers.", }, }, - Functions: []tfprotov5.FunctionMetadata{}, - Resources: []tfprotov5.ResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{}, + Functions: []tfprotov5.FunctionMetadata{}, + Resources: []tfprotov5.ResourceMetadata{}, ServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -194,8 +198,9 @@ func TestServerGetMetadata(t *testing.T) { TypeName: "test_ephemeral_resource2", }, }, - Functions: []tfprotov5.FunctionMetadata{}, - Resources: []tfprotov5.ResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{}, + Functions: []tfprotov5.FunctionMetadata{}, + Resources: []tfprotov5.ResourceMetadata{}, ServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -241,8 +246,9 @@ func TestServerGetMetadata(t *testing.T) { "This is always an issue with the provider and should be reported to the provider developers.", }, }, - Functions: []tfprotov5.FunctionMetadata{}, - Resources: []tfprotov5.ResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{}, + Functions: []tfprotov5.FunctionMetadata{}, + Resources: []tfprotov5.ResourceMetadata{}, ServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -280,6 +286,141 @@ func TestServerGetMetadata(t *testing.T) { "This is always an issue with the provider and should be reported to the provider developers.", }, }, + ListResources: []tfprotov5.ListResourceMetadata{}, + Functions: []tfprotov5.FunctionMetadata{}, + Resources: []tfprotov5.ResourceMetadata{}, + ServerCapabilities: &tfprotov5.ServerCapabilities{ + GetProviderSchemaOptional: true, + MoveResourceState: true, + PlanDestroy: true, + }, + }, + }, + "listresources": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ListResourcesMethod: func(_ context.Context) []func() list.ListResource { + return []func() list.ListResource{ + func() list.ListResource { + return &testprovider.ListResource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_list_resource1" + }, + } + }, + func() list.ListResource { + return &testprovider.ListResource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_list_resource2" + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.GetMetadataRequest{}, + expectedResponse: &tfprotov5.GetMetadataResponse{ + DataSources: []tfprotov5.DataSourceMetadata{}, + EphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{ + { + TypeName: "test_list_resource1", + }, + { + TypeName: "test_list_resource2", + }, + }, + Functions: []tfprotov5.FunctionMetadata{}, + Resources: []tfprotov5.ResourceMetadata{}, + ServerCapabilities: &tfprotov5.ServerCapabilities{ + GetProviderSchemaOptional: true, + MoveResourceState: true, + PlanDestroy: true, + }, + }, + }, + "listresources-duplicate-type-name": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ListResourcesMethod: func(_ context.Context) []func() list.ListResource { + return []func() list.ListResource{ + func() list.ListResource { + return &testprovider.ListResource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_list_resource" + }, + } + }, + func() list.ListResource { + return &testprovider.ListResource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "test_list_resource" + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.GetMetadataRequest{}, + expectedResponse: &tfprotov5.GetMetadataResponse{ + DataSources: []tfprotov5.DataSourceMetadata{}, + EphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{}, + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "Duplicate List Resource Type Defined", + Detail: "The test_list_resource list resource type name was returned for multiple list resources. " + + "List resource type names must be unique. " + + "This is always an issue with the provider and should be reported to the provider developers.", + }, + }, + Functions: []tfprotov5.FunctionMetadata{}, + Resources: []tfprotov5.ResourceMetadata{}, + ServerCapabilities: &tfprotov5.ServerCapabilities{ + GetProviderSchemaOptional: true, + MoveResourceState: true, + PlanDestroy: true, + }, + }, + }, + "listresources-empty-type-name": { + server: &Server{ + FrameworkServer: fwserver.Server{ + Provider: &testprovider.Provider{ + ListResourcesMethod: func(_ context.Context) []func() list.ListResource { + return []func() list.ListResource{ + func() list.ListResource { + return &testprovider.ListResource{ + MetadataMethod: func(_ context.Context, _ resource.MetadataRequest, resp *resource.MetadataResponse) { + resp.TypeName = "" + }, + } + }, + } + }, + }, + }, + }, + request: &tfprotov5.GetMetadataRequest{}, + expectedResponse: &tfprotov5.GetMetadataResponse{ + DataSources: []tfprotov5.DataSourceMetadata{}, + EphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{}, + Diagnostics: []*tfprotov5.Diagnostic{ + { + Severity: tfprotov5.DiagnosticSeverityError, + Summary: "List Resource Type Name Missing", + Detail: "The *testprovider.ListResource ListResource returned an empty string from the Metadata method. " + + "This is always an issue with the provider and should be reported to the provider developers.", + }, + }, Functions: []tfprotov5.FunctionMetadata{}, Resources: []tfprotov5.ResourceMetadata{}, ServerCapabilities: &tfprotov5.ServerCapabilities{ @@ -318,6 +459,7 @@ func TestServerGetMetadata(t *testing.T) { expectedResponse: &tfprotov5.GetMetadataResponse{ DataSources: []tfprotov5.DataSourceMetadata{}, EphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{}, Functions: []tfprotov5.FunctionMetadata{ { Name: "function1", @@ -372,8 +514,9 @@ func TestServerGetMetadata(t *testing.T) { "This is always an issue with the provider and should be reported to the provider developers.", }, }, - Functions: []tfprotov5.FunctionMetadata{}, - Resources: []tfprotov5.ResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{}, + Functions: []tfprotov5.FunctionMetadata{}, + Resources: []tfprotov5.ResourceMetadata{}, ServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -411,8 +554,9 @@ func TestServerGetMetadata(t *testing.T) { "This is always an issue with the provider and should be reported to the provider developers.", }, }, - Functions: []tfprotov5.FunctionMetadata{}, - Resources: []tfprotov5.ResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{}, + Functions: []tfprotov5.FunctionMetadata{}, + Resources: []tfprotov5.ResourceMetadata{}, ServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -449,6 +593,7 @@ func TestServerGetMetadata(t *testing.T) { expectedResponse: &tfprotov5.GetMetadataResponse{ DataSources: []tfprotov5.DataSourceMetadata{}, EphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{}, Functions: []tfprotov5.FunctionMetadata{}, Resources: []tfprotov5.ResourceMetadata{ { @@ -503,8 +648,9 @@ func TestServerGetMetadata(t *testing.T) { "This is always an issue with the provider and should be reported to the provider developers.", }, }, - Functions: []tfprotov5.FunctionMetadata{}, - Resources: []tfprotov5.ResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{}, + Functions: []tfprotov5.FunctionMetadata{}, + Resources: []tfprotov5.ResourceMetadata{}, ServerCapabilities: &tfprotov5.ServerCapabilities{ GetProviderSchemaOptional: true, MoveResourceState: true, @@ -534,6 +680,7 @@ func TestServerGetMetadata(t *testing.T) { expectedResponse: &tfprotov5.GetMetadataResponse{ DataSources: []tfprotov5.DataSourceMetadata{}, EphemeralResources: []tfprotov5.EphemeralResourceMetadata{}, + ListResources: []tfprotov5.ListResourceMetadata{}, Diagnostics: []*tfprotov5.Diagnostic{ { Severity: tfprotov5.DiagnosticSeverityError, @@ -572,6 +719,10 @@ func TestServerGetMetadata(t *testing.T) { return got.EphemeralResources[i].TypeName < got.EphemeralResources[j].TypeName }) + sort.Slice(got.ListResources, func(i, j int) bool { + return got.ListResources[i].TypeName < got.ListResources[j].TypeName + }) + sort.Slice(got.Functions, func(i int, j int) bool { return got.Functions[i].Name < got.Functions[j].Name }) diff --git a/internal/toproto5/list_resource_result_test.go b/internal/toproto5/list_resource_result_test.go new file mode 100644 index 000000000..4f47b737f --- /dev/null +++ b/internal/toproto5/list_resource_result_test.go @@ -0,0 +1,76 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5_test + +import ( + "context" + "github.com/hashicorp/terraform-plugin-framework/internal/fwschema" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema" + "github.com/hashicorp/terraform-plugin-framework/tfsdk" + "github.com/hashicorp/terraform-plugin-framework/types" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +func TestListResourceResult(t *testing.T) { + t.Parallel() + + testListResultData := &fwserver.ListResult{ + Identity: nil, + Resource: &tfsdk.Resource{ + Schema: testschema.Schema{ + Attributes: map[string]fwschema.Attribute{ + "test_attribute": testschema.Attribute{ + Required: true, + Type: types.StringType, + }, + }, + }, + }, + DisplayName: "test-display-name", + Diagnostics: nil, + } + + testCases := map[string]struct { + input *fwserver.ListResult + expected tfprotov5.ListResourceResult + }{ + "nil": { + input: &fwserver.ListResult{ + Identity: nil, + Resource: nil, + DisplayName: "", + Diagnostics: nil, + }, + expected: tfprotov5.ListResourceResult{ + Identity: nil, + Resource: nil, + DisplayName: "", + Diagnostics: nil, + }, + }, + "valid": { + input: testListResultData, + expected: tfprotov5.ListResourceResult{ + DisplayName: "test-display-name", + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := toproto5.ListResourceResult(context.Background(), testCase.input) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +} diff --git a/internal/toproto5/listresourcemetadata_test.go b/internal/toproto5/listresourcemetadata_test.go new file mode 100644 index 000000000..1d8ff2055 --- /dev/null +++ b/internal/toproto5/listresourcemetadata_test.go @@ -0,0 +1,44 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package toproto5_test + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/hashicorp/terraform-plugin-framework/internal/fwserver" + "github.com/hashicorp/terraform-plugin-framework/internal/toproto5" + "github.com/hashicorp/terraform-plugin-go/tfprotov5" +) + +func TestListResourceMetadata(t *testing.T) { + t.Parallel() + + testCases := map[string]struct { + fw fwserver.ListResourceMetadata + expected tfprotov5.ListResourceMetadata + }{ + "TypeName": { + fw: fwserver.ListResourceMetadata{ + TypeName: "test", + }, + expected: tfprotov5.ListResourceMetadata{ + TypeName: "test", + }, + }, + } + + for name, testCase := range testCases { + t.Run(name, func(t *testing.T) { + t.Parallel() + + got := toproto5.ListResourceMetadata(context.Background(), testCase.fw) + + if diff := cmp.Diff(got, testCase.expected); diff != "" { + t.Errorf("unexpected difference: %s", diff) + } + }) + } +}