From 846f5460faa7104f33de9963d1586b986a134525 Mon Sep 17 00:00:00 2001 From: Nithya Subramanian <98416062+nithyatsu@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:13:10 -0800 Subject: [PATCH] Resource type create - fixes (#8248) # Description Fixes below issues in resource-type create - APIversion for the created resource-type was blank. - Display of the resource-type created at the end of the command was incorrect - Added UTs that would have caught these issues. - sample manifest updated to have just one resource-type ## Type of change - This pull request fixes a bug in Radius and has an approved issue (issue link required). - This pull request is a minor refactor, code cleanup, test improvement, or other maintenance task and doesn't change the functionality of Radius (issue link optional) Fixes: #issue_number ## Contributor checklist Please verify that the PR meets the following requirements, where applicable: - [ NA ] An overview of proposed schema changes is included in a linked GitHub issue. - [ NA ] A design document PR is created in the [design-notes repository](https://github.com/radius-project/design-notes/), if new APIs are being introduced. - [ NA ] If applicable, design document has been reviewed and approved by Radius maintainers/approvers. - [ NA ] A PR for the [samples repository](https://github.com/radius-project/samples) is created, if existing samples are affected by the changes in this PR. - [ NA ] A PR for the [documentation repository](https://github.com/radius-project/docs) is created, if the changes in this PR affect the documentation or any user facing updates are made. - [ NA ] A PR for the [recipes repository](https://github.com/radius-project/recipes) is created, if existing recipes are affected by the changes in this PR. --- pkg/cli/clients/errors.go | 10 ++ pkg/cli/clients/errors_test.go | 6 ++ .../cmd/resourcetype/common/resourcetype.go | 26 ++++++ .../resourcetype/common/resourcetype_test.go | 90 ++++++++++++++++++ pkg/cli/cmd/resourcetype/create/create.go | 44 +++++---- .../cmd/resourcetype/create/create_test.go | 93 +++++++++++++++---- .../resourcetype/create/testdata/valid.yaml | 11 +-- pkg/cli/cmd/resourcetype/show/show.go | 22 +---- pkg/cli/manifest/registermanifest.go | 15 +++ pkg/cli/manifest/registermanifest_test.go | 82 ++++++++++++++++ 10 files changed, 339 insertions(+), 60 deletions(-) create mode 100644 pkg/cli/cmd/resourcetype/common/resourcetype_test.go diff --git a/pkg/cli/clients/errors.go b/pkg/cli/clients/errors.go index 5dd20fee58..ea81e286e8 100644 --- a/pkg/cli/clients/errors.go +++ b/pkg/cli/clients/errors.go @@ -20,12 +20,17 @@ import ( "encoding/json" "errors" "net/http" + "strings" "github.com/Azure/azure-sdk-for-go/sdk/azcore" v1 "github.com/radius-project/radius/pkg/armrpc/api/v1" "github.com/radius-project/radius/pkg/corerp/api/v20231001preview" ) +const ( + fakeServerNotFoundResponse = "unexpected status code 404. acceptable values are http.StatusOK" +) + // Is404Error returns true if the error is a 404 payload from an autorest operation. // @@ -38,6 +43,11 @@ func Is404Error(err error) bool { return false } + // NotFound Response from Fake Server - used for testing + if strings.Contains(err.Error(), fakeServerNotFoundResponse) { + return true + } + // The error might already be an ResponseError responseError := &azcore.ResponseError{} if errors.As(err, &responseError) && responseError.ErrorCode == v1.CodeNotFound || responseError.StatusCode == http.StatusNotFound { diff --git a/pkg/cli/clients/errors_test.go b/pkg/cli/clients/errors_test.go index c9f8626320..0a9f3beb13 100644 --- a/pkg/cli/clients/errors_test.go +++ b/pkg/cli/clients/errors_test.go @@ -62,4 +62,10 @@ func TestIs404Error(t *testing.T) { if Is404Error(nil) { t.Errorf("Expected Is404Error to return false for nil error, but it returned true") } + + // Test with a fake server not found response + err = errors.New(fakeServerNotFoundResponse) + if !Is404Error(err) { + t.Errorf("Expected Is404Error to return true for fake server not found response, but it returned false") + } } diff --git a/pkg/cli/cmd/resourcetype/common/resourcetype.go b/pkg/cli/cmd/resourcetype/common/resourcetype.go index cf806978ea..fee2058e5d 100644 --- a/pkg/cli/cmd/resourcetype/common/resourcetype.go +++ b/pkg/cli/cmd/resourcetype/common/resourcetype.go @@ -17,6 +17,11 @@ limitations under the License. package common import ( + "context" + "slices" + + "github.com/radius-project/radius/pkg/cli/clients" + "github.com/radius-project/radius/pkg/cli/clierrors" "github.com/radius-project/radius/pkg/cli/output" "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" ) @@ -68,3 +73,24 @@ func GetResourceTypeTableFormat() output.FormatterOptions { }, } } + +// GetResourceTypeDetails fetches the details of a resource type from the resource provider. +func GetResourceTypeDetails(ctx context.Context, resourceProviderName string, resourceTypeName string, client clients.ApplicationsManagementClient) (ResourceType, error) { + resourceProvider, err := client.GetResourceProviderSummary(ctx, "local", resourceProviderName) + if clients.Is404Error(err) { + return ResourceType{}, clierrors.Message("The resource provider %q was not found or has been deleted.", resourceProviderName) + } else if err != nil { + return ResourceType{}, err + } + + resourceTypes := ResourceTypesForProvider(&resourceProvider) + idx := slices.IndexFunc(resourceTypes, func(rt ResourceType) bool { + return rt.Name == resourceProviderName+"/"+resourceTypeName + }) + + if idx < 0 { + return ResourceType{}, clierrors.Message("Resource type %q not found in resource provider %q.", resourceTypeName, *resourceProvider.Name) + } + + return resourceTypes[idx], nil +} diff --git a/pkg/cli/cmd/resourcetype/common/resourcetype_test.go b/pkg/cli/cmd/resourcetype/common/resourcetype_test.go new file mode 100644 index 0000000000..2bbc1bbd2a --- /dev/null +++ b/pkg/cli/cmd/resourcetype/common/resourcetype_test.go @@ -0,0 +1,90 @@ +/* +Copyright 2023 The Radius Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package common + +import ( + "context" + "errors" + "testing" + + "github.com/radius-project/radius/pkg/cli/clients" + "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" + "github.com/radius-project/radius/test/radcli" + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" +) + +func Test_GetResourceTypeDetails(t *testing.T) { + t.Run("Get Resource Details Success", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + resourceProvider := v20231001preview.ResourceProviderSummary{ + Name: to.Ptr("Applications.Test"), + ResourceTypes: map[string]*v20231001preview.ResourceProviderSummaryResourceType{ + "exampleResources": { + APIVersions: map[string]map[string]any{ + "2023-10-01-preview": {}, + }, + }, + }, + } + + appManagementClient := clients.NewMockApplicationsManagementClient(ctrl) + appManagementClient.EXPECT(). + GetResourceProviderSummary(gomock.Any(), "local", "Applications.Test"). + Return(resourceProvider, nil). + Times(1) + + res, err := GetResourceTypeDetails(context.Background(), "Applications.Test", "exampleResources", appManagementClient) + require.NoError(t, err) + require.Equal(t, "Applications.Test/exampleResources", res.Name) + + }) + + t.Run("Get Resource Details Failure - Resource Provider Not found", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + appManagementClient := clients.NewMockApplicationsManagementClient(ctrl) + appManagementClient.EXPECT(). + GetResourceProviderSummary(gomock.Any(), "local", "Applications.Test"). + Return(v20231001preview.ResourceProviderSummary{}, radcli.Create404Error()). + Times(1) + + _, err := GetResourceTypeDetails(context.Background(), "Applications.Test", "exampleResources", appManagementClient) + + require.Error(t, err) + require.Equal(t, "The resource provider \"Applications.Test\" was not found or has been deleted.", err.Error()) + }) + + t.Run("Get Resource Details Failures Other Than Not Found", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + appManagementClient := clients.NewMockApplicationsManagementClient(ctrl) + appManagementClient.EXPECT(). + GetResourceProviderSummary(gomock.Any(), "local", "Applications.Test"). + Return(v20231001preview.ResourceProviderSummary{}, errors.New("some error occurred")). + Times(1) + + _, err := GetResourceTypeDetails(context.Background(), "Applications.Test", "exampleResources", appManagementClient) + + require.Error(t, err) + }) +} diff --git a/pkg/cli/cmd/resourcetype/create/create.go b/pkg/cli/cmd/resourcetype/create/create.go index d8a24c3ac0..0aa9343838 100644 --- a/pkg/cli/cmd/resourcetype/create/create.go +++ b/pkg/cli/cmd/resourcetype/create/create.go @@ -18,13 +18,13 @@ package create import ( "context" - "strings" "github.com/radius-project/radius/pkg/cli" "github.com/radius-project/radius/pkg/cli/clients" "github.com/radius-project/radius/pkg/cli/clierrors" "github.com/radius-project/radius/pkg/cli/cmd/commonflags" "github.com/radius-project/radius/pkg/cli/cmd/resourcetype/common" + "github.com/radius-project/radius/pkg/cli/connections" "github.com/radius-project/radius/pkg/cli/framework" "github.com/radius-project/radius/pkg/cli/manifest" "github.com/radius-project/radius/pkg/cli/output" @@ -36,10 +36,6 @@ import ( aztoken "github.com/radius-project/radius/pkg/azure/tokencredentials" ) -const ( - fakeServerResourceProviderNotFoundResponse = "unexpected status code 404. acceptable values are http.StatusOK" -) - // NewCommand creates an instance of the `rad resource-type create` command and runner. func NewCommand(factory framework.Factory) (*cobra.Command, framework.Runner) { runner := NewRunner(factory) @@ -47,7 +43,7 @@ func NewCommand(factory framework.Factory) (*cobra.Command, framework.Runner) { cmd := &cobra.Command{ Use: "create [input]", Short: "Create or update a resource type", - Long: `Create or update a resource type from a resource provider manifest. + Long: `Create or update a resource type from a resource type manifest. Resource types are user defined types such as 'Mycompany.Messaging/plaid'. @@ -75,11 +71,12 @@ rad resource-type create myType --from-file /path/to/input.json // Runner is the Runner implementation for the `rad resource-type create` command. type Runner struct { - UCPClientFactory *v20231001preview.ClientFactory - ConfigHolder *framework.ConfigHolder - Output output.Interface - Format string - Workspace *workspaces.Workspace + UCPClientFactory *v20231001preview.ClientFactory + ConnectionFactory connections.Factory + ConfigHolder *framework.ConfigHolder + Output output.Interface + Format string + Workspace *workspaces.Workspace ResourceProviderManifestFilePath string ResourceProvider *manifest.ResourceProvider @@ -90,8 +87,9 @@ type Runner struct { // NewRunner creates an instance of the runner for the `rad resource-type create` command. func NewRunner(factory framework.Factory) *Runner { return &Runner{ - ConfigHolder: factory.GetConfigHolder(), - Output: factory.GetOutput(), + ConnectionFactory: factory.GetConnectionFactory(), + ConfigHolder: factory.GetConfigHolder(), + Output: factory.GetOutput(), Logger: func(format string, args ...any) { output.LogInfo(format, args...) }, @@ -142,10 +140,9 @@ func (r *Runner) Run(ctx context.Context) error { } } - response, err := r.UCPClientFactory.NewResourceProvidersClient().Get(ctx, "local", r.ResourceProvider.Name, nil) + _, err := r.UCPClientFactory.NewResourceProvidersClient().Get(ctx, "local", r.ResourceProvider.Name, nil) if err != nil { - // The second clause is required for testing purpose since fake server returns a different type of error. - if clients.Is404Error(err) || strings.Contains(err.Error(), fakeServerResourceProviderNotFoundResponse) { + if clients.Is404Error(err) { r.Output.LogInfo("Resource provider %q not found.", r.ResourceProvider.Name) if registerErr := manifest.RegisterFile(ctx, r.UCPClientFactory, "local", r.ResourceProviderManifestFilePath, r.Logger); err != nil { return registerErr @@ -160,9 +157,22 @@ func (r *Runner) Run(ctx context.Context) error { } } + _, err = r.UCPClientFactory.NewResourceTypesClient().Get(ctx, "local", r.ResourceProvider.Name, r.ResourceTypeName, nil) + if err != nil { + return err + } + r.Output.LogInfo("") - err = r.Output.WriteFormatted(r.Format, response, common.GetResourceTypeTableFormat()) + client, err := r.ConnectionFactory.CreateApplicationsManagementClient(ctx, *r.Workspace) + if err != nil { + return err + } + resourceTypeDetails, err := common.GetResourceTypeDetails(ctx, r.ResourceProvider.Name, r.ResourceTypeName, client) + if err != nil { + return err + } + err = r.Output.WriteFormatted(r.Format, resourceTypeDetails, common.GetResourceTypeTableFormat()) if err != nil { return err } diff --git a/pkg/cli/cmd/resourcetype/create/create_test.go b/pkg/cli/cmd/resourcetype/create/create_test.go index 8d22ada7d8..75e175e0ca 100644 --- a/pkg/cli/cmd/resourcetype/create/create_test.go +++ b/pkg/cli/cmd/resourcetype/create/create_test.go @@ -22,12 +22,18 @@ import ( "fmt" "testing" + "github.com/radius-project/radius/pkg/cli/clients" + "github.com/radius-project/radius/pkg/cli/cmd/resourcetype/common" + "github.com/radius-project/radius/pkg/cli/connections" "github.com/radius-project/radius/pkg/cli/framework" "github.com/radius-project/radius/pkg/cli/manifest" "github.com/radius-project/radius/pkg/cli/output" "github.com/radius-project/radius/pkg/cli/workspaces" + "github.com/radius-project/radius/pkg/to" + "github.com/radius-project/radius/pkg/ucp/api/v20231001preview" "github.com/radius-project/radius/test/radcli" "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" ) func Test_CommandValidation(t *testing.T) { @@ -40,7 +46,7 @@ func Test_Validate(t *testing.T) { testcases := []radcli.ValidateInput{ { Name: "Valid", - Input: []string{"coolResources", "--from-file", "testdata/valid.yaml"}, + Input: []string{"testResources", "--from-file", "testdata/valid.yaml"}, ExpectedValid: true, ConfigHolder: framework.ConfigHolder{Config: config}, }, @@ -63,38 +69,88 @@ func Test_Validate(t *testing.T) { func Test_Run(t *testing.T) { t.Run("Success: resource type created", func(t *testing.T) { + resourceProvider := v20231001preview.ResourceProviderSummary{ + Name: to.Ptr("MyCompany.Resources"), + ResourceTypes: map[string]*v20231001preview.ResourceProviderSummaryResourceType{ + "testResources": &v20231001preview.ResourceProviderSummaryResourceType{ + APIVersions: map[string]map[string]any{ + "2023-10-01-preview": {}, + }, + Capabilities: []*string{}, + }, + }, + } + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + appManagementClient := clients.NewMockApplicationsManagementClient(ctrl) + appManagementClient.EXPECT(). + GetResourceProviderSummary(gomock.Any(), "local", "MyCompany.Resources"). + Return(resourceProvider, nil). + Times(1) + resourceProviderData, err := manifest.ReadFile("testdata/valid.yaml") require.NoError(t, err) - expectedResourceType := "testResources" - clientFactory, err := manifest.NewTestClientFactory(manifest.WithResourceProviderServerNoError) require.NoError(t, err) - var logBuffer bytes.Buffer - logger := func(format string, args ...any) { - fmt.Fprintf(&logBuffer, format+"\n", args...) - } - + outputSink := &output.MockOutput{} runner := &Runner{ + ConnectionFactory: &connections.MockFactory{ApplicationsManagementClient: appManagementClient}, UCPClientFactory: clientFactory, - Output: &output.MockOutput{}, + Output: outputSink, Workspace: &workspaces.Workspace{}, ResourceProvider: resourceProviderData, Format: "table", - Logger: logger, ResourceProviderManifestFilePath: "testdata/valid.yaml", - ResourceTypeName: expectedResourceType, + ResourceTypeName: "testResources", } err = runner.Run(context.Background()) require.NoError(t, err) - - logOutput := logBuffer.String() - require.NotContains(t, logOutput, fmt.Sprintf("Creating resource provider %s", runner.ResourceProvider.Name)) - require.Contains(t, logOutput, fmt.Sprintf("Resource type %s/%s created successfully", resourceProviderData.Name, expectedResourceType)) + expected := []interface{}{ + output.LogOutput{ + Format: "Resource provider %q found. Registering resource type %q.", + Params: []interface{}{"MyCompany.Resources", "testResources"}, + }, + output.LogOutput{ + Format: "", + Params: nil, + }, + output.FormattedOutput{ + Format: "table", + Obj: common.ResourceType{ + Name: "MyCompany.Resources/testResources", + ResourceProviderNamespace: "MyCompany.Resources", + APIVersions: []string{"2023-10-01-preview"}, + }, + Options: output.FormatterOptions{ + Columns: []output.Column{ + { + Heading: "TYPE", + JSONPath: "{ .Name }", + }, + { + Heading: "NAMESPACE", + JSONPath: "{ .ResourceProviderNamespace }", + }, + { + Heading: "APIVERSION", + JSONPath: "{ .APIVersions }", + }, + }, + }, + }, + } + require.Equal(t, expected, outputSink.Writes, "Mismatch in output sink writes") }) + t.Run("Resource provider does not exist", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + appManagementClient := clients.NewMockApplicationsManagementClient(ctrl) + resourceProviderData, err := manifest.ReadFile("testdata/valid.yaml") require.NoError(t, err) @@ -109,6 +165,7 @@ func Test_Run(t *testing.T) { } runner := &Runner{ + ConnectionFactory: &connections.MockFactory{ApplicationsManagementClient: appManagementClient}, UCPClientFactory: clientFactory, Output: &output.MockOutput{}, Workspace: &workspaces.Workspace{}, @@ -122,9 +179,12 @@ func Test_Run(t *testing.T) { _ = runner.Run(context.Background()) logOutput := logBuffer.String() require.Contains(t, logOutput, fmt.Sprintf("Creating resource provider %s", runner.ResourceProvider.Name)) - }) t.Run("Get Resource provider Internal Error", func(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + appManagementClient := clients.NewMockApplicationsManagementClient(ctrl) + resourceProviderData, err := manifest.ReadFile("testdata/valid.yaml") require.NoError(t, err) @@ -139,6 +199,7 @@ func Test_Run(t *testing.T) { } runner := &Runner{ + ConnectionFactory: &connections.MockFactory{ApplicationsManagementClient: appManagementClient}, UCPClientFactory: clientFactory, Output: &output.MockOutput{}, Workspace: &workspaces.Workspace{}, diff --git a/pkg/cli/cmd/resourcetype/create/testdata/valid.yaml b/pkg/cli/cmd/resourcetype/create/testdata/valid.yaml index 220799cc23..a6f26f7fb4 100644 --- a/pkg/cli/cmd/resourcetype/create/testdata/valid.yaml +++ b/pkg/cli/cmd/resourcetype/create/testdata/valid.yaml @@ -1,12 +1,7 @@ -name: CoolCompany.Resources +name: MyCompany.Resources types: testResources: apiVersions: - '2023-10-01-preview': + '2025-01-01-preview': schema: {} - capabilities: ["Recipes"] - coolResources: - apiVersions: - '2023-10-01-preview': - schema: {} - capabilities: ["Recipes"] \ No newline at end of file + capabilities: ["SupportsRecipes"] \ No newline at end of file diff --git a/pkg/cli/cmd/resourcetype/show/show.go b/pkg/cli/cmd/resourcetype/show/show.go index add2518e54..42a2a7a233 100644 --- a/pkg/cli/cmd/resourcetype/show/show.go +++ b/pkg/cli/cmd/resourcetype/show/show.go @@ -18,11 +18,9 @@ package show import ( "context" - "slices" "strings" "github.com/radius-project/radius/pkg/cli" - "github.com/radius-project/radius/pkg/cli/clients" "github.com/radius-project/radius/pkg/cli/clierrors" "github.com/radius-project/radius/pkg/cli/cmd/commonflags" "github.com/radius-project/radius/pkg/cli/cmd/resourcetype/common" @@ -110,27 +108,13 @@ func (r *Runner) Run(ctx context.Context) error { if err != nil { return err } - - resourceProvider, err := client.GetResourceProviderSummary(ctx, "local", r.ResourceProviderNamespace) - if clients.Is404Error(err) { - return clierrors.Message("The resource provider %q was not found or has been deleted.", r.ResourceProviderNamespace) - } else if err != nil { + resourceTypeDetails, err := common.GetResourceTypeDetails(ctx, r.ResourceProviderNamespace, r.ResourceTypeSuffix, client) + if err != nil { return err } - - resourceTypes := common.ResourceTypesForProvider(&resourceProvider) - idx := slices.IndexFunc(resourceTypes, func(rt common.ResourceType) bool { - return rt.Name == r.ResourceTypeName - }) - - if idx < 0 { - return clierrors.Message("Resource type %q not found in resource provider %q.", r.ResourceTypeSuffix, r.ResourceProviderNamespace) - } - - err = r.Output.WriteFormatted(r.Format, resourceTypes[idx], common.GetResourceTypeTableFormat()) + err = r.Output.WriteFormatted(r.Format, resourceTypeDetails, common.GetResourceTypeTableFormat()) if err != nil { return err } - return nil } diff --git a/pkg/cli/manifest/registermanifest.go b/pkg/cli/manifest/registermanifest.go index 2c2a54fb72..8f785def4b 100644 --- a/pkg/cli/manifest/registermanifest.go +++ b/pkg/cli/manifest/registermanifest.go @@ -187,6 +187,21 @@ func RegisterType(ctx context.Context, clientFactory *v20231001preview.ClientFac return err } + for apiVersionName := range resourceType.APIVersions { + logIfEnabled(logger, "Creating API Version %s/%s@%s", resourceProvider.Name, typeName, apiVersionName) + apiVersionsPoller, err := clientFactory.NewAPIVersionsClient().BeginCreateOrUpdate(ctx, planeName, resourceProvider.Name, typeName, apiVersionName, v20231001preview.APIVersionResource{ + Properties: &v20231001preview.APIVersionProperties{}, + }, nil) + if err != nil { + return err + } + + _, err = apiVersionsPoller.PollUntilDone(ctx, nil) + if err != nil { + return err + } + } + // get the existing location resource and update it with new resource type. We have to revisit this code once schema is finalized and validated. locationResourceGetResponse, err := clientFactory.NewLocationsClient().Get(ctx, planeName, resourceProvider.Name, v1.LocationGlobal, nil) if err != nil { diff --git a/pkg/cli/manifest/registermanifest_test.go b/pkg/cli/manifest/registermanifest_test.go index 810fddce9a..8026c6b898 100644 --- a/pkg/cli/manifest/registermanifest_test.go +++ b/pkg/cli/manifest/registermanifest_test.go @@ -144,6 +144,88 @@ func TestRegisterFile(t *testing.T) { } else { require.NoError(t, err) + if tt.expectedResourceProvider != "" { + rp, err := clientFactory.NewResourceProvidersClient().Get(context.Background(), tt.planeName, tt.expectedResourceProvider, nil) + require.NoError(t, err, "Failed to retrieve the expected resource provider") + require.Equal(t, to.Ptr(tt.expectedResourceProvider), rp.Name) + + logOutput := logBuffer.String() + require.Contains(t, logOutput, fmt.Sprintf("Creating resource type %s/%s", tt.expectedResourceProvider, tt.expectedResourceTypeName)) + require.Contains(t, logOutput, fmt.Sprintf("Creating API Version %s/%s@%s", tt.expectedResourceProvider, tt.expectedResourceTypeName, tt.expectedAPIVersion)) + } + } + }) + } +} + +func TestRegisterType(t *testing.T) { + tests := []struct { + name string + planeName string + resourceProviderName string + resourceTypeName string + filePath string + expectError bool + expectedErrorMessage string + expectedResourceProvider string + expectedResourceTypeName string + expectedAPIVersion string + }{ + { + name: "Success", + planeName: "local", + resourceProviderName: "MyCompany2.CompanyName2", + resourceTypeName: "testResource3", + filePath: "testdata/registerdirectory/resourceprovider-valid2.yaml", + expectError: false, + expectedErrorMessage: "", + expectedResourceProvider: "MyCompany2.CompanyName2", + expectedResourceTypeName: "testResource3", + expectedAPIVersion: "2025-01-01-preview", + }, + { + name: "ResourceTypeNotFound", + planeName: "local", + resourceProviderName: "MyCompany2.CompanyName2", + resourceTypeName: "testResource5", + filePath: "testdata/registerdirectory/resourceprovider-valid2.yaml", + expectError: true, + expectedErrorMessage: "Type testResource5 not found in manifest file testdata/registerdirectory/resourceprovider-valid2.yaml", + expectedResourceProvider: "", + expectedResourceTypeName: "", + }, + { + name: "EmptyFilePath", + planeName: "local", + resourceProviderName: "MyCompany2.CompanyName2", + resourceTypeName: "testResource3", + filePath: "", + expectError: true, + expectedErrorMessage: "invalid manifest file path", + expectedResourceProvider: "", + expectedResourceTypeName: "", + }, + } + + // Run the tests + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + clientFactory, err := NewTestClientFactory(WithResourceProviderServerNoError) + require.NoError(t, err, "Failed to create client factory") + + var logBuffer bytes.Buffer + logger := func(format string, args ...interface{}) { + fmt.Fprintf(&logBuffer, format+"\n", args...) + } + + err = RegisterType(context.Background(), clientFactory, tt.planeName, tt.filePath, tt.resourceTypeName, logger) + if tt.expectError { + require.Error(t, err) + require.Contains(t, err.Error(), tt.expectedErrorMessage) + } else { + require.NoError(t, err) + // Verify the expected resource provider exists if tt.expectedResourceProvider != "" { rp, err := clientFactory.NewResourceProvidersClient().Get(context.Background(), tt.planeName, tt.expectedResourceProvider, nil)