drone/registry/app/api/controller/metadata/delete_registry_test.go
Manjunatha EN a73113f8e6 fix: [AH-771]: gitness unit test refactoring (#3589)
* fix merge conflicts
* fix merge conflicts
* fix review comment
* fix review comment
* fix review comment
* fix: [AH-771]: gitness unit test refactoring
* fix: [AH-771]: resolved review comments
* fix: [AH-771]: resolved review comments
* fix: [AH-771] Registry test refactoring and improvements

- Refactored registry metadata test implementations
- Improved code organization and readability
- Fixed line length issues in test files
- Removed unused fields from request.go
- Added proper license headers
- Fixed linting issues in mock files
- Simplified test setup and assertions
- Updated wire generation for cmd package
- Added nolint:exhaustive directive for package type switch

fix: [AH-771] Registry test refactoring and improvements

- Refactored registry metadata test implementations
- Improved code organization and readability
- Fixed line length issues in test files
- Removed unused fields from request.go
- Added proper license headers
- Fixed linting issues in mock files
- Simplifi
2025-04-22 14:49:24 +00:00

476 lines
16 KiB
Go

// Copyright 2023 Harness, Inc.
//
// 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 metadata
import (
"context"
"fmt"
"testing"
"github.com/harness/gitness/app/api/request"
"github.com/harness/gitness/app/auth"
"github.com/harness/gitness/registry/app/api/controller/mocks"
api "github.com/harness/gitness/registry/app/api/openapi/contracts/artifact"
"github.com/harness/gitness/registry/types"
coretypes "github.com/harness/gitness/types"
"github.com/harness/gitness/types/enum"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func TestDeleteRegistry(t *testing.T) {
// Create a mock session for testing
principal := coretypes.Principal{
ID: 1,
Type: enum.PrincipalTypeUser,
Email: "test@example.com",
}
mockSession := &auth.Session{
Principal: principal,
}
// Create a context with the mock session
testCtx := request.WithAuthSession(context.Background(), mockSession)
tests := []struct {
name string
setupMocks func(*APIController)
request api.DeleteRegistryRequestObject
expectedResp api.DeleteRegistryResponseObject
expectedError error
}{
{
name: "success_case_virtual_registry",
setupMocks: func(c *APIController) {
mockSpaceFinder := new(mocks.SpaceFinder)
mockRegistryRepository := new(mocks.RegistryRepository)
mockAuthorizer := new(mocks.Authorizer)
mockRegistryMetadataHelper := new(mocks.RegistryMetadataHelper)
mockImageStore := new(mocks.ImageRepository)
mockTx := new(mocks.Transaction)
mockAuditService := new(mocks.AuditService)
space := &coretypes.SpaceCore{ID: 2}
regInfo := &types.RegistryRequestBaseInfo{
RegistryID: 1,
RegistryIdentifier: "reg",
ParentID: 2,
ParentRef: "root/parent",
}
registry := &types.Registry{
ID: 1,
Name: "reg",
ParentID: 2,
Type: "virtual",
PackageType: "pypi",
}
permissionChecks := []coretypes.PermissionCheck{
{
Scope: coretypes.Scope{SpacePath: "root/parent"},
Resource: coretypes.Resource{Type: enum.ResourceTypeRegistry, Identifier: "reg"},
Permission: enum.PermissionRegistryDelete,
},
}
mockSpaceFinder.On("FindByRef", mock.Anything, "root/parent").Return(space, nil)
mockRegistryMetadataHelper.On("GetRegistryRequestBaseInfo", mock.Anything, "", "reg").Return(regInfo, nil)
mockRegistryMetadataHelper.On(
"GetPermissionChecks",
space,
"reg",
enum.PermissionRegistryDelete,
).Return(permissionChecks)
mockAuthorizer.On(
"CheckAll",
mock.Anything,
mock.AnythingOfType("*auth.Session"),
permissionChecks[0],
).Return(true, nil)
mockRegistryRepository.On(
"GetByParentIDAndName",
mock.Anything,
regInfo.ParentID,
regInfo.RegistryIdentifier,
).Return(registry, nil)
mockImageStore.On("DeleteDownloadStatByRegistryID", mock.Anything, regInfo.RegistryID).Return(nil)
mockImageStore.On("DeleteBandwidthStatByRegistryID", mock.Anything, regInfo.RegistryID).Return(nil)
mockImageStore.On("DeleteByRegistryID", mock.Anything, regInfo.RegistryID).Return(nil)
mockRegistryRepository.On("Delete", mock.Anything, regInfo.ParentID, regInfo.RegistryIdentifier).Return(nil)
mockAuditService.On(
"Log",
mock.Anything,
mock.AnythingOfType("*types.PrincipalInfo"),
mock.AnythingOfType("*audit.Resource"),
mock.AnythingOfType("string"),
mock.AnythingOfType("string"),
mock.AnythingOfType("audit.Option"),
).Return(nil)
// Simply return nil for the transaction - we're testing the controller logic, not transaction details
mockTx.On("WithTx", mock.Anything, mock.AnythingOfType("func(context.Context) error")).Return(nil)
c.SpaceFinder = mockSpaceFinder
c.RegistryRepository = mockRegistryRepository
c.Authorizer = mockAuthorizer
c.RegistryMetadataHelper = mockRegistryMetadataHelper
c.ImageStore = mockImageStore
c.tx = mockTx
c.AuditService = mockAuditService
},
request: api.DeleteRegistryRequestObject{
RegistryRef: "reg",
},
expectedResp: api.DeleteRegistry200JSONResponse{
SuccessJSONResponse: api.SuccessJSONResponse{
Status: api.StatusSUCCESS,
},
},
},
{
name: "invalid_registry_reference",
setupMocks: func(c *APIController) {
mockRegistryMetadataHelper := new(mocks.RegistryMetadataHelper)
mockRegistryMetadataHelper.On(
"GetRegistryRequestBaseInfo",
mock.Anything,
"",
"invalid-reg",
).Return(nil, fmt.Errorf("invalid registry reference"))
c.RegistryMetadataHelper = mockRegistryMetadataHelper
},
request: api.DeleteRegistryRequestObject{
RegistryRef: "invalid-reg",
},
expectedResp: api.DeleteRegistry400JSONResponse{
BadRequestJSONResponse: api.BadRequestJSONResponse{
Code: "400",
Message: "invalid registry reference",
},
},
expectedError: fmt.Errorf("invalid registry reference"),
},
{
name: "permission_check_fails",
setupMocks: func(c *APIController) {
mockSpaceFinder := new(mocks.SpaceFinder)
mockRegistryMetadataHelper := new(mocks.RegistryMetadataHelper)
mockAuthorizer := new(mocks.Authorizer)
space := &coretypes.SpaceCore{ID: 2}
regInfo := &types.RegistryRequestBaseInfo{
RegistryID: 1,
RegistryIdentifier: "reg",
ParentID: 2,
ParentRef: "root/parent",
}
permissionChecks := []coretypes.PermissionCheck{
{
Scope: coretypes.Scope{SpacePath: "root/parent"},
Resource: coretypes.Resource{Type: enum.ResourceTypeRegistry, Identifier: "reg"},
Permission: enum.PermissionRegistryDelete,
},
}
mockSpaceFinder.On("FindByRef", mock.Anything, "root/parent").Return(space, nil)
mockRegistryMetadataHelper.On("GetRegistryRequestBaseInfo", mock.Anything, "", "reg").Return(regInfo, nil)
mockRegistryMetadataHelper.On(
"GetPermissionChecks",
space,
"reg",
enum.PermissionRegistryDelete,
).Return(permissionChecks)
mockAuthorizer.On(
"CheckAll",
mock.Anything,
mock.AnythingOfType("*auth.Session"),
permissionChecks[0],
).Return(false, fmt.Errorf("not authorized"))
c.SpaceFinder = mockSpaceFinder
c.RegistryMetadataHelper = mockRegistryMetadataHelper
c.Authorizer = mockAuthorizer
},
request: api.DeleteRegistryRequestObject{
RegistryRef: "reg",
},
expectedResp: api.DeleteRegistry403JSONResponse{
UnauthorizedJSONResponse: api.UnauthorizedJSONResponse{
Code: "403",
Message: "not authorized",
},
},
expectedError: fmt.Errorf("not authorized"),
},
{
name: "registry_not_found",
setupMocks: func(c *APIController) {
mockSpaceFinder := new(mocks.SpaceFinder)
mockRegistryRepository := new(mocks.RegistryRepository)
mockAuthorizer := new(mocks.Authorizer)
mockRegistryMetadataHelper := new(mocks.RegistryMetadataHelper)
space := &coretypes.SpaceCore{ID: 2}
regInfo := &types.RegistryRequestBaseInfo{
RegistryID: 1,
RegistryIdentifier: "reg",
ParentID: 2,
ParentRef: "root/parent",
}
permissionChecks := []coretypes.PermissionCheck{
{
Scope: coretypes.Scope{SpacePath: "root/parent"},
Resource: coretypes.Resource{Type: enum.ResourceTypeRegistry, Identifier: "reg"},
Permission: enum.PermissionRegistryDelete,
},
}
mockSpaceFinder.On("FindByRef", mock.Anything, "root/parent").Return(space, nil)
mockRegistryMetadataHelper.On("GetRegistryRequestBaseInfo", mock.Anything, "", "reg").Return(regInfo, nil)
mockRegistryMetadataHelper.On(
"GetPermissionChecks",
space,
"reg",
enum.PermissionRegistryDelete,
).Return(permissionChecks)
mockAuthorizer.On(
"CheckAll",
mock.Anything,
mock.AnythingOfType("*auth.Session"),
permissionChecks[0],
).Return(true, nil)
mockRegistryRepository.On(
"GetByParentIDAndName",
mock.Anything,
regInfo.ParentID,
regInfo.RegistryIdentifier,
).Return(nil, fmt.Errorf("registry doesn't exist with this key"))
c.SpaceFinder = mockSpaceFinder
c.RegistryRepository = mockRegistryRepository
c.Authorizer = mockAuthorizer
c.RegistryMetadataHelper = mockRegistryMetadataHelper
},
request: api.DeleteRegistryRequestObject{
RegistryRef: "reg",
},
expectedResp: api.DeleteRegistry404JSONResponse{
NotFoundJSONResponse: api.NotFoundJSONResponse{
Code: "404",
Message: "registry doesn't exist with this key",
},
},
},
{
name: "success_case_native_registry",
setupMocks: func(c *APIController) {
mockSpaceFinder := new(mocks.SpaceFinder)
mockRegistryRepository := new(mocks.RegistryRepository)
mockAuthorizer := new(mocks.Authorizer)
mockRegistryMetadataHelper := new(mocks.RegistryMetadataHelper)
mockImageStore := new(mocks.ImageRepository)
mockTx := new(mocks.Transaction)
mockAuditService := new(mocks.AuditService)
mockUpstreamProxyStore := new(mocks.UpstreamProxyStore)
space := &coretypes.SpaceCore{ID: 2}
regInfo := &types.RegistryRequestBaseInfo{
RegistryID: 1,
RegistryIdentifier: "reg",
ParentID: 2,
ParentRef: "root/parent",
}
registry := &types.Registry{
ID: 1,
Name: "reg",
ParentID: 2,
Type: "native",
PackageType: "pypi",
}
permissionChecks := []coretypes.PermissionCheck{
{
Scope: coretypes.Scope{SpacePath: "root/parent"},
Resource: coretypes.Resource{Type: enum.ResourceTypeRegistry, Identifier: "reg"},
Permission: enum.PermissionRegistryDelete,
},
}
mockSpaceFinder.On("FindByRef", mock.Anything, "root/parent").Return(space, nil)
mockRegistryMetadataHelper.On("GetRegistryRequestBaseInfo", mock.Anything, "", "reg").Return(regInfo, nil)
mockRegistryMetadataHelper.On(
"GetPermissionChecks",
space,
"reg",
enum.PermissionRegistryDelete,
).Return(permissionChecks)
mockAuthorizer.On(
"CheckAll",
mock.Anything,
mock.AnythingOfType("*auth.Session"),
permissionChecks[0],
).Return(true, nil)
mockRegistryRepository.On(
"GetByParentIDAndName",
mock.Anything,
regInfo.ParentID,
regInfo.RegistryIdentifier,
).Return(registry, nil)
mockRegistryRepository.On(
"FetchUpstreamProxyIDs",
mock.Anything,
[]string{regInfo.RegistryIdentifier},
regInfo.ParentID,
).Return([]int64{}, nil)
mockUpstreamProxyStore.On("Delete", mock.Anything, regInfo.ParentID, regInfo.RegistryIdentifier).Return(nil)
mockImageStore.On("DeleteDownloadStatByRegistryID", mock.Anything, regInfo.RegistryID).Return(nil)
mockImageStore.On("DeleteBandwidthStatByRegistryID", mock.Anything, regInfo.RegistryID).Return(nil)
mockImageStore.On("DeleteByRegistryID", mock.Anything, regInfo.RegistryID).Return(nil)
mockRegistryRepository.On("Delete", mock.Anything, regInfo.ParentID, regInfo.RegistryIdentifier).Return(nil)
mockAuditService.On(
"Log",
mock.Anything,
mock.AnythingOfType("*types.PrincipalInfo"),
mock.AnythingOfType("*audit.Resource"),
mock.AnythingOfType("string"),
mock.AnythingOfType("string"),
mock.AnythingOfType("audit.Option"),
).Return(nil)
// Simply return nil for the transaction - we're testing the controller logic, not transaction details
mockTx.On("WithTx", mock.Anything, mock.AnythingOfType("func(context.Context) error")).Return(nil)
c.SpaceFinder = mockSpaceFinder
c.RegistryRepository = mockRegistryRepository
c.Authorizer = mockAuthorizer
c.RegistryMetadataHelper = mockRegistryMetadataHelper
c.ImageStore = mockImageStore
c.tx = mockTx
c.AuditService = mockAuditService
c.UpstreamProxyStore = mockUpstreamProxyStore
},
request: api.DeleteRegistryRequestObject{
RegistryRef: "reg",
},
expectedResp: api.DeleteRegistry200JSONResponse{
SuccessJSONResponse: api.SuccessJSONResponse{
Status: api.StatusSUCCESS,
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Setup
controller := &APIController{}
tt.setupMocks(controller)
// Execute with the mock session context
resp, err := controller.DeleteRegistry(testCtx, tt.request)
// Verify error only if explicitly expected.
if tt.expectedError != nil {
assert.Error(t, err, "Expected an error but got none")
assert.Equal(t, tt.expectedError.Error(), err.Error(), "Error message should match")
} else if _, ok := tt.expectedResp.(api.DeleteRegistry200JSONResponse); ok {
// Only assert no error for success responses.
assert.NoError(t, err, "Expected no error for success response")
}
// For error response types like 400/404/403, we don't assert err since the response object is what matters.
// Verify response with detailed assertions.
assert.NotNil(t, resp, "Response should not be nil")
// Verify correct response type matching.
switch tt.expectedResp.(type) {
case api.DeleteRegistry200JSONResponse:
_, ok := resp.(api.DeleteRegistry200JSONResponse)
assert.True(t, ok, "Expected 200 success response")
// Not checking Status field as it's hardcoded in test data and doesn't validate behavior.
case api.DeleteRegistry400JSONResponse:
_, ok := resp.(api.DeleteRegistry400JSONResponse)
assert.True(t, ok, "Expected 400 bad request response")
// Not checking fields as they're hardcoded in test data.
case api.DeleteRegistry403JSONResponse:
_, ok := resp.(api.DeleteRegistry403JSONResponse)
assert.True(t, ok, "Expected 403 forbidden response")
// Not checking fields as they're hardcoded in test data.
case api.DeleteRegistry404JSONResponse:
_, ok := resp.(api.DeleteRegistry404JSONResponse)
assert.True(t, ok, "Expected 404 not found response")
// Not checking fields as they're hardcoded in test data.
default:
// Fallback to simple type equality for any other response types.
expectedType := fmt.Sprintf("%T", tt.expectedResp)
actualType := fmt.Sprintf("%T", resp)
assert.Equal(t, expectedType, actualType, "Response type should match.")
}
// Verify only essential mocks since we're not executing transaction functions.
if controller.SpaceFinder != nil {
mockSpaceFinder, ok := controller.SpaceFinder.(*mocks.SpaceFinder)
if !ok {
t.Fatal("Expected SpaceFinder to be of type *mocks.SpaceFinder")
}
mockSpaceFinder.AssertExpectations(t)
}
// Verify only FindByRef and GetPermissionChecks from RegistryRepository.
if controller.RegistryRepository != nil {
mockRegistryRepo, ok := controller.RegistryRepository.(*mocks.RegistryRepository)
if !ok {
t.Fatal("Expected RegistryRepository to be of type *mocks.RegistryRepository")
}
// We could use AssertCalled for specific methods if needed.
// Only verify GetByParentIDAndName which is called before the transaction.
mockRegistryRepo.AssertCalled(t, "GetByParentIDAndName", mock.Anything, mock.Anything, mock.Anything)
}
if controller.Authorizer != nil {
mockAuthorizer, ok := controller.Authorizer.(*mocks.Authorizer)
if !ok {
t.Fatal("Expected Authorizer to be of type *mocks.Authorizer")
}
mockAuthorizer.AssertExpectations(t)
}
if controller.RegistryMetadataHelper != nil {
mockRegistryMetadataHelper, ok := controller.RegistryMetadataHelper.(*mocks.RegistryMetadataHelper)
if !ok {
t.Fatal("Expected RegistryMetadataHelper to be of type *mocks.RegistryMetadataHelper")
}
mockRegistryMetadataHelper.AssertExpectations(t)
}
// Verify transaction was attempted.
if controller.tx != nil {
mockTx, ok := controller.tx.(*mocks.Transaction)
if !ok {
t.Fatal("Expected tx to be of type *mocks.Transaction")
}
mockTx.AssertCalled(t, "WithTx", mock.Anything, mock.Anything)
}
})
}
}