Coverage: Service 71.7% → 71.8% - classified_error_test.go (10 tests): error wrapping, Unwrap, errors.Is - stats_test.go (12 tests): user stats, dashboard stats, daysAgo utility
170 lines
6.0 KiB
Go
170 lines
6.0 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/user-management-system/internal/domain"
|
|
)
|
|
|
|
// Mock implementations
|
|
type mockStatsUserRepo struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockStatsUserRepo) List(ctx context.Context, offset, limit int) ([]*domain.User, int64, error) {
|
|
args := m.Called(ctx, offset, limit)
|
|
return args.Get(0).([]*domain.User), args.Get(1).(int64), args.Error(2)
|
|
}
|
|
|
|
func (m *mockStatsUserRepo) ListByStatus(ctx context.Context, status domain.UserStatus, offset, limit int) ([]*domain.User, int64, error) {
|
|
args := m.Called(ctx, status, offset, limit)
|
|
return args.Get(0).([]*domain.User), args.Get(1).(int64), args.Error(2)
|
|
}
|
|
|
|
func (m *mockStatsUserRepo) ListCreatedAfter(ctx context.Context, since time.Time, offset, limit int) ([]*domain.User, int64, error) {
|
|
args := m.Called(ctx, since, offset, limit)
|
|
return args.Get(0).([]*domain.User), args.Get(1).(int64), args.Error(2)
|
|
}
|
|
|
|
type mockStatsLoginLogRepo struct {
|
|
mock.Mock
|
|
}
|
|
|
|
func (m *mockStatsLoginLogRepo) CountByResultSince(ctx context.Context, success bool, since time.Time) (int64, error) {
|
|
args := m.Called(ctx, success, since)
|
|
return args.Get(0).(int64), args.Error(1)
|
|
}
|
|
|
|
func setupStatsServiceTest() (*StatsService, *mockStatsUserRepo, *mockStatsLoginLogRepo) {
|
|
userRepo := &mockStatsUserRepo{}
|
|
loginLogRepo := &mockStatsLoginLogRepo{}
|
|
svc := NewStatsService(userRepo, loginLogRepo)
|
|
return svc, userRepo, loginLogRepo
|
|
}
|
|
|
|
// =============================================================================
|
|
// GetUserStats Tests
|
|
// =============================================================================
|
|
|
|
func TestStatsService_GetUserStats_Success(t *testing.T) {
|
|
svc, userRepo, _ := setupStatsServiceTest()
|
|
|
|
// Setup expectations
|
|
userRepo.On("List", mock.Anything, 0, 1).Return([]*domain.User{}, int64(100), nil)
|
|
userRepo.On("ListByStatus", mock.Anything, domain.UserStatusActive, 0, 1).Return([]*domain.User{}, int64(80), nil)
|
|
userRepo.On("ListByStatus", mock.Anything, domain.UserStatusInactive, 0, 1).Return([]*domain.User{}, int64(10), nil)
|
|
userRepo.On("ListByStatus", mock.Anything, domain.UserStatusLocked, 0, 1).Return([]*domain.User{}, int64(5), nil)
|
|
userRepo.On("ListByStatus", mock.Anything, domain.UserStatusDisabled, 0, 1).Return([]*domain.User{}, int64(5), nil)
|
|
userRepo.On("ListCreatedAfter", mock.Anything, mock.Anything, 0, 0).Return([]*domain.User{}, int64(5), nil).Times(3)
|
|
|
|
stats, err := svc.GetUserStats(context.Background())
|
|
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, int64(100), stats.TotalUsers)
|
|
assert.Equal(t, int64(80), stats.ActiveUsers)
|
|
assert.Equal(t, int64(10), stats.InactiveUsers)
|
|
assert.Equal(t, int64(5), stats.LockedUsers)
|
|
assert.Equal(t, int64(5), stats.DisabledUsers)
|
|
userRepo.AssertExpectations(t)
|
|
}
|
|
|
|
func TestStatsService_GetUserStats_ListError(t *testing.T) {
|
|
svc, userRepo, _ := setupStatsServiceTest()
|
|
|
|
userRepo.On("List", mock.Anything, 0, 1).Return([]*domain.User{}, int64(0), errors.New("db error"))
|
|
|
|
stats, err := svc.GetUserStats(context.Background())
|
|
|
|
assert.Error(t, err)
|
|
assert.Nil(t, stats)
|
|
userRepo.AssertExpectations(t)
|
|
}
|
|
|
|
// =============================================================================
|
|
// GetDashboardStats Tests
|
|
// =============================================================================
|
|
|
|
func TestStatsService_GetDashboardStats_Success(t *testing.T) {
|
|
svc, userRepo, loginLogRepo := setupStatsServiceTest()
|
|
|
|
// User stats expectations
|
|
userRepo.On("List", mock.Anything, 0, 1).Return([]*domain.User{}, int64(100), nil)
|
|
userRepo.On("ListByStatus", mock.Anything, mock.Anything, 0, 1).Return([]*domain.User{}, int64(0), nil).Times(4)
|
|
userRepo.On("ListCreatedAfter", mock.Anything, mock.Anything, 0, 0).Return([]*domain.User{}, int64(0), nil).Times(3)
|
|
|
|
// Login stats expectations
|
|
loginLogRepo.On("CountByResultSince", mock.Anything, true, mock.Anything).Return(int64(50), nil).Twice()
|
|
loginLogRepo.On("CountByResultSince", mock.Anything, false, mock.Anything).Return(int64(10), nil).Once()
|
|
|
|
stats, err := svc.GetDashboardStats(context.Background())
|
|
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, stats)
|
|
assert.Equal(t, int64(100), stats.Users.TotalUsers)
|
|
assert.Equal(t, int64(50), stats.Logins.LoginsTodaySuccess)
|
|
assert.Equal(t, int64(10), stats.Logins.LoginsTodayFailed)
|
|
userRepo.AssertExpectations(t)
|
|
loginLogRepo.AssertExpectations(t)
|
|
}
|
|
|
|
func TestStatsService_GetDashboardStats_UserStatsError(t *testing.T) {
|
|
svc, userRepo, _ := setupStatsServiceTest()
|
|
|
|
userRepo.On("List", mock.Anything, 0, 1).Return([]*domain.User{}, int64(0), errors.New("db error"))
|
|
|
|
stats, err := svc.GetDashboardStats(context.Background())
|
|
|
|
assert.Error(t, err)
|
|
assert.Nil(t, stats)
|
|
userRepo.AssertExpectations(t)
|
|
}
|
|
|
|
// =============================================================================
|
|
// daysAgo Tests
|
|
// =============================================================================
|
|
|
|
func TestDaysAgo_Today(t *testing.T) {
|
|
result := daysAgo(0)
|
|
now := time.Now()
|
|
|
|
// Should be today at midnight
|
|
assert.Equal(t, now.Year(), result.Year())
|
|
assert.Equal(t, now.Month(), result.Month())
|
|
assert.Equal(t, now.Day(), result.Day())
|
|
assert.Equal(t, 0, result.Hour())
|
|
assert.Equal(t, 0, result.Minute())
|
|
assert.Equal(t, 0, result.Second())
|
|
}
|
|
|
|
func TestDaysAgo_Yesterday(t *testing.T) {
|
|
result := daysAgo(1)
|
|
expected := time.Now().AddDate(0, 0, -1)
|
|
|
|
assert.Equal(t, expected.Year(), result.Year())
|
|
assert.Equal(t, expected.Month(), result.Month())
|
|
assert.Equal(t, expected.Day(), result.Day())
|
|
}
|
|
|
|
func TestDaysAgo_OneWeek(t *testing.T) {
|
|
result := daysAgo(7)
|
|
expected := time.Now().AddDate(0, 0, -7)
|
|
|
|
assert.Equal(t, expected.Year(), result.Year())
|
|
assert.Equal(t, expected.Month(), result.Month())
|
|
assert.Equal(t, expected.Day(), result.Day())
|
|
}
|
|
|
|
func TestDaysAgo_OneMonth(t *testing.T) {
|
|
result := daysAgo(30)
|
|
expected := time.Now().AddDate(0, 0, -30)
|
|
|
|
assert.Equal(t, expected.Year(), result.Year())
|
|
assert.Equal(t, expected.Month(), result.Month())
|
|
assert.Equal(t, expected.Day(), result.Day())
|
|
}
|