import { beforeEach, describe, expect, it, vi } from 'vitest' import { assignAdminRolePermissions, assignAdminUserHotels, assignAdminUserRoles, createAdminHotel, createAdminMenu, createAdminRole, createAdminUser, fetchAdminAudits, fetchAdminHotels, fetchAdminMenuTree, fetchAdminMenus, fetchAdminPermissions, fetchAdminRoleDetail, fetchAdminRoles, fetchAdminUserDetail, fetchAdminUsers, resetAdminUserPassword, updateAdminHotel, updateAdminHotelStatus, updateAdminMenu, updateAdminMenuTreeOrder, updateAdminRole, updateAdminUser, } from '@/services/systemAdminService' const jsonHeaders = { headers: { get: (name: string) => (name.toLowerCase() === 'content-type' ? 'application/json' : null), }, } function mockJsonResponse(payload: unknown): Response { return { ok: true, status: 200, json: async () => payload, text: async () => JSON.stringify(payload), ...jsonHeaders, } as Response } describe('systemAdminService', () => { beforeEach(() => { vi.restoreAllMocks() sessionStorage.clear() }) it('fetches admin users with filters', async () => { const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue( mockJsonResponse({ items: [], page: { page_num: 2, page_size: 10, total: 0, }, }), ) await fetchAdminUsers({ keyword: 'admin', user_status: 'ACTIVE', page_num: 2, page_size: 10, }) expect(fetchMock).toHaveBeenCalledWith( '/api/admin/users?keyword=admin&user_status=ACTIVE&page_num=2&page_size=10', expect.objectContaining({ method: 'GET' }), ) }) it('fetches role, menu, hotel lists and details from admin endpoints', async () => { const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(mockJsonResponse({ items: [], page: {} })) await fetchAdminRoles({ role_status: 'DISABLED' }) await fetchAdminMenus({ menu_status: 'ACTIVE' }) await fetchAdminHotels({ hotel_status: 'ACTIVE' }) await fetchAdminUserDetail('10001') await fetchAdminRoleDetail('20001') expect(fetchMock).toHaveBeenNthCalledWith( 1, '/api/admin/roles?role_status=DISABLED', expect.objectContaining({ method: 'GET' }), ) expect(fetchMock).toHaveBeenNthCalledWith( 2, '/api/admin/menus?menu_status=ACTIVE', expect.objectContaining({ method: 'GET' }), ) expect(fetchMock).toHaveBeenNthCalledWith( 3, '/api/admin/hotels?hotel_status=ACTIVE', expect.objectContaining({ method: 'GET' }), ) expect(fetchMock).toHaveBeenNthCalledWith( 4, '/api/admin/users/10001', expect.objectContaining({ method: 'GET' }), ) expect(fetchMock).toHaveBeenNthCalledWith( 5, '/api/admin/roles/20001', expect.objectContaining({ method: 'GET' }), ) }) it('fetches and saves the admin menu tree endpoints', async () => { const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue( mockJsonResponse({ items: [ { id: '40001', parent_id: null, menu_code: 'SYSTEM_SETTINGS', menu_name: '系统设置', menu_type: 'GROUP', route_path: '/system', component_key: 'system', icon_key: 'pi pi-cog', permission_code: 'SYSTEM_ADMIN_CONSOLE_ACCESS', sort_order: 900, visible: true, menu_status: 'ACTIVE', known_route: true, created_at: '2026-07-16T00:00:00Z', updated_at: '2026-07-16T00:00:00Z', children: [], }, ], warnings: ['菜单 49999 的 parent_id 不存在,已按根级异常节点返回。'], }), ) const tree = await fetchAdminMenuTree() await updateAdminMenuTreeOrder({ items: [ { menu_id: '40002', parent_id: '40001', sort_order: 100, }, ], }) expect(tree.items[0]?.children).toEqual([]) expect(tree.warnings[0]).toContain('parent_id') expect(fetchMock).toHaveBeenNthCalledWith( 1, '/api/admin/menus/tree', expect.objectContaining({ method: 'GET' }), ) expect(fetchMock).toHaveBeenNthCalledWith( 2, '/api/admin/menus/tree-order', expect.objectContaining({ method: 'PUT', body: JSON.stringify({ items: [ { menu_id: '40002', parent_id: '40001', sort_order: 100, }, ], }), }), ) }) it('fetches the readonly permission list', async () => { const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue( mockJsonResponse([ { id: '1', permission_code: 'SYSTEM_USER_MANAGE', permission_name: '用户管理', permission_group: 'SYSTEM', permission_status: 'ACTIVE', }, ]), ) const result = await fetchAdminPermissions() expect(fetchMock).toHaveBeenCalledWith('/api/admin/permissions', expect.objectContaining({ method: 'GET' })) expect(result[0]?.permission_code).toBe('SYSTEM_USER_MANAGE') }) it('sends admin user write operations to backend endpoints', async () => { const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(mockJsonResponse({ id: '10001' })) await createAdminUser({ username: 'operator', initial_password: 'User@123456', display_name: 'Operator', user_status: 'ACTIVE', role_ids: ['20001'], hotel_ids: ['HOTEL-TEST'], default_hotel_id: 'HOTEL-TEST', }) await updateAdminUser('10001', { display_name: 'Operator A', user_status: 'DISABLED', }) await assignAdminUserRoles('10001', { role_ids: ['20002'], }) await assignAdminUserHotels('10001', { hotel_ids: ['HOTEL-TEST'], default_hotel_id: 'HOTEL-TEST', }) await resetAdminUserPassword('10001') expect(fetchMock).toHaveBeenNthCalledWith( 1, '/api/admin/users', expect.objectContaining({ method: 'POST' }), ) expect(fetchMock).toHaveBeenNthCalledWith( 2, '/api/admin/users/10001', expect.objectContaining({ method: 'PUT' }), ) expect(fetchMock).toHaveBeenNthCalledWith( 3, '/api/admin/users/10001/roles', expect.objectContaining({ method: 'PUT' }), ) expect(fetchMock).toHaveBeenNthCalledWith( 4, '/api/admin/users/10001/hotels', expect.objectContaining({ method: 'PUT' }), ) expect(fetchMock).toHaveBeenNthCalledWith( 5, '/api/admin/users/10001/password-reset', expect.objectContaining({ method: 'POST', body: JSON.stringify({}) }), ) }) it('sends role, menu, hotel writes and audit queries to admin endpoints', async () => { const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(mockJsonResponse({ id: '1', items: [], page: {} })) await createAdminRole({ role_code: 'CUSTOM_ROLE', role_name: 'Custom role', role_status: 'ACTIVE' }) await updateAdminRole('20001', { role_name: 'Custom role A', role_status: 'DISABLED' }) await assignAdminRolePermissions('20001', { permission_ids: ['30001'] }) await createAdminMenu({ menu_code: 'CUSTOM_MENU', menu_name: 'Custom menu', menu_type: 'PAGE', visible: false, menu_status: 'DISABLED', }) await updateAdminMenu('40001', { menu_name: 'Custom menu A', menu_type: 'PAGE', visible: true, menu_status: 'ACTIVE', }) await createAdminHotel({ hotel_id: 'HOTEL-BKK', hotel_name: 'Bangkok Hotel', time_zone: 'Asia/Bangkok', }) await updateAdminHotel('HOTEL-BKK', { hotel_name: 'Bangkok Hotel A', time_zone: 'Asia/Bangkok', }) await updateAdminHotelStatus('HOTEL-BKK', { hotel_status: 'ACTIVE' }) await fetchAdminAudits({ target_type: 'PLATFORM_USER', page_num: 1, page_size: 20 }) expect(fetchMock).toHaveBeenNthCalledWith(1, '/api/admin/roles', expect.objectContaining({ method: 'POST' })) expect(fetchMock).toHaveBeenNthCalledWith(2, '/api/admin/roles/20001', expect.objectContaining({ method: 'PUT' })) expect(fetchMock).toHaveBeenNthCalledWith( 3, '/api/admin/roles/20001/permissions', expect.objectContaining({ method: 'PUT' }), ) expect(fetchMock).toHaveBeenNthCalledWith(4, '/api/admin/menus', expect.objectContaining({ method: 'POST' })) expect(fetchMock).toHaveBeenNthCalledWith(5, '/api/admin/menus/40001', expect.objectContaining({ method: 'PUT' })) expect(fetchMock).toHaveBeenNthCalledWith(6, '/api/admin/hotels', expect.objectContaining({ method: 'POST' })) expect(fetchMock).toHaveBeenNthCalledWith( 7, '/api/admin/hotels/HOTEL-BKK', expect.objectContaining({ method: 'PUT' }), ) expect(fetchMock).toHaveBeenNthCalledWith( 8, '/api/admin/hotels/HOTEL-BKK/status', expect.objectContaining({ method: 'PUT' }), ) expect(fetchMock).toHaveBeenNthCalledWith( 9, '/api/admin/audits?target_type=PLATFORM_USER&page_num=1&page_size=20', expect.objectContaining({ method: 'GET' }), ) }) })