P0-01: Add ESCAPE clause to LIKE queries in operation_log.go and device.go P0-02: Add atomic Increment to L1Cache and L2Cache interfaces P0-07: Add TOTP verification step after password login P1-01: Sanitize error messages in error.go middleware P1-03: Remove err.Error() from export error messages P1-04: Add error return to CountByResultSince in login_log.go P1-05: Add transactional DeleteCascade to RoleRepository P1-06: Add PasswordChangedAt tracking for JWT token invalidation P1-07: Wrap theme SetDefault in database transaction P1-08: Use config values for database pool parameters P1-09: Add rows.Err() checks in social_account_repo.go P1-10: Validate sortOrder with map in user.go ORDER BY P1-11: Add GORM tags to Announcement struct P1-15: Add pageSize upper limit (100) to device and log handlers
135 lines
3.6 KiB
Go
135 lines
3.6 KiB
Go
package service_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/user-management-system/internal/domain"
|
|
"github.com/user-management-system/internal/service"
|
|
)
|
|
|
|
// =============================================================================
|
|
// Stats Service Tests - TDD approach
|
|
// =============================================================================
|
|
|
|
// mockStatsUserRepo 模拟用户仓储
|
|
type mockStatsUserRepo struct {
|
|
totalUsers int64
|
|
activeUsers int64
|
|
inactiveUsers int64
|
|
lockedUsers int64
|
|
disabledUsers int64
|
|
newUsersToday int64
|
|
}
|
|
|
|
func (m *mockStatsUserRepo) List(ctx context.Context, offset, limit int) ([]*domain.User, int64, error) {
|
|
return nil, m.totalUsers, nil
|
|
}
|
|
|
|
func (m *mockStatsUserRepo) ListByStatus(ctx context.Context, status domain.UserStatus, offset, limit int) ([]*domain.User, int64, error) {
|
|
switch status {
|
|
case domain.UserStatusActive:
|
|
return nil, m.activeUsers, nil
|
|
case domain.UserStatusInactive:
|
|
return nil, m.inactiveUsers, nil
|
|
case domain.UserStatusLocked:
|
|
return nil, m.lockedUsers, nil
|
|
case domain.UserStatusDisabled:
|
|
return nil, m.disabledUsers, nil
|
|
}
|
|
return nil, 0, nil
|
|
}
|
|
|
|
func (m *mockStatsUserRepo) ListCreatedAfter(ctx context.Context, since time.Time, offset, limit int) ([]*domain.User, int64, error) {
|
|
return nil, m.newUsersToday, nil
|
|
}
|
|
|
|
// mockStatsLoginLogRepo 模拟登录日志仓储
|
|
type mockStatsLoginLogRepo struct {
|
|
successCount int64
|
|
failedCount int64
|
|
weekCount int64
|
|
}
|
|
|
|
func (m *mockStatsLoginLogRepo) CountByResultSince(ctx context.Context, success bool, since time.Time) (int64, error) {
|
|
if success {
|
|
return m.successCount, nil
|
|
}
|
|
return m.failedCount, nil
|
|
}
|
|
|
|
func TestStatsService_GetUserStats(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
t.Run("获取用户统计", func(t *testing.T) {
|
|
userRepo := &mockStatsUserRepo{
|
|
totalUsers: 100,
|
|
activeUsers: 80,
|
|
inactiveUsers: 10,
|
|
lockedUsers: 5,
|
|
disabledUsers: 5,
|
|
newUsersToday: 3,
|
|
}
|
|
loginLogRepo := &mockStatsLoginLogRepo{}
|
|
svc := service.NewStatsService(userRepo, loginLogRepo)
|
|
|
|
stats, err := svc.GetUserStats(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetUserStats failed: %v", err)
|
|
}
|
|
|
|
if stats.TotalUsers != 100 {
|
|
t.Errorf("期望 TotalUsers=100, 得到 %d", stats.TotalUsers)
|
|
}
|
|
if stats.ActiveUsers != 80 {
|
|
t.Errorf("期望 ActiveUsers=80, 得到 %d", stats.ActiveUsers)
|
|
}
|
|
if stats.InactiveUsers != 10 {
|
|
t.Errorf("期望 InactiveUsers=10, 得到 %d", stats.InactiveUsers)
|
|
}
|
|
if stats.LockedUsers != 5 {
|
|
t.Errorf("期望 LockedUsers=5, 得到 %d", stats.LockedUsers)
|
|
}
|
|
if stats.DisabledUsers != 5 {
|
|
t.Errorf("期望 DisabledUsers=5, 得到 %d", stats.DisabledUsers)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestStatsService_GetDashboardStats(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
t.Run("获取仪表盘统计", func(t *testing.T) {
|
|
userRepo := &mockStatsUserRepo{
|
|
totalUsers: 50,
|
|
activeUsers: 40,
|
|
inactiveUsers: 5,
|
|
lockedUsers: 3,
|
|
disabledUsers: 2,
|
|
newUsersToday: 2,
|
|
}
|
|
loginLogRepo := &mockStatsLoginLogRepo{
|
|
successCount: 100,
|
|
failedCount: 10,
|
|
weekCount: 500,
|
|
}
|
|
svc := service.NewStatsService(userRepo, loginLogRepo)
|
|
|
|
stats, err := svc.GetDashboardStats(ctx)
|
|
if err != nil {
|
|
t.Fatalf("GetDashboardStats failed: %v", err)
|
|
}
|
|
|
|
if stats.Users.TotalUsers != 50 {
|
|
t.Errorf("期望 Users.TotalUsers=50, 得到 %d", stats.Users.TotalUsers)
|
|
}
|
|
if stats.Logins.LoginsTodaySuccess != 100 {
|
|
t.Errorf("期望 LoginsTodaySuccess=100, 得到 %d", stats.Logins.LoginsTodaySuccess)
|
|
}
|
|
if stats.Logins.LoginsTodayFailed != 10 {
|
|
t.Errorf("期望 LoginsTodayFailed=10, 得到 %d", stats.Logins.LoginsTodayFailed)
|
|
}
|
|
})
|
|
}
|