a18f96912d
interfaces - refactor: split UserRepository into AuthUserRepository and AdminUserRepository capabilities - refactor: split SessionRepository into AuthSessionRepository and repository-owned CLI/admin methods - refactor: replace generic Repository Update/Delete with operation-specific params and ownership predicates - refactor: replace CredentialRepository generic CRUD with passkey-specific methods - refactor: replace FileRepository generic Create/Update/Delete with UploadedFileParams, DirectoryParams, and owned soft-delete - refactor: remove repository fields from WebApp struct; repositories are now composition-time wiring only - feat: add domain error kinds ErrParentNotFound, ErrParentNotDir, ErrDirectoryNotEmpty, ErrInvalidMove - feat: add CredentialTypeAppPasskey constant - feat: add testutil.SetUserAdmin for test fixture setup that bypasses production service ports - test: add architecture test banning GORM Save in repository package - test: add capability interface contract tests ensuring each service receives the minimal interface - test: add blockingStorage helper for concurrent promotion tests - test: add preserved DSN parameter test for sqliteImmediateDSN - docs: update architecture decisions with repository write rules and capability separation - docs: update roadmap to clarify atomic single-use refresh sessions - docs: add -race test target to development docs
70 lines
2.0 KiB
Go
70 lines
2.0 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
|
|
"github.com/dhao2001/mygo/internal/model"
|
|
"github.com/dhao2001/mygo/internal/repository"
|
|
)
|
|
|
|
func setupAdminService(t *testing.T) (*AdminService, repository.UserRepository) {
|
|
t.Helper()
|
|
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
|
|
if err != nil {
|
|
t.Fatalf("open db: %v", err)
|
|
}
|
|
if err := db.AutoMigrate(&model.User{}); err != nil {
|
|
t.Fatalf("migrate: %v", err)
|
|
}
|
|
|
|
userRepo := repository.NewUserRepository(db)
|
|
return NewAdminService(userRepo), userRepo
|
|
}
|
|
|
|
func TestAdminServiceGetUserMissingReturnsNotFound(t *testing.T) {
|
|
svc, _ := setupAdminService(t)
|
|
|
|
_, err := svc.GetUser(context.Background(), "missing")
|
|
var ae *model.AppError
|
|
if !errors.As(err, &ae) {
|
|
t.Fatalf("expected AppError, got %T: %v", err, err)
|
|
}
|
|
if ae.Kind != model.KindNotFound {
|
|
t.Fatalf("kind = %q, want %q", ae.Kind, model.KindNotFound)
|
|
}
|
|
}
|
|
|
|
func TestAdminServiceListIncludesDeletedUsers(t *testing.T) {
|
|
svc, repo := setupAdminService(t)
|
|
ctx := context.Background()
|
|
|
|
active := &model.User{ID: "active-1", Username: "active", Email: "active@example.com", Status: model.StatusActive}
|
|
deleted := &model.User{ID: "deleted-1", Username: "deleted", Email: "deleted@example.com", Status: model.StatusActive}
|
|
if _, err := repo.CreateRegisteredUser(ctx, repository.RegisteredUserParams{ID: active.ID, Username: active.Username, Email: active.Email, PasswordHash: "hash"}); err != nil {
|
|
t.Fatalf("create active: %v", err)
|
|
}
|
|
if _, err := repo.CreateRegisteredUser(ctx, repository.RegisteredUserParams{ID: deleted.ID, Username: deleted.Username, Email: deleted.Email, PasswordHash: "hash"}); err != nil {
|
|
t.Fatalf("create deleted: %v", err)
|
|
}
|
|
if err := repo.MarkAdminDeleted(ctx, deleted.ID); err != nil {
|
|
t.Fatalf("delete user: %v", err)
|
|
}
|
|
|
|
users, total, err := svc.ListUsers(ctx, 0, 10)
|
|
if err != nil {
|
|
t.Fatalf("ListUsers = %v", err)
|
|
}
|
|
if total != 2 {
|
|
t.Fatalf("total = %d, want 2", total)
|
|
}
|
|
if len(users) != 2 {
|
|
t.Fatalf("len(users) = %d, want 2", len(users))
|
|
}
|
|
}
|