refactor(api): enforce protocol-neutral service boundaries
- refactor: move HTTP status and DTO concerns out of model and service layers into API and handler code. - feat: add admin service boundary and route auth through services instead of direct repository access. - test: add architecture and error tests covering package boundaries, redaction, and service behavior. - docs: record the protocol-neutral boundary decision and update architecture and roadmap notes.
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
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.Create(ctx, active); err != nil {
|
||||
t.Fatalf("create active: %v", err)
|
||||
}
|
||||
if err := repo.Create(ctx, deleted); err != nil {
|
||||
t.Fatalf("create deleted: %v", err)
|
||||
}
|
||||
if err := repo.Delete(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))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user