126 lines
3.5 KiB
TypeScript
126 lines
3.5 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
import { fetchCurrentAuth, loginAuth, logoutAuth } from '@/services/authService'
|
|
|
|
const jsonHeaders = {
|
|
headers: {
|
|
get: (name: string) => (name.toLowerCase() === 'content-type' ? 'application/json' : null),
|
|
},
|
|
}
|
|
|
|
function mockJsonResponse(payload: unknown, status = 200): Response {
|
|
return {
|
|
ok: status >= 200 && status < 300,
|
|
status,
|
|
json: async () => payload,
|
|
text: async () => JSON.stringify(payload),
|
|
...jsonHeaders,
|
|
} as Response
|
|
}
|
|
|
|
function createAuthPayload() {
|
|
return {
|
|
access_token: 'session-token',
|
|
token_type: 'Bearer',
|
|
expires_at: '2026-07-09T12:00:00Z',
|
|
user: {
|
|
id: '1900000000000000001',
|
|
username: 'admin',
|
|
display_name: '系统管理员',
|
|
super_admin: true,
|
|
},
|
|
default_hotel_id: 'HOTEL-TEST',
|
|
hotels: [
|
|
{
|
|
hotel_id: 'HOTEL-TEST',
|
|
hotel_name: '测试酒店',
|
|
time_zone: 'Asia/Bangkok',
|
|
default_hotel: true,
|
|
},
|
|
],
|
|
permissions: ['RESERVATION_ORDER_READ'],
|
|
menus: [
|
|
{
|
|
menu_code: 'RESERVATION_ORDERS',
|
|
menu_name: '订单列表',
|
|
route_path: '/reservation/orders',
|
|
component_key: 'ReservationOrders',
|
|
icon_key: 'pi pi-list',
|
|
permission_code: 'RESERVATION_ORDER_READ',
|
|
sort_order: 10,
|
|
},
|
|
],
|
|
}
|
|
}
|
|
|
|
describe('authService', () => {
|
|
beforeEach(() => {
|
|
vi.restoreAllMocks()
|
|
sessionStorage.clear()
|
|
})
|
|
|
|
it('logs in with username, password, and optional preferred hotel id', async () => {
|
|
const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(mockJsonResponse(createAuthPayload()))
|
|
|
|
const result = await loginAuth({
|
|
username: 'admin',
|
|
password: 'Admin@123456',
|
|
preferred_hotel_id: 'HOTEL-TEST',
|
|
})
|
|
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
'/api/auth/login',
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: expect.objectContaining({
|
|
'Content-Type': 'application/json',
|
|
}),
|
|
body: JSON.stringify({
|
|
username: 'admin',
|
|
password: 'Admin@123456',
|
|
preferred_hotel_id: 'HOTEL-TEST',
|
|
}),
|
|
}),
|
|
)
|
|
expect(result.access_token).toBe('session-token')
|
|
expect(result.default_hotel_id).toBe('HOTEL-TEST')
|
|
})
|
|
|
|
it('restores the current user through auth me', async () => {
|
|
const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(mockJsonResponse(createAuthPayload()))
|
|
sessionStorage.setItem('th_hotel_access_token', 'session-token')
|
|
|
|
const result = await fetchCurrentAuth()
|
|
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
'/api/auth/me',
|
|
expect.objectContaining({
|
|
method: 'GET',
|
|
headers: expect.objectContaining({
|
|
Authorization: 'Bearer session-token',
|
|
}),
|
|
}),
|
|
)
|
|
expect(result.user.username).toBe('admin')
|
|
})
|
|
|
|
it('logs out the current token without persisting secrets outside sessionStorage', async () => {
|
|
const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue(mockJsonResponse({ success: true }))
|
|
sessionStorage.setItem('th_hotel_access_token', 'session-token')
|
|
|
|
const result = await logoutAuth()
|
|
|
|
expect(fetchMock).toHaveBeenCalledWith(
|
|
'/api/auth/logout',
|
|
expect.objectContaining({
|
|
method: 'POST',
|
|
headers: expect.objectContaining({
|
|
Authorization: 'Bearer session-token',
|
|
}),
|
|
}),
|
|
)
|
|
expect(result.success).toBe(true)
|
|
expect(localStorage.getItem('th_hotel_access_token')).toBeNull()
|
|
})
|
|
})
|