304 lines
9.1 KiB
TypeScript
304 lines
9.1 KiB
TypeScript
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' }),
|
|
)
|
|
})
|
|
})
|