POST /users/{id}/promote and /demote set a user's role (admin/user), admin-only
(403 otherwise). Mirrors the verify endpoints: SetRole use case + repo method,
validated role. Unit, admin-flow, and integration tests included.
91 lines
2.0 KiB
Go
91 lines
2.0 KiB
Go
// Package memstore provides in-memory implementations of the usecase ports for
|
|
// fast, deterministic tests that need no database.
|
|
package memstore
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/emil28092005/SciMesh/users/internal/domain"
|
|
"github.com/emil28092005/SciMesh/users/internal/usecase"
|
|
)
|
|
|
|
// UserRepo is an in-memory usecase.UserRepository. It stores copies, so callers
|
|
// mutating a returned user cannot corrupt the store.
|
|
type UserRepo struct {
|
|
mu sync.Mutex
|
|
byID map[uuid.UUID]domain.User
|
|
byEmail map[string]uuid.UUID
|
|
}
|
|
|
|
func NewUserRepo() *UserRepo {
|
|
return &UserRepo{
|
|
byID: make(map[uuid.UUID]domain.User),
|
|
byEmail: make(map[string]uuid.UUID),
|
|
}
|
|
}
|
|
|
|
func (r *UserRepo) Insert(_ context.Context, u *domain.User) error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
if _, ok := r.byEmail[u.Email]; ok {
|
|
return usecase.ErrEmailExists
|
|
}
|
|
r.byID[u.ID] = *u
|
|
r.byEmail[u.Email] = u.ID
|
|
return nil
|
|
}
|
|
|
|
func (r *UserRepo) GetByEmail(_ context.Context, email string) (*domain.User, error) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
id, ok := r.byEmail[email]
|
|
if !ok {
|
|
return nil, usecase.ErrUserNotFound
|
|
}
|
|
u := r.byID[id]
|
|
return &u, nil
|
|
}
|
|
|
|
func (r *UserRepo) GetByID(_ context.Context, id uuid.UUID) (*domain.User, error) {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
u, ok := r.byID[id]
|
|
if !ok {
|
|
return nil, usecase.ErrUserNotFound
|
|
}
|
|
return &u, nil
|
|
}
|
|
|
|
func (r *UserRepo) SetVerified(_ context.Context, id uuid.UUID, verified bool) error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
u, ok := r.byID[id]
|
|
if !ok {
|
|
return usecase.ErrUserNotFound
|
|
}
|
|
u.Verified = verified
|
|
r.byID[id] = u
|
|
return nil
|
|
}
|
|
|
|
func (r *UserRepo) SetRole(_ context.Context, id uuid.UUID, role domain.Role) error {
|
|
r.mu.Lock()
|
|
defer r.mu.Unlock()
|
|
u, ok := r.byID[id]
|
|
if !ok {
|
|
return usecase.ErrUserNotFound
|
|
}
|
|
u.Role = role
|
|
r.byID[id] = u
|
|
return nil
|
|
}
|
|
|
|
// Clock is a fixed usecase.Clock for deterministic tests.
|
|
type Clock struct{ T time.Time }
|
|
|
|
func (c Clock) Now() time.Time { return c.T }
|