Files
skybridge/test/mock_repositories.go
2025-08-22 14:40:59 -04:00

603 lines
14 KiB
Go

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 valid []string
for _, scope := range scopes {
if _, exists := m.scopeIndex[scope]; exists {
valid = append(valid, scope)
}
}
return valid, 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
}