package test import ( "context" "fmt" "sync" "time" "github.com/google/uuid" "github.com/kms/api-key-service/internal/domain" "github.com/kms/api-key-service/internal/repository" ) // MockDatabaseProvider implements DatabaseProvider for testing type MockDatabaseProvider struct { mu sync.RWMutex } func NewMockDatabaseProvider() repository.DatabaseProvider { return &MockDatabaseProvider{} } func (m *MockDatabaseProvider) GetDB() interface{} { return m } func (m *MockDatabaseProvider) Ping(ctx context.Context) error { return nil } func (m *MockDatabaseProvider) Close() error { return nil } func (m *MockDatabaseProvider) BeginTx(ctx context.Context) (repository.TransactionProvider, error) { return &MockTransactionProvider{}, nil } func (m *MockDatabaseProvider) Migrate(ctx context.Context, migrationPath string) error { return nil } // MockTransactionProvider implements TransactionProvider for testing type MockTransactionProvider struct{} func (m *MockTransactionProvider) Commit() error { return nil } func (m *MockTransactionProvider) Rollback() error { return nil } func (m *MockTransactionProvider) GetTx() interface{} { return m } // MockApplicationRepository implements ApplicationRepository for testing type MockApplicationRepository struct { mu sync.RWMutex applications map[string]*domain.Application } func NewMockApplicationRepository() repository.ApplicationRepository { return &MockApplicationRepository{ applications: make(map[string]*domain.Application), } } func (m *MockApplicationRepository) Create(ctx context.Context, app *domain.Application) error { m.mu.Lock() defer m.mu.Unlock() if _, exists := m.applications[app.AppID]; exists { return fmt.Errorf("application with ID '%s' already exists", app.AppID) } now := time.Now() app.CreatedAt = now app.UpdatedAt = now // Make a copy to avoid reference issues appCopy := *app m.applications[app.AppID] = &appCopy return nil } func (m *MockApplicationRepository) GetByID(ctx context.Context, appID string) (*domain.Application, error) { m.mu.RLock() defer m.mu.RUnlock() app, exists := m.applications[appID] if !exists { return nil, fmt.Errorf("application with ID '%s' not found", appID) } // Return a copy to avoid reference issues appCopy := *app return &appCopy, nil } func (m *MockApplicationRepository) List(ctx context.Context, limit, offset int) ([]*domain.Application, error) { m.mu.RLock() defer m.mu.RUnlock() var apps []*domain.Application i := 0 for _, app := range m.applications { if i < offset { i++ continue } if len(apps) >= limit { break } // Return a copy to avoid reference issues appCopy := *app apps = append(apps, &appCopy) i++ } return apps, nil } func (m *MockApplicationRepository) Update(ctx context.Context, appID string, updates *domain.UpdateApplicationRequest) (*domain.Application, error) { m.mu.Lock() defer m.mu.Unlock() app, exists := m.applications[appID] if !exists { return nil, fmt.Errorf("application with ID '%s' not found", appID) } // Apply updates if updates.AppLink != nil { app.AppLink = *updates.AppLink } if updates.Type != nil { app.Type = *updates.Type } if updates.CallbackURL != nil { app.CallbackURL = *updates.CallbackURL } if updates.HMACKey != nil { app.HMACKey = *updates.HMACKey } if updates.TokenRenewalDuration != nil { app.TokenRenewalDuration = *updates.TokenRenewalDuration } if updates.MaxTokenDuration != nil { app.MaxTokenDuration = *updates.MaxTokenDuration } if updates.Owner != nil { app.Owner = *updates.Owner } app.UpdatedAt = time.Now() // Return a copy appCopy := *app return &appCopy, nil } func (m *MockApplicationRepository) Delete(ctx context.Context, appID string) error { m.mu.Lock() defer m.mu.Unlock() if _, exists := m.applications[appID]; !exists { return fmt.Errorf("application with ID '%s' not found", appID) } delete(m.applications, appID) return nil } func (m *MockApplicationRepository) Exists(ctx context.Context, appID string) (bool, error) { m.mu.RLock() defer m.mu.RUnlock() _, exists := m.applications[appID] return exists, nil } // MockStaticTokenRepository implements StaticTokenRepository for testing type MockStaticTokenRepository struct { mu sync.RWMutex tokens map[uuid.UUID]*domain.StaticToken } func NewMockStaticTokenRepository() repository.StaticTokenRepository { return &MockStaticTokenRepository{ tokens: make(map[uuid.UUID]*domain.StaticToken), } } func (m *MockStaticTokenRepository) Create(ctx context.Context, token *domain.StaticToken) error { m.mu.Lock() defer m.mu.Unlock() if token.ID == uuid.Nil { token.ID = uuid.New() } now := time.Now() token.CreatedAt = now token.UpdatedAt = now // Make a copy tokenCopy := *token m.tokens[token.ID] = &tokenCopy return nil } func (m *MockStaticTokenRepository) GetByID(ctx context.Context, tokenID uuid.UUID) (*domain.StaticToken, error) { m.mu.RLock() defer m.mu.RUnlock() token, exists := m.tokens[tokenID] if !exists { return nil, fmt.Errorf("token with ID '%s' not found", tokenID) } tokenCopy := *token return &tokenCopy, nil } func (m *MockStaticTokenRepository) GetByKeyHash(ctx context.Context, keyHash string) (*domain.StaticToken, error) { m.mu.RLock() defer m.mu.RUnlock() for _, token := range m.tokens { if token.KeyHash == keyHash { tokenCopy := *token return &tokenCopy, nil } } return nil, fmt.Errorf("token with key hash not found") } func (m *MockStaticTokenRepository) GetByAppID(ctx context.Context, appID string) ([]*domain.StaticToken, error) { m.mu.RLock() defer m.mu.RUnlock() var tokens []*domain.StaticToken for _, token := range m.tokens { if token.AppID == appID { tokenCopy := *token tokens = append(tokens, &tokenCopy) } } return tokens, nil } func (m *MockStaticTokenRepository) List(ctx context.Context, limit, offset int) ([]*domain.StaticToken, error) { m.mu.RLock() defer m.mu.RUnlock() var tokens []*domain.StaticToken i := 0 for _, token := range m.tokens { if i < offset { i++ continue } if len(tokens) >= limit { break } tokenCopy := *token tokens = append(tokens, &tokenCopy) i++ } return tokens, nil } func (m *MockStaticTokenRepository) Delete(ctx context.Context, tokenID uuid.UUID) error { m.mu.Lock() defer m.mu.Unlock() if _, exists := m.tokens[tokenID]; !exists { return fmt.Errorf("token with ID '%s' not found", tokenID) } delete(m.tokens, tokenID) return nil } func (m *MockStaticTokenRepository) Exists(ctx context.Context, tokenID uuid.UUID) (bool, error) { m.mu.RLock() defer m.mu.RUnlock() _, exists := m.tokens[tokenID] return exists, nil } // MockPermissionRepository implements PermissionRepository for testing type MockPermissionRepository struct { mu sync.RWMutex permissions map[uuid.UUID]*domain.AvailablePermission scopeIndex map[string]uuid.UUID } func NewMockPermissionRepository() repository.PermissionRepository { repo := &MockPermissionRepository{ permissions: make(map[uuid.UUID]*domain.AvailablePermission), scopeIndex: make(map[string]uuid.UUID), } // Add some default permissions for testing ctx := context.Background() defaultPerms := []*domain.AvailablePermission{ { ID: uuid.New(), Scope: "repo.read", Name: "Repository Read", Description: "Read repository data", Category: "repository", IsSystem: false, CreatedAt: time.Now(), CreatedBy: "system", UpdatedAt: time.Now(), UpdatedBy: "system", }, { ID: uuid.New(), Scope: "repo.write", Name: "Repository Write", Description: "Write to repositories", Category: "repository", IsSystem: false, CreatedAt: time.Now(), CreatedBy: "system", UpdatedAt: time.Now(), UpdatedBy: "system", }, } for _, perm := range defaultPerms { repo.CreateAvailablePermission(ctx, perm) } return repo } func (m *MockPermissionRepository) CreateAvailablePermission(ctx context.Context, permission *domain.AvailablePermission) error { m.mu.Lock() defer m.mu.Unlock() if permission.ID == uuid.Nil { permission.ID = uuid.New() } if _, exists := m.scopeIndex[permission.Scope]; exists { return fmt.Errorf("permission with scope '%s' already exists", permission.Scope) } now := time.Now() permission.CreatedAt = now permission.UpdatedAt = now permCopy := *permission m.permissions[permission.ID] = &permCopy m.scopeIndex[permission.Scope] = permission.ID return nil } func (m *MockPermissionRepository) GetAvailablePermission(ctx context.Context, permissionID uuid.UUID) (*domain.AvailablePermission, error) { m.mu.RLock() defer m.mu.RUnlock() perm, exists := m.permissions[permissionID] if !exists { return nil, fmt.Errorf("permission with ID '%s' not found", permissionID) } permCopy := *perm return &permCopy, nil } func (m *MockPermissionRepository) GetAvailablePermissionByScope(ctx context.Context, scope string) (*domain.AvailablePermission, error) { m.mu.RLock() defer m.mu.RUnlock() permID, exists := m.scopeIndex[scope] if !exists { return nil, fmt.Errorf("permission with scope '%s' not found", scope) } perm := m.permissions[permID] permCopy := *perm return &permCopy, nil } func (m *MockPermissionRepository) ListAvailablePermissions(ctx context.Context, category string, includeSystem bool, limit, offset int) ([]*domain.AvailablePermission, error) { m.mu.RLock() defer m.mu.RUnlock() var perms []*domain.AvailablePermission i := 0 for _, perm := range m.permissions { if category != "" && perm.Category != category { continue } if !includeSystem && perm.IsSystem { continue } if i < offset { i++ continue } if len(perms) >= limit { break } permCopy := *perm perms = append(perms, &permCopy) i++ } return perms, nil } func (m *MockPermissionRepository) UpdateAvailablePermission(ctx context.Context, permissionID uuid.UUID, permission *domain.AvailablePermission) error { m.mu.Lock() defer m.mu.Unlock() if _, exists := m.permissions[permissionID]; !exists { return fmt.Errorf("permission with ID '%s' not found", permissionID) } permission.ID = permissionID permission.UpdatedAt = time.Now() permCopy := *permission m.permissions[permissionID] = &permCopy return nil } func (m *MockPermissionRepository) DeleteAvailablePermission(ctx context.Context, permissionID uuid.UUID) error { m.mu.Lock() defer m.mu.Unlock() perm, exists := m.permissions[permissionID] if !exists { return fmt.Errorf("permission with ID '%s' not found", permissionID) } delete(m.permissions, permissionID) delete(m.scopeIndex, perm.Scope) return nil } func (m *MockPermissionRepository) ValidatePermissionScopes(ctx context.Context, scopes []string) ([]string, error) { m.mu.RLock() defer m.mu.RUnlock() var invalid []string for _, scope := range scopes { if _, exists := m.scopeIndex[scope]; !exists { invalid = append(invalid, scope) } } return invalid, nil } func (m *MockPermissionRepository) GetPermissionHierarchy(ctx context.Context, scopes []string) ([]*domain.AvailablePermission, error) { m.mu.RLock() defer m.mu.RUnlock() var perms []*domain.AvailablePermission for _, scope := range scopes { if permID, exists := m.scopeIndex[scope]; exists { perm := m.permissions[permID] permCopy := *perm perms = append(perms, &permCopy) } } return perms, nil } // MockGrantedPermissionRepository implements GrantedPermissionRepository for testing type MockGrantedPermissionRepository struct { mu sync.RWMutex grants map[uuid.UUID]*domain.GrantedPermission } func NewMockGrantedPermissionRepository() repository.GrantedPermissionRepository { return &MockGrantedPermissionRepository{ grants: make(map[uuid.UUID]*domain.GrantedPermission), } } func (m *MockGrantedPermissionRepository) GrantPermissions(ctx context.Context, grants []*domain.GrantedPermission) error { m.mu.Lock() defer m.mu.Unlock() for _, grant := range grants { if grant.ID == uuid.Nil { grant.ID = uuid.New() } grant.CreatedAt = time.Now() grantCopy := *grant m.grants[grant.ID] = &grantCopy } return nil } func (m *MockGrantedPermissionRepository) GetGrantedPermissions(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID) ([]*domain.GrantedPermission, error) { m.mu.RLock() defer m.mu.RUnlock() var grants []*domain.GrantedPermission for _, grant := range m.grants { if grant.TokenType == tokenType && grant.TokenID == tokenID && !grant.Revoked { grantCopy := *grant grants = append(grants, &grantCopy) } } return grants, nil } func (m *MockGrantedPermissionRepository) GetGrantedPermissionScopes(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID) ([]string, error) { m.mu.RLock() defer m.mu.RUnlock() var scopes []string for _, grant := range m.grants { if grant.TokenType == tokenType && grant.TokenID == tokenID && !grant.Revoked { scopes = append(scopes, grant.Scope) } } return scopes, nil } func (m *MockGrantedPermissionRepository) RevokePermission(ctx context.Context, grantID uuid.UUID, revokedBy string) error { m.mu.Lock() defer m.mu.Unlock() grant, exists := m.grants[grantID] if !exists { return fmt.Errorf("granted permission with ID '%s' not found", grantID) } grant.Revoked = true return nil } func (m *MockGrantedPermissionRepository) RevokeAllPermissions(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID, revokedBy string) error { m.mu.Lock() defer m.mu.Unlock() for _, grant := range m.grants { if grant.TokenType == tokenType && grant.TokenID == tokenID { grant.Revoked = true } } return nil } func (m *MockGrantedPermissionRepository) HasPermission(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID, scope string) (bool, error) { m.mu.RLock() defer m.mu.RUnlock() for _, grant := range m.grants { if grant.TokenType == tokenType && grant.TokenID == tokenID && grant.Scope == scope && !grant.Revoked { return true, nil } } return false, nil } func (m *MockGrantedPermissionRepository) HasAnyPermission(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID, scopes []string) (map[string]bool, error) { m.mu.RLock() defer m.mu.RUnlock() result := make(map[string]bool) for _, scope := range scopes { result[scope] = false for _, grant := range m.grants { if grant.TokenType == tokenType && grant.TokenID == tokenID && grant.Scope == scope && !grant.Revoked { result[scope] = true break } } } return result, nil }