mirror of
https://github.com/harness/drone.git
synced 2025-05-03 15:12:15 +08:00

* 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
476 lines
16 KiB
Go
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)
|
|
}
|
|
})
|
|
}
|
|
}
|