Files
user-system/internal/service/stats_internal_test.go
long-agent 8095307d82 fix: P0/P1 security and quality fixes
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
2026-04-18 15:33:12 +08:00

133 lines
3.5 KiB
Go

package service
import (
"context"
"testing"
"time"
"github.com/user-management-system/internal/domain"
)
// =============================================================================
// Stats Service Internal Tests
// =============================================================================
// mockStatsUserRepoInternal mocks user repository for stats tests
type mockStatsUserRepoInternal struct {
totalUsers int64
activeUsers int64
inactiveUsers int64
lockedUsers int64
disabledUsers int64
newUsersToday int64
}
func (m *mockStatsUserRepoInternal) List(ctx context.Context, offset, limit int) ([]*domain.User, int64, error) {
return nil, m.totalUsers, nil
}
func (m *mockStatsUserRepoInternal) 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 *mockStatsUserRepoInternal) ListCreatedAfter(ctx context.Context, since time.Time, offset, limit int) ([]*domain.User, int64, error) {
return nil, m.newUsersToday, nil
}
// mockStatsLoginLogRepoInternal mocks login log repository for stats tests
type mockStatsLoginLogRepoInternal struct {
successCount int64
failedCount int64
weekCount int64
}
func (m *mockStatsLoginLogRepoInternal) CountByResultSince(ctx context.Context, success bool, since time.Time) (int64, error) {
if success {
return m.successCount, nil
}
return m.failedCount, nil
}
func TestStatsService_GetDashboardStats_Internal(t *testing.T) {
userRepo := &mockStatsUserRepoInternal{
totalUsers: 100,
activeUsers: 80,
inactiveUsers: 10,
lockedUsers: 5,
disabledUsers: 5,
newUsersToday: 3,
}
loginLogRepo := &mockStatsLoginLogRepoInternal{
successCount: 50,
failedCount: 5,
weekCount: 200,
}
svc := NewStatsService(userRepo, loginLogRepo)
ctx := context.Background()
stats, err := svc.GetDashboardStats(ctx)
if err != nil {
t.Fatalf("GetDashboardStats failed: %v", err)
}
if stats.Users.TotalUsers != 100 {
t.Errorf("Expected TotalUsers=100, got %d", stats.Users.TotalUsers)
}
if stats.Logins.LoginsTodaySuccess != 50 {
t.Errorf("Expected LoginsTodaySuccess=50, got %d", stats.Logins.LoginsTodaySuccess)
}
if stats.Logins.LoginsTodayFailed != 5 {
t.Errorf("Expected LoginsTodayFailed=5, got %d", stats.Logins.LoginsTodayFailed)
}
}
func TestStatsService_GetDashboardStats_NilLoginLogRepo(t *testing.T) {
userRepo := &mockStatsUserRepoInternal{
totalUsers: 50,
activeUsers: 40,
inactiveUsers: 5,
lockedUsers: 3,
disabledUsers: 2,
newUsersToday: 2,
}
svc := NewStatsService(userRepo, nil)
ctx := context.Background()
stats, err := svc.GetDashboardStats(ctx)
if err != nil {
t.Fatalf("GetDashboardStats failed: %v", err)
}
if stats.Users.TotalUsers != 50 {
t.Errorf("Expected TotalUsers=50, got %d", stats.Users.TotalUsers)
}
// Login stats should be 0 when loginLogRepo is nil
if stats.Logins.LoginsTodaySuccess != 0 {
t.Errorf("Expected LoginsTodaySuccess=0, got %d", stats.Logins.LoginsTodaySuccess)
}
}
func TestDaysAgo(t *testing.T) {
result := daysAgo(0)
if result.After(time.Now()) {
t.Error("daysAgo(0) should not be in the future")
}
result = daysAgo(7)
if result.After(time.Now()) {
t.Error("daysAgo(7) should not be in the future")
}
}