import { beforeEach, describe, expect, it, vi } from 'vitest' const getMock = vi.fn() const postMock = vi.fn() const putMock = vi.fn() const delMock = vi.fn() vi.mock('@/lib/http/client', () => ({ get: getMock, post: postMock, put: putMock, del: delMock, })) describe('stats service', () => { beforeEach(() => { getMock.mockReset() }) it('gets dashboard stats', async () => { const mockData = { users: { total_users: 100, active_users: 80, inactive_users: 10, locked_users: 5, disabled_users: 5, new_users_today: 3, new_users_week: 15, new_users_month: 50, }, logins: { logins_today_success: 50, logins_today_failed: 2, logins_week: 300, }, } getMock.mockResolvedValue(mockData) const { getDashboardStats } = await import('./stats') const result = await getDashboardStats() expect(getMock).toHaveBeenCalledWith('/admin/stats/dashboard') expect(result).toEqual(mockData) expect(result.users.total_users).toBe(100) expect(result.users.active_users).toBe(80) }) it('gets user stats', async () => { const mockData = { total: 100, by_status: { active: 80, inactive: 10, locked: 5, disabled: 5, }, today_new: 3, week_new: 15, month_new: 50, } getMock.mockResolvedValue(mockData) const { getUserStats } = await import('./stats') const result = await getUserStats() expect(getMock).toHaveBeenCalledWith('/admin/stats/users') expect(result.total).toBe(100) expect(result.by_status.active).toBe(80) }) }) describe('permissions service', () => { beforeEach(() => { getMock.mockReset() postMock.mockReset() putMock.mockReset() delMock.mockReset() }) it('gets permission tree', async () => { const mockPermissions = [ { id: 1, name: 'Users', code: 'users', children: [{ id: 2, name: 'View', code: 'users:view' }] }, ] getMock.mockResolvedValue(mockPermissions) const { getPermissionTree } = await import('./permissions') const result = await getPermissionTree() expect(getMock).toHaveBeenCalledWith('/permissions/tree') expect(result).toEqual(mockPermissions) expect(result[0].children?.[0]?.name).toBe('View') }) it('lists all permissions', async () => { const mockPermissions = [ { id: 1, name: 'Users', code: 'users' }, { id: 2, name: 'Roles', code: 'roles' }, ] getMock.mockResolvedValue(mockPermissions) const { listPermissions } = await import('./permissions') const result = await listPermissions() expect(getMock).toHaveBeenCalledWith('/permissions') expect(result).toHaveLength(2) }) it('gets permission by id', async () => { const mockPermission = { id: 1, name: 'Users', code: 'users' } getMock.mockResolvedValue(mockPermission) const { getPermission } = await import('./permissions') const result = await getPermission(1) expect(getMock).toHaveBeenCalledWith('/permissions/1') expect(result.id).toBe(1) }) it('creates a permission', async () => { const newPermission = { name: 'Test', code: 'test', type: 'button' as const } const createdPermission = { id: 10, ...newPermission } postMock.mockResolvedValue(createdPermission) const { createPermission } = await import('./permissions') const result = await createPermission(newPermission) expect(postMock).toHaveBeenCalledWith('/permissions', newPermission) expect(result.id).toBe(10) }) it('updates a permission', async () => { const update = { name: 'Updated', code: 'updated' } const updatedPermission = { id: 1, ...update } putMock.mockResolvedValue(updatedPermission) const { updatePermission } = await import('./permissions') const result = await updatePermission(1, update) expect(putMock).toHaveBeenCalledWith('/permissions/1', update) expect(result.name).toBe('Updated') }) it('deletes a permission', async () => { delMock.mockResolvedValue(undefined) const { deletePermission } = await import('./permissions') await deletePermission(1) expect(delMock).toHaveBeenCalledWith('/permissions/1') }) it('updates permission status', async () => { putMock.mockResolvedValue(undefined) const { updatePermissionStatus } = await import('./permissions') await updatePermissionStatus(1, 1) expect(putMock).toHaveBeenCalledWith('/permissions/1/status', { status: 1 }) }) }) describe('roles service', () => { beforeEach(() => { getMock.mockReset() postMock.mockReset() putMock.mockReset() delMock.mockReset() }) it('lists roles with pagination', async () => { const mockResponse = { items: [{ id: 1, name: 'Admin', code: 'admin' }], total: 1, page: 1, page_size: 20, } getMock.mockResolvedValue(mockResponse) const { listRoles } = await import('./roles') const result = await listRoles({ page: 1, page_size: 20 }) expect(getMock).toHaveBeenCalledWith('/roles', { page: 1, page_size: 20 }) expect(result.items).toHaveLength(1) expect(result.total).toBe(1) }) it('gets role by id', async () => { const mockRole = { id: 1, name: 'Admin', code: 'admin' } getMock.mockResolvedValue(mockRole) const { getRole } = await import('./roles') const result = await getRole(1) expect(getMock).toHaveBeenCalledWith('/roles/1') expect(result.name).toBe('Admin') }) it('creates a role', async () => { const newRole = { name: 'Test', code: 'test' } const createdRole = { id: 10, ...newRole } postMock.mockResolvedValue(createdRole) const { createRole } = await import('./roles') const result = await createRole(newRole) expect(postMock).toHaveBeenCalledWith('/roles', newRole) expect(result.id).toBe(10) }) it('updates a role', async () => { const update = { name: 'Updated' } const updatedRole = { id: 1, ...update } putMock.mockResolvedValue(updatedRole) const { updateRole } = await import('./roles') const result = await updateRole(1, update) expect(putMock).toHaveBeenCalledWith('/roles/1', update) expect(result.name).toBe('Updated') }) it('deletes a role', async () => { delMock.mockResolvedValue(undefined) const { deleteRole } = await import('./roles') await deleteRole(1) expect(delMock).toHaveBeenCalledWith('/roles/1') }) it('updates role status', async () => { putMock.mockResolvedValue(undefined) const { updateRoleStatus } = await import('./roles') await updateRoleStatus(1, 1) expect(putMock).toHaveBeenCalledWith('/roles/1/status', { status: 1 }) }) it('gets role permissions', async () => { const mockPermissions = [{ id: 1 }, { id: 2 }, { id: 3 }] getMock.mockResolvedValue(mockPermissions) const { getRolePermissions } = await import('./roles') const result = await getRolePermissions(1) expect(getMock).toHaveBeenCalledWith('/roles/1/permissions') expect(result).toEqual([1, 2, 3]) }) it('assigns role permissions', async () => { putMock.mockResolvedValue(undefined) const { assignRolePermissions } = await import('./roles') await assignRolePermissions(1, [1, 2, 3]) expect(putMock).toHaveBeenCalledWith('/roles/1/permissions', { permission_ids: [1, 2, 3] }) }) }) describe('profile service', () => { beforeEach(() => { getMock.mockReset() postMock.mockReset() putMock.mockReset() }) it('gets current user profile', async () => { const mockUser = { id: 1, username: 'testuser', email: 'test@example.com' } const mockRoles = [{ id: 1, name: 'Admin', code: 'admin' }] getMock .mockResolvedValueOnce(mockUser) .mockResolvedValueOnce(mockRoles) const { getCurrentProfile } = await import('./profile') const result = await getCurrentProfile(1) expect(result.user.username).toBe('testuser') expect(result.roles).toHaveLength(1) expect(result.roles[0].name).toBe('Admin') }) it('updates profile', async () => { const update = { nickname: 'Updated Name' } const updatedUser = { id: 1, username: 'testuser', nickname: 'Updated Name' } putMock.mockResolvedValue(updatedUser) const { updateProfile } = await import('./profile') const result = await updateProfile(1, update) expect(putMock).toHaveBeenCalledWith('/users/1', update) expect(result.nickname).toBe('Updated Name') }) it('gets TOTP status', async () => { const mockStatus = { totp_enabled: true } getMock.mockResolvedValue(mockStatus) const { getTOTPStatus } = await import('./profile') const result = await getTOTPStatus() expect(getMock).toHaveBeenCalledWith('/auth/2fa/status') expect(result.totp_enabled).toBe(true) }) it('gets TOTP setup', async () => { const mockSetup = { secret: 'JBSWY3DPEHPK3PXP', qr_code_base64: 'base64image...', recovery_codes: ['ABCDE-FGHIJ', 'KLMNO-PQRST'], } getMock.mockResolvedValue(mockSetup) const { getTOTPSetup } = await import('./profile') const result = await getTOTPSetup() expect(getMock).toHaveBeenCalledWith('/auth/2fa/setup') expect(result.secret).toBe('JBSWY3DPEHPK3PXP') expect(result.recovery_codes).toHaveLength(2) }) it('enables TOTP', async () => { postMock.mockResolvedValue(undefined) const { enableTOTP } = await import('./profile') await enableTOTP('123456') expect(postMock).toHaveBeenCalledWith('/auth/2fa/enable', { code: '123456' }) }) it('disables TOTP', async () => { postMock.mockResolvedValue(undefined) const { disableTOTP } = await import('./profile') await disableTOTP('123456') expect(postMock).toHaveBeenCalledWith('/auth/2fa/disable', { code: '123456' }) }) }) describe('operation-logs service', () => { beforeEach(() => { getMock.mockReset() }) it('lists operation logs', async () => { const mockResponse = { list: [ { id: 1, action: 'user.login', user_id: 1, created_at: '2024-01-01T00:00:00Z' }, { id: 2, action: 'user.logout', user_id: 1, created_at: '2024-01-01T01:00:00Z' }, ], total: 2, page: 1, size: 20, } getMock.mockResolvedValue(mockResponse) const { listOperationLogs } = await import('./operation-logs') const result = await listOperationLogs({ page: 1, page_size: 20 }) expect(getMock).toHaveBeenCalledWith('/logs/operation', { page: 1, page_size: 20 }) expect(result.items).toHaveLength(2) expect(result.total).toBe(2) }) it('lists my operation logs', async () => { const mockResponse = { list: [{ id: 1, action: 'user.login', user_id: 1 }], total: 1, page: 1, size: 20, } getMock.mockResolvedValue(mockResponse) const { listMyOperationLogs } = await import('./operation-logs') const result = await listMyOperationLogs({ page: 1, page_size: 20 }) expect(getMock).toHaveBeenCalledWith('/logs/operation/me', { page: 1, page_size: 20 }) expect(result.items).toHaveLength(1) }) it('transforms backend response to frontend format', async () => { const backendResponse = { list: [{ id: 1, action: 'test' }], total: 100, page: 2, size: 10, } getMock.mockResolvedValue(backendResponse) const { listOperationLogs } = await import('./operation-logs') const result = await listOperationLogs({ page: 2, page_size: 10 }) // Verify transformation from backend format to frontend format expect(result.items).toEqual(backendResponse.list) expect(result.total).toBe(100) expect(result.page).toBe(2) expect(result.page_size).toBe(10) }) })