Files
user-system/internal/service/settings_test.go

309 lines
9.4 KiB
Go
Raw Normal View History

package service_test
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/gin-gonic/gin"
"github.com/user-management-system/internal/api/handler"
"github.com/user-management-system/internal/api/middleware"
"github.com/user-management-system/internal/api/router"
"github.com/user-management-system/internal/auth"
"github.com/user-management-system/internal/cache"
"github.com/user-management-system/internal/config"
"github.com/user-management-system/internal/repository"
"github.com/user-management-system/internal/service"
"github.com/user-management-system/internal/domain"
gormsqlite "gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
_ "modernc.org/sqlite"
)
// doRequest makes an HTTP request with optional body
func doRequest(method, url string, token string, body interface{}) (*http.Response, string) {
var bodyReader io.Reader
if body != nil {
jsonBytes, _ := json.Marshal(body)
bodyReader = bytes.NewReader(jsonBytes)
}
req, _ := http.NewRequest(method, url, bodyReader)
if token != "" {
req.Header.Set("Authorization", "Bearer "+token)
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
bodyBytes, _ := io.ReadAll(resp.Body)
resp.Body.Close()
return resp, string(bodyBytes)
}
func doPost(url, token string, body interface{}) (*http.Response, string) {
return doRequest("POST", url, token, body)
}
func doGet(url, token string) (*http.Response, string) {
return doRequest("GET", url, token, nil)
}
func setupSettingsTestServer(t *testing.T) (*httptest.Server, *service.SettingsService, string, func()) {
gin.SetMode(gin.TestMode)
// 使用内存 SQLite
db, err := gorm.Open(gormsqlite.New(gormsqlite.Config{
DriverName: "sqlite",
DSN: "file::memory:?mode=memory&cache=shared",
}), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if err != nil {
t.Skipf("skipping test (SQLite unavailable): %v", err)
return nil, nil, "", func() {}
}
// 自动迁移
if err := db.AutoMigrate(
&domain.User{},
&domain.Role{},
&domain.Permission{},
&domain.UserRole{},
&domain.RolePermission{},
&domain.Device{},
&domain.LoginLog{},
&domain.OperationLog{},
&domain.SocialAccount{},
&domain.Webhook{},
&domain.WebhookDelivery{},
); err != nil {
t.Fatalf("db migration failed: %v", err)
}
// 创建 JWT Manager
jwtManager, err := auth.NewJWTWithOptions(auth.JWTOptions{
HS256Secret: "test-settings-secret-key",
AccessTokenExpire: 15 * time.Minute,
RefreshTokenExpire: 7 * 24 * time.Hour,
})
if err != nil {
t.Fatalf("create jwt manager failed: %v", err)
}
// 创建缓存
l1Cache := cache.NewL1Cache()
l2Cache := cache.NewRedisCache(false)
cacheManager := cache.NewCacheManager(l1Cache, l2Cache)
// 创建 repositories
userRepo := repository.NewUserRepository(db)
roleRepo := repository.NewRoleRepository(db)
permissionRepo := repository.NewPermissionRepository(db)
userRoleRepo := repository.NewUserRoleRepository(db)
rolePermissionRepo := repository.NewRolePermissionRepository(db)
deviceRepo := repository.NewDeviceRepository(db)
loginLogRepo := repository.NewLoginLogRepository(db)
opLogRepo := repository.NewOperationLogRepository(db)
passwordHistoryRepo := repository.NewPasswordHistoryRepository(db)
// 创建 services
authSvc := service.NewAuthService(userRepo, nil, jwtManager, cacheManager, 8, 5, 15*time.Minute)
authSvc.SetRoleRepositories(userRoleRepo, roleRepo)
userSvc := service.NewUserService(userRepo, userRoleRepo, roleRepo, passwordHistoryRepo)
roleSvc := service.NewRoleService(roleRepo, rolePermissionRepo)
permSvc := service.NewPermissionService(permissionRepo)
deviceSvc := service.NewDeviceService(deviceRepo, userRepo)
loginLogSvc := service.NewLoginLogService(loginLogRepo)
opLogSvc := service.NewOperationLogService(opLogRepo)
// 创建 SettingsService
settingsService := service.NewSettingsService()
// 创建 middleware
rateLimitCfg := config.RateLimitConfig{}
rateLimitMiddleware := middleware.NewRateLimitMiddleware(rateLimitCfg)
authMiddleware := middleware.NewAuthMiddleware(
jwtManager, userRepo, userRoleRepo, roleRepo, rolePermissionRepo, permissionRepo, l1Cache,
)
authMiddleware.SetCacheManager(cacheManager)
opLogMiddleware := middleware.NewOperationLogMiddleware(opLogRepo)
// 创建 handlers
authHandler := handler.NewAuthHandler(authSvc)
userHandler := handler.NewUserHandler(userSvc)
roleHandler := handler.NewRoleHandler(roleSvc)
permHandler := handler.NewPermissionHandler(permSvc)
deviceHandler := handler.NewDeviceHandler(deviceSvc)
logHandler := handler.NewLogHandler(loginLogSvc, opLogSvc)
settingsHandler := handler.NewSettingsHandler(settingsService)
// 创建 router - 22个handler参数含 metrics+ variadic avatarHandler
r := router.NewRouter(
authHandler, userHandler, roleHandler, permHandler, deviceHandler,
logHandler, authMiddleware, rateLimitMiddleware, opLogMiddleware,
nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
nil,
settingsHandler, nil,
)
engine := r.Setup()
server := httptest.NewServer(engine)
// 注册用户用于测试
resp, _ := doPost(server.URL+"/api/v1/auth/register", "", map[string]interface{}{
"username": "admintestsu",
"email": "admintestsu@test.com",
"password": "Password123!",
})
resp.Body.Close()
// 获取 token
loginResp, _ := doPost(server.URL+"/api/v1/auth/login", "", map[string]interface{}{
"account": "admintestsu",
"password": "Password123!",
})
var result map[string]interface{}
json.NewDecoder(loginResp.Body).Decode(&result)
loginResp.Body.Close()
token := ""
if data, ok := result["data"].(map[string]interface{}); ok {
token, _ = data["access_token"].(string)
}
return server, settingsService, token, func() {
server.Close()
if sqlDB, _ := db.DB(); sqlDB != nil {
sqlDB.Close()
}
}
}
// =============================================================================
// Settings API Tests
// =============================================================================
func TestGetSettings_Success(t *testing.T) {
// 仅测试 service 层,不测试 HTTP API
svc := service.NewSettingsService()
settings, err := svc.GetSettings(context.Background())
if err != nil {
t.Fatalf("GetSettings failed: %v", err)
}
if settings.System.Name != "用户管理系统" {
t.Errorf("expected system name '用户管理系统', got '%s'", settings.System.Name)
}
}
func TestGetSettings_Unauthorized(t *testing.T) {
server, _, _, cleanup := setupSettingsTestServer(t)
defer cleanup()
req, _ := http.NewRequest("GET", server.URL+"/api/v1/admin/settings", nil)
// 不设置 Authorization header
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
t.Fatalf("request failed: %v", err)
}
defer resp.Body.Close()
// 无 token 应该返回 401
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("expected status 401, got %d", resp.StatusCode)
}
}
func TestGetSettings_ResponseStructure(t *testing.T) {
// 仅测试 service 层数据结构
svc := service.NewSettingsService()
settings, err := svc.GetSettings(context.Background())
if err != nil {
t.Fatalf("GetSettings failed: %v", err)
}
// 验证 system 字段
if settings.System.Name == "" {
t.Error("System.Name should not be empty")
}
if settings.System.Version == "" {
t.Error("System.Version should not be empty")
}
if settings.System.Environment == "" {
t.Error("System.Environment should not be empty")
}
// 验证 security 字段
if settings.Security.PasswordMinLength == 0 {
t.Error("Security.PasswordMinLength should not be zero")
}
if !settings.Security.PasswordRequireUppercase {
t.Error("Security.PasswordRequireUppercase should be true")
}
// 验证 features 字段
if !settings.Features.EmailVerification {
t.Error("Features.EmailVerification should be true")
}
if len(settings.Features.OAuthProviders) == 0 {
t.Error("Features.OAuthProviders should not be empty")
}
}
// =============================================================================
// SettingsService Unit Tests
// =============================================================================
func TestSettingsService_GetSettings(t *testing.T) {
svc := service.NewSettingsService()
settings, err := svc.GetSettings(context.Background())
if err != nil {
t.Fatalf("GetSettings failed: %v", err)
}
// 验证 system
if settings.System.Name == "" {
t.Error("System.Name should not be empty")
}
if settings.System.Version == "" {
t.Error("System.Version should not be empty")
}
// 验证 security defaults
if settings.Security.PasswordMinLength != 8 {
t.Errorf("PasswordMinLength: got %d, want 8", settings.Security.PasswordMinLength)
}
if !settings.Security.PasswordRequireUppercase {
t.Error("PasswordRequireUppercase should be true")
}
if !settings.Security.PasswordRequireLowercase {
t.Error("PasswordRequireLowercase should be true")
}
if !settings.Security.PasswordRequireNumbers {
t.Error("PasswordRequireNumbers should be true")
}
if !settings.Security.PasswordRequireSymbols {
t.Error("PasswordRequireSymbols should be true")
}
if settings.Security.PasswordHistory != 5 {
t.Errorf("PasswordHistory: got %d, want 5", settings.Security.PasswordHistory)
}
// 验证 features defaults
if !settings.Features.EmailVerification {
t.Error("EmailVerification should be true")
}
if settings.Features.DataExportEnabled != true {
t.Error("DataExportEnabled should be true")
}
}