add tests
This commit is contained in:
@@ -1,119 +1,920 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { AdminService } from '@/lib/services/admin/AdminService';
|
||||
import { AdminApiClient } from '@/lib/api/admin/AdminApiClient';
|
||||
import { ApiError } from '@/lib/api/base/ApiError';
|
||||
import type { DashboardStats, UserDto, UserListResponse } from '@/lib/types/admin';
|
||||
|
||||
// Mock the API client
|
||||
vi.mock('@/lib/api/admin/AdminApiClient');
|
||||
|
||||
describe('AdminService', () => {
|
||||
let service: AdminService;
|
||||
let mockApiClient: any;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
service = new AdminService();
|
||||
mockApiClient = (service as any).apiClient;
|
||||
});
|
||||
|
||||
describe('happy paths', () => {
|
||||
it('should successfully fetch dashboard statistics', () => {
|
||||
// TODO: Implement test
|
||||
it('should successfully fetch dashboard statistics', async () => {
|
||||
const mockStats: DashboardStats = {
|
||||
totalUsers: 1250,
|
||||
activeUsers: 1100,
|
||||
suspendedUsers: 50,
|
||||
deletedUsers: 100,
|
||||
systemAdmins: 5,
|
||||
recentLogins: 450,
|
||||
newUsersToday: 12,
|
||||
userGrowth: [
|
||||
{ label: 'This week', value: 45, color: '#10b981' },
|
||||
{ label: 'Last week', value: 38, color: '#3b82f6' },
|
||||
],
|
||||
roleDistribution: [
|
||||
{ label: 'Users', value: 1200, color: '#6b7280' },
|
||||
{ label: 'Admins', value: 50, color: '#8b5cf6' },
|
||||
],
|
||||
statusDistribution: {
|
||||
active: 1100,
|
||||
suspended: 50,
|
||||
deleted: 100,
|
||||
},
|
||||
activityTimeline: [
|
||||
{ date: '2024-01-01', newUsers: 10, logins: 200 },
|
||||
{ date: '2024-01-02', newUsers: 15, logins: 220 },
|
||||
],
|
||||
};
|
||||
|
||||
mockApiClient.getDashboardStats.mockResolvedValue(mockStats);
|
||||
|
||||
const result = await service.getDashboardStats();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toEqual(mockStats);
|
||||
expect(mockApiClient.getDashboardStats).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should successfully list users with filtering', () => {
|
||||
// TODO: Implement test
|
||||
it('should successfully list users with filtering', async () => {
|
||||
const mockUsers: UserDto[] = [
|
||||
{
|
||||
id: '1',
|
||||
email: 'admin@example.com',
|
||||
displayName: 'Admin User',
|
||||
roles: ['owner', 'admin'],
|
||||
status: 'active',
|
||||
isSystemAdmin: true,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
lastLoginAt: '2024-01-15T10:00:00.000Z',
|
||||
primaryDriverId: 'driver-1',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
email: 'user@example.com',
|
||||
displayName: 'Regular User',
|
||||
roles: ['user'],
|
||||
status: 'active',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-02T00:00:00.000Z',
|
||||
updatedAt: '2024-01-02T00:00:00.000Z',
|
||||
lastLoginAt: '2024-01-14T15:00:00.000Z',
|
||||
},
|
||||
];
|
||||
|
||||
const mockResponse: UserListResponse = {
|
||||
users: mockUsers,
|
||||
total: 2,
|
||||
page: 1,
|
||||
limit: 50,
|
||||
totalPages: 1,
|
||||
};
|
||||
|
||||
mockApiClient.listUsers.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.listUsers();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toEqual(mockResponse);
|
||||
expect(mockApiClient.listUsers).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should successfully update user status', () => {
|
||||
// TODO: Implement test
|
||||
it('should successfully update user status', async () => {
|
||||
const userId = 'user-123';
|
||||
const newStatus = 'suspended';
|
||||
const mockUpdatedUser: UserDto = {
|
||||
id: userId,
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
roles: ['user'],
|
||||
status: newStatus,
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
};
|
||||
|
||||
mockApiClient.updateUserStatus.mockResolvedValue(mockUpdatedUser);
|
||||
|
||||
const result = await service.updateUserStatus(userId, newStatus);
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toEqual(mockUpdatedUser);
|
||||
expect(mockApiClient.updateUserStatus).toHaveBeenCalledWith(userId, newStatus);
|
||||
});
|
||||
|
||||
it('should successfully delete user', () => {
|
||||
// TODO: Implement test
|
||||
it('should successfully delete user', async () => {
|
||||
mockApiClient.deleteUser.mockResolvedValue(undefined);
|
||||
|
||||
const result = await service.deleteUser();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeUndefined();
|
||||
expect(mockApiClient.deleteUser).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('failure modes', () => {
|
||||
it('should handle dashboard stats fetch errors', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle dashboard stats fetch errors', async () => {
|
||||
const error = new ApiError(
|
||||
'Dashboard stats not found',
|
||||
'NOT_FOUND',
|
||||
{
|
||||
endpoint: '/admin/dashboard/stats',
|
||||
method: 'GET',
|
||||
timestamp: new Date().toISOString(),
|
||||
statusCode: 404,
|
||||
}
|
||||
);
|
||||
|
||||
mockApiClient.getDashboardStats.mockRejectedValue(error);
|
||||
|
||||
const result = await service.getDashboardStats();
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toEqual({
|
||||
type: 'notFound',
|
||||
message: 'Dashboard stats not found',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle user list fetch errors', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle user list fetch errors', async () => {
|
||||
const error = new ApiError(
|
||||
'Failed to fetch users',
|
||||
'SERVER_ERROR',
|
||||
{
|
||||
endpoint: '/admin/users',
|
||||
method: 'GET',
|
||||
timestamp: new Date().toISOString(),
|
||||
statusCode: 500,
|
||||
}
|
||||
);
|
||||
|
||||
mockApiClient.listUsers.mockRejectedValue(error);
|
||||
|
||||
const result = await service.listUsers();
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toEqual({
|
||||
type: 'serverError',
|
||||
message: 'Failed to fetch users',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle user status update errors', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle user status update errors', async () => {
|
||||
const error = new ApiError(
|
||||
'Invalid user ID',
|
||||
'VALIDATION_ERROR',
|
||||
{
|
||||
endpoint: '/admin/users/user-123/status',
|
||||
method: 'PATCH',
|
||||
timestamp: new Date().toISOString(),
|
||||
statusCode: 400,
|
||||
}
|
||||
);
|
||||
|
||||
mockApiClient.updateUserStatus.mockRejectedValue(error);
|
||||
|
||||
const result = await service.updateUserStatus('user-123', 'active');
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toEqual({
|
||||
type: 'unknown',
|
||||
message: 'Invalid user ID',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle user deletion errors', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle user deletion errors', async () => {
|
||||
const error = new ApiError(
|
||||
'User not found',
|
||||
'NOT_FOUND',
|
||||
{
|
||||
endpoint: '/admin/users/user-123',
|
||||
method: 'DELETE',
|
||||
timestamp: new Date().toISOString(),
|
||||
statusCode: 404,
|
||||
}
|
||||
);
|
||||
|
||||
mockApiClient.deleteUser.mockRejectedValue(error);
|
||||
|
||||
const result = await service.deleteUser();
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toEqual({
|
||||
type: 'notFound',
|
||||
message: 'User not found',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle invalid user ID', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle invalid user ID', async () => {
|
||||
const error = new ApiError(
|
||||
'Invalid user ID format',
|
||||
'VALIDATION_ERROR',
|
||||
{
|
||||
endpoint: '/admin/users/invalid-id/status',
|
||||
method: 'PATCH',
|
||||
timestamp: new Date().toISOString(),
|
||||
statusCode: 400,
|
||||
}
|
||||
);
|
||||
|
||||
mockApiClient.updateUserStatus.mockRejectedValue(error);
|
||||
|
||||
const result = await service.updateUserStatus('invalid-id', 'active');
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toEqual({
|
||||
type: 'unknown',
|
||||
message: 'Invalid user ID format',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle invalid status value', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle invalid status value', async () => {
|
||||
const error = new ApiError(
|
||||
'Invalid status value',
|
||||
'VALIDATION_ERROR',
|
||||
{
|
||||
endpoint: '/admin/users/user-123/status',
|
||||
method: 'PATCH',
|
||||
timestamp: new Date().toISOString(),
|
||||
statusCode: 400,
|
||||
}
|
||||
);
|
||||
|
||||
mockApiClient.updateUserStatus.mockRejectedValue(error);
|
||||
|
||||
const result = await service.updateUserStatus('user-123', 'invalid-status');
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError()).toEqual({
|
||||
type: 'unknown',
|
||||
message: 'Invalid status value',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('retries', () => {
|
||||
it('should retry on transient API failures', () => {
|
||||
// TODO: Implement test
|
||||
it('should retry on transient API failures', async () => {
|
||||
const mockStats: DashboardStats = {
|
||||
totalUsers: 1250,
|
||||
activeUsers: 1100,
|
||||
suspendedUsers: 50,
|
||||
deletedUsers: 100,
|
||||
systemAdmins: 5,
|
||||
recentLogins: 450,
|
||||
newUsersToday: 12,
|
||||
userGrowth: [],
|
||||
roleDistribution: [],
|
||||
statusDistribution: { active: 1100, suspended: 50, deleted: 100 },
|
||||
activityTimeline: [],
|
||||
};
|
||||
|
||||
const error = new ApiError(
|
||||
'Network error',
|
||||
'NETWORK_ERROR',
|
||||
{
|
||||
endpoint: '/admin/dashboard/stats',
|
||||
method: 'GET',
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
);
|
||||
|
||||
// First call fails, second succeeds
|
||||
mockApiClient.getDashboardStats
|
||||
.mockRejectedValueOnce(error)
|
||||
.mockResolvedValueOnce(mockStats);
|
||||
|
||||
const result = await service.getDashboardStats();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toEqual(mockStats);
|
||||
expect(mockApiClient.getDashboardStats).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should retry on timeout when fetching dashboard stats', () => {
|
||||
// TODO: Implement test
|
||||
it('should retry on timeout when fetching dashboard stats', async () => {
|
||||
const mockStats: DashboardStats = {
|
||||
totalUsers: 1250,
|
||||
activeUsers: 1100,
|
||||
suspendedUsers: 50,
|
||||
deletedUsers: 100,
|
||||
systemAdmins: 5,
|
||||
recentLogins: 450,
|
||||
newUsersToday: 12,
|
||||
userGrowth: [],
|
||||
roleDistribution: [],
|
||||
statusDistribution: { active: 1100, suspended: 50, deleted: 100 },
|
||||
activityTimeline: [],
|
||||
};
|
||||
|
||||
const error = new ApiError(
|
||||
'Request timed out after 30 seconds',
|
||||
'TIMEOUT_ERROR',
|
||||
{
|
||||
endpoint: '/admin/dashboard/stats',
|
||||
method: 'GET',
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
);
|
||||
|
||||
// First call times out, second succeeds
|
||||
mockApiClient.getDashboardStats
|
||||
.mockRejectedValueOnce(error)
|
||||
.mockResolvedValueOnce(mockStats);
|
||||
|
||||
const result = await service.getDashboardStats();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toEqual(mockStats);
|
||||
expect(mockApiClient.getDashboardStats).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fallback logic', () => {
|
||||
it('should use mock data when API is unavailable', () => {
|
||||
// TODO: Implement test
|
||||
it('should use mock data when API is unavailable', async () => {
|
||||
const error = new ApiError(
|
||||
'Unable to connect to server',
|
||||
'NETWORK_ERROR',
|
||||
{
|
||||
endpoint: '/admin/dashboard/stats',
|
||||
method: 'GET',
|
||||
timestamp: new Date().toISOString(),
|
||||
}
|
||||
);
|
||||
|
||||
mockApiClient.getDashboardStats.mockRejectedValue(error);
|
||||
|
||||
const result = await service.getDashboardStats();
|
||||
|
||||
// The service should return the mock data from the service itself
|
||||
expect(result.isOk()).toBe(true);
|
||||
const stats = result.unwrap();
|
||||
expect(stats.totalUsers).toBe(1250);
|
||||
expect(stats.activeUsers).toBe(1100);
|
||||
});
|
||||
|
||||
it('should handle partial user data gracefully', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle partial user data gracefully', async () => {
|
||||
const mockUsers: UserDto[] = [
|
||||
{
|
||||
id: '1',
|
||||
email: 'admin@example.com',
|
||||
displayName: 'Admin User',
|
||||
roles: ['owner', 'admin'],
|
||||
status: 'active',
|
||||
isSystemAdmin: true,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
lastLoginAt: '2024-01-15T10:00:00.000Z',
|
||||
primaryDriverId: 'driver-1',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
email: 'user@example.com',
|
||||
displayName: 'Regular User',
|
||||
roles: ['user'],
|
||||
status: 'active',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-02T00:00:00.000Z',
|
||||
updatedAt: '2024-01-02T00:00:00.000Z',
|
||||
// Missing lastLoginAt - partial data
|
||||
},
|
||||
];
|
||||
|
||||
const mockResponse: UserListResponse = {
|
||||
users: mockUsers,
|
||||
total: 2,
|
||||
page: 1,
|
||||
limit: 50,
|
||||
totalPages: 1,
|
||||
};
|
||||
|
||||
mockApiClient.listUsers.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.listUsers();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const response = result.unwrap();
|
||||
expect(response.users).toHaveLength(2);
|
||||
expect(response.users[0].lastLoginAt).toBeDefined();
|
||||
expect(response.users[1].lastLoginAt).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should handle empty user list', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle empty user list', async () => {
|
||||
const mockResponse: UserListResponse = {
|
||||
users: [],
|
||||
total: 0,
|
||||
page: 1,
|
||||
limit: 50,
|
||||
totalPages: 0,
|
||||
};
|
||||
|
||||
mockApiClient.listUsers.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.listUsers();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const response = result.unwrap();
|
||||
expect(response.users).toHaveLength(0);
|
||||
expect(response.total).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('aggregation logic', () => {
|
||||
it('should aggregate dashboard statistics correctly', () => {
|
||||
// TODO: Implement test
|
||||
it('should aggregate dashboard statistics correctly', async () => {
|
||||
const mockStats: DashboardStats = {
|
||||
totalUsers: 1250,
|
||||
activeUsers: 1100,
|
||||
suspendedUsers: 50,
|
||||
deletedUsers: 100,
|
||||
systemAdmins: 5,
|
||||
recentLogins: 450,
|
||||
newUsersToday: 12,
|
||||
userGrowth: [
|
||||
{ label: 'This week', value: 45, color: '#10b981' },
|
||||
{ label: 'Last week', value: 38, color: '#3b82f6' },
|
||||
],
|
||||
roleDistribution: [
|
||||
{ label: 'Users', value: 1200, color: '#6b7280' },
|
||||
{ label: 'Admins', value: 50, color: '#8b5cf6' },
|
||||
],
|
||||
statusDistribution: {
|
||||
active: 1100,
|
||||
suspended: 50,
|
||||
deleted: 100,
|
||||
},
|
||||
activityTimeline: [
|
||||
{ date: '2024-01-01', newUsers: 10, logins: 200 },
|
||||
{ date: '2024-01-02', newUsers: 15, logins: 220 },
|
||||
],
|
||||
};
|
||||
|
||||
mockApiClient.getDashboardStats.mockResolvedValue(mockStats);
|
||||
|
||||
const result = await service.getDashboardStats();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const stats = result.unwrap();
|
||||
|
||||
// Verify aggregation
|
||||
expect(stats.totalUsers).toBe(1250);
|
||||
expect(stats.activeUsers).toBe(1100);
|
||||
expect(stats.suspendedUsers).toBe(50);
|
||||
expect(stats.deletedUsers).toBe(100);
|
||||
expect(stats.systemAdmins).toBe(5);
|
||||
expect(stats.recentLogins).toBe(450);
|
||||
expect(stats.newUsersToday).toBe(12);
|
||||
|
||||
// Verify user growth aggregation
|
||||
expect(stats.userGrowth).toHaveLength(2);
|
||||
expect(stats.userGrowth[0].value).toBe(45);
|
||||
expect(stats.userGrowth[1].value).toBe(38);
|
||||
|
||||
// Verify role distribution aggregation
|
||||
expect(stats.roleDistribution).toHaveLength(2);
|
||||
expect(stats.roleDistribution[0].value).toBe(1200);
|
||||
expect(stats.roleDistribution[1].value).toBe(50);
|
||||
|
||||
// Verify status distribution aggregation
|
||||
expect(stats.statusDistribution.active).toBe(1100);
|
||||
expect(stats.statusDistribution.suspended).toBe(50);
|
||||
expect(stats.statusDistribution.deleted).toBe(100);
|
||||
|
||||
// Verify activity timeline aggregation
|
||||
expect(stats.activityTimeline).toHaveLength(2);
|
||||
expect(stats.activityTimeline[0].newUsers).toBe(10);
|
||||
expect(stats.activityTimeline[1].newUsers).toBe(15);
|
||||
});
|
||||
|
||||
it('should calculate user growth metrics', () => {
|
||||
// TODO: Implement test
|
||||
it('should calculate user growth metrics', async () => {
|
||||
const mockStats: DashboardStats = {
|
||||
totalUsers: 1250,
|
||||
activeUsers: 1100,
|
||||
suspendedUsers: 50,
|
||||
deletedUsers: 100,
|
||||
systemAdmins: 5,
|
||||
recentLogins: 450,
|
||||
newUsersToday: 12,
|
||||
userGrowth: [
|
||||
{ label: 'This week', value: 45, color: '#10b981' },
|
||||
{ label: 'Last week', value: 38, color: '#3b82f6' },
|
||||
],
|
||||
roleDistribution: [],
|
||||
statusDistribution: { active: 1100, suspended: 50, deleted: 100 },
|
||||
activityTimeline: [],
|
||||
};
|
||||
|
||||
mockApiClient.getDashboardStats.mockResolvedValue(mockStats);
|
||||
|
||||
const result = await service.getDashboardStats();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const stats = result.unwrap();
|
||||
|
||||
// Calculate growth percentage
|
||||
const growth = stats.userGrowth;
|
||||
expect(growth).toHaveLength(2);
|
||||
expect(growth[0].value).toBe(45);
|
||||
expect(growth[1].value).toBe(38);
|
||||
|
||||
// Verify growth trend
|
||||
const growthTrend = ((45 - 38) / 38) * 100;
|
||||
expect(growthTrend).toBeCloseTo(18.42, 1);
|
||||
});
|
||||
|
||||
it('should aggregate role distribution data', () => {
|
||||
// TODO: Implement test
|
||||
it('should aggregate role distribution data', async () => {
|
||||
const mockStats: DashboardStats = {
|
||||
totalUsers: 1250,
|
||||
activeUsers: 1100,
|
||||
suspendedUsers: 50,
|
||||
deletedUsers: 100,
|
||||
systemAdmins: 5,
|
||||
recentLogins: 450,
|
||||
newUsersToday: 12,
|
||||
userGrowth: [],
|
||||
roleDistribution: [
|
||||
{ label: 'Users', value: 1200, color: '#6b7280' },
|
||||
{ label: 'Admins', value: 50, color: '#8b5cf6' },
|
||||
],
|
||||
statusDistribution: { active: 1100, suspended: 50, deleted: 100 },
|
||||
activityTimeline: [],
|
||||
};
|
||||
|
||||
mockApiClient.getDashboardStats.mockResolvedValue(mockStats);
|
||||
|
||||
const result = await service.getDashboardStats();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const stats = result.unwrap();
|
||||
|
||||
// Verify role distribution
|
||||
expect(stats.roleDistribution).toHaveLength(2);
|
||||
expect(stats.roleDistribution[0].label).toBe('Users');
|
||||
expect(stats.roleDistribution[0].value).toBe(1200);
|
||||
expect(stats.roleDistribution[1].label).toBe('Admins');
|
||||
expect(stats.roleDistribution[1].value).toBe(50);
|
||||
|
||||
// Verify total matches
|
||||
const totalRoles = stats.roleDistribution.reduce((sum, role) => sum + role.value, 0);
|
||||
expect(totalRoles).toBe(1250);
|
||||
});
|
||||
|
||||
it('should aggregate status distribution data', () => {
|
||||
// TODO: Implement test
|
||||
it('should aggregate status distribution data', async () => {
|
||||
const mockStats: DashboardStats = {
|
||||
totalUsers: 1250,
|
||||
activeUsers: 1100,
|
||||
suspendedUsers: 50,
|
||||
deletedUsers: 100,
|
||||
systemAdmins: 5,
|
||||
recentLogins: 450,
|
||||
newUsersToday: 12,
|
||||
userGrowth: [],
|
||||
roleDistribution: [],
|
||||
statusDistribution: {
|
||||
active: 1100,
|
||||
suspended: 50,
|
||||
deleted: 100,
|
||||
},
|
||||
activityTimeline: [],
|
||||
};
|
||||
|
||||
mockApiClient.getDashboardStats.mockResolvedValue(mockStats);
|
||||
|
||||
const result = await service.getDashboardStats();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const stats = result.unwrap();
|
||||
|
||||
// Verify status distribution
|
||||
expect(stats.statusDistribution.active).toBe(1100);
|
||||
expect(stats.statusDistribution.suspended).toBe(50);
|
||||
expect(stats.statusDistribution.deleted).toBe(100);
|
||||
|
||||
// Verify total matches
|
||||
const totalStatuses = stats.statusDistribution.active + stats.statusDistribution.suspended + stats.statusDistribution.deleted;
|
||||
expect(totalStatuses).toBe(1250);
|
||||
});
|
||||
|
||||
it('should aggregate activity timeline data', () => {
|
||||
// TODO: Implement test
|
||||
it('should aggregate activity timeline data', async () => {
|
||||
const mockStats: DashboardStats = {
|
||||
totalUsers: 1250,
|
||||
activeUsers: 1100,
|
||||
suspendedUsers: 50,
|
||||
deletedUsers: 100,
|
||||
systemAdmins: 5,
|
||||
recentLogins: 450,
|
||||
newUsersToday: 12,
|
||||
userGrowth: [],
|
||||
roleDistribution: [],
|
||||
statusDistribution: { active: 1100, suspended: 50, deleted: 100 },
|
||||
activityTimeline: [
|
||||
{ date: '2024-01-01', newUsers: 10, logins: 200 },
|
||||
{ date: '2024-01-02', newUsers: 15, logins: 220 },
|
||||
{ date: '2024-01-03', newUsers: 20, logins: 250 },
|
||||
],
|
||||
};
|
||||
|
||||
mockApiClient.getDashboardStats.mockResolvedValue(mockStats);
|
||||
|
||||
const result = await service.getDashboardStats();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const stats = result.unwrap();
|
||||
|
||||
// Verify activity timeline
|
||||
expect(stats.activityTimeline).toHaveLength(3);
|
||||
expect(stats.activityTimeline[0].date).toBe('2024-01-01');
|
||||
expect(stats.activityTimeline[0].newUsers).toBe(10);
|
||||
expect(stats.activityTimeline[0].logins).toBe(200);
|
||||
|
||||
// Calculate total new users
|
||||
const totalNewUsers = stats.activityTimeline.reduce((sum, day) => sum + day.newUsers, 0);
|
||||
expect(totalNewUsers).toBe(45);
|
||||
|
||||
// Calculate total logins
|
||||
const totalLogins = stats.activityTimeline.reduce((sum, day) => sum + day.logins, 0);
|
||||
expect(totalLogins).toBe(670);
|
||||
});
|
||||
});
|
||||
|
||||
describe('decision branches', () => {
|
||||
it('should handle different user roles correctly', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle different user roles correctly', async () => {
|
||||
const mockUsers: UserDto[] = [
|
||||
{
|
||||
id: '1',
|
||||
email: 'owner@example.com',
|
||||
displayName: 'Owner',
|
||||
roles: ['owner'],
|
||||
status: 'active',
|
||||
isSystemAdmin: true,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
email: 'admin@example.com',
|
||||
displayName: 'Admin',
|
||||
roles: ['admin'],
|
||||
status: 'active',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-02T00:00:00.000Z',
|
||||
updatedAt: '2024-01-02T00:00:00.000Z',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
email: 'user@example.com',
|
||||
displayName: 'User',
|
||||
roles: ['user'],
|
||||
status: 'active',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-03T00:00:00.000Z',
|
||||
updatedAt: '2024-01-03T00:00:00.000Z',
|
||||
},
|
||||
];
|
||||
|
||||
const mockResponse: UserListResponse = {
|
||||
users: mockUsers,
|
||||
total: 3,
|
||||
page: 1,
|
||||
limit: 50,
|
||||
totalPages: 1,
|
||||
};
|
||||
|
||||
mockApiClient.listUsers.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.listUsers();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const response = result.unwrap();
|
||||
|
||||
// Verify different roles are handled correctly
|
||||
expect(response.users[0].roles).toContain('owner');
|
||||
expect(response.users[0].isSystemAdmin).toBe(true);
|
||||
expect(response.users[1].roles).toContain('admin');
|
||||
expect(response.users[1].isSystemAdmin).toBe(false);
|
||||
expect(response.users[2].roles).toContain('user');
|
||||
expect(response.users[2].isSystemAdmin).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle different user statuses', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle different user statuses', async () => {
|
||||
const mockUsers: UserDto[] = [
|
||||
{
|
||||
id: '1',
|
||||
email: 'active@example.com',
|
||||
displayName: 'Active User',
|
||||
roles: ['user'],
|
||||
status: 'active',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
email: 'suspended@example.com',
|
||||
displayName: 'Suspended User',
|
||||
roles: ['user'],
|
||||
status: 'suspended',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-02T00:00:00.000Z',
|
||||
updatedAt: '2024-01-02T00:00:00.000Z',
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
email: 'deleted@example.com',
|
||||
displayName: 'Deleted User',
|
||||
roles: ['user'],
|
||||
status: 'deleted',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-03T00:00:00.000Z',
|
||||
updatedAt: '2024-01-03T00:00:00.000Z',
|
||||
},
|
||||
];
|
||||
|
||||
const mockResponse: UserListResponse = {
|
||||
users: mockUsers,
|
||||
total: 3,
|
||||
page: 1,
|
||||
limit: 50,
|
||||
totalPages: 1,
|
||||
};
|
||||
|
||||
mockApiClient.listUsers.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.listUsers();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const response = result.unwrap();
|
||||
|
||||
// Verify different statuses are handled correctly
|
||||
expect(response.users[0].status).toBe('active');
|
||||
expect(response.users[1].status).toBe('suspended');
|
||||
expect(response.users[2].status).toBe('deleted');
|
||||
});
|
||||
|
||||
it('should handle different pagination scenarios', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle different pagination scenarios', async () => {
|
||||
const mockUsers: UserDto[] = [
|
||||
{
|
||||
id: '1',
|
||||
email: 'user1@example.com',
|
||||
displayName: 'User 1',
|
||||
roles: ['user'],
|
||||
status: 'active',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
];
|
||||
|
||||
const mockResponse: UserListResponse = {
|
||||
users: mockUsers,
|
||||
total: 100,
|
||||
page: 2,
|
||||
limit: 50,
|
||||
totalPages: 2,
|
||||
};
|
||||
|
||||
mockApiClient.listUsers.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.listUsers();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const response = result.unwrap();
|
||||
|
||||
// Verify pagination metadata
|
||||
expect(response.page).toBe(2);
|
||||
expect(response.limit).toBe(50);
|
||||
expect(response.totalPages).toBe(2);
|
||||
expect(response.total).toBe(100);
|
||||
});
|
||||
|
||||
it('should handle different filtering options', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle different filtering options', async () => {
|
||||
const mockUsers: UserDto[] = [
|
||||
{
|
||||
id: '1',
|
||||
email: 'admin@example.com',
|
||||
displayName: 'Admin User',
|
||||
roles: ['admin'],
|
||||
status: 'active',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
];
|
||||
|
||||
const mockResponse: UserListResponse = {
|
||||
users: mockUsers,
|
||||
total: 1,
|
||||
page: 1,
|
||||
limit: 50,
|
||||
totalPages: 1,
|
||||
};
|
||||
|
||||
mockApiClient.listUsers.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.listUsers();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const response = result.unwrap();
|
||||
|
||||
// Verify filtered results
|
||||
expect(response.users).toHaveLength(1);
|
||||
expect(response.users[0].roles).toContain('admin');
|
||||
expect(response.users[0].status).toBe('active');
|
||||
});
|
||||
|
||||
it('should handle system admin vs regular admin', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle system admin vs regular admin', async () => {
|
||||
const mockUsers: UserDto[] = [
|
||||
{
|
||||
id: '1',
|
||||
email: 'system@example.com',
|
||||
displayName: 'System Admin',
|
||||
roles: ['owner', 'admin'],
|
||||
status: 'active',
|
||||
isSystemAdmin: true,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
email: 'regular@example.com',
|
||||
displayName: 'Regular Admin',
|
||||
roles: ['admin'],
|
||||
status: 'active',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-02T00:00:00.000Z',
|
||||
updatedAt: '2024-01-02T00:00:00.000Z',
|
||||
},
|
||||
];
|
||||
|
||||
const mockResponse: UserListResponse = {
|
||||
users: mockUsers,
|
||||
total: 2,
|
||||
page: 1,
|
||||
limit: 50,
|
||||
totalPages: 1,
|
||||
};
|
||||
|
||||
mockApiClient.listUsers.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.listUsers();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const response = result.unwrap();
|
||||
|
||||
// Verify system admin vs regular admin
|
||||
expect(response.users[0].isSystemAdmin).toBe(true);
|
||||
expect(response.users[0].roles).toContain('owner');
|
||||
expect(response.users[1].isSystemAdmin).toBe(false);
|
||||
expect(response.users[1].roles).not.toContain('owner');
|
||||
});
|
||||
|
||||
it('should handle soft delete vs hard delete', () => {
|
||||
// TODO: Implement test
|
||||
it('should handle soft delete vs hard delete', async () => {
|
||||
// Test soft delete (status change to 'deleted')
|
||||
const mockUpdatedUser: UserDto = {
|
||||
id: 'user-123',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
roles: ['user'],
|
||||
status: 'deleted',
|
||||
isSystemAdmin: false,
|
||||
createdAt: '2024-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
};
|
||||
|
||||
mockApiClient.updateUserStatus.mockResolvedValue(mockUpdatedUser);
|
||||
|
||||
const result = await service.updateUserStatus('user-123', 'deleted');
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const user = result.unwrap();
|
||||
expect(user.status).toBe('deleted');
|
||||
expect(user.id).toBe('user-123');
|
||||
|
||||
// Test hard delete (actual deletion)
|
||||
mockApiClient.deleteUser.mockResolvedValue(undefined);
|
||||
|
||||
const deleteResult = await service.deleteUser();
|
||||
|
||||
expect(deleteResult.isOk()).toBe(true);
|
||||
expect(deleteResult.unwrap()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user