Files
oci-portal-dash/src/api/request.test.ts
T
2026-07-22 16:51:45 +08:00

67 lines
2.2 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { createPinia, setActivePinia } from 'pinia'
import { request, ApiError } from './request'
import { useAuthStore } from '@/stores/auth'
/** happy-dom 提供的当前页 URL 控制入口(标准 DOM 无此能力) */
function setPageURL(url: string) {
;(window as unknown as { happyDOM: { setURL: (u: string) => void } }).happyDOM.setURL(url)
}
/** 构造最小 401 响应;rotate 在响应返回前换发新会话,模拟 OAuth 绑定回跳 */
function stub401(rotateTo?: string) {
vi.stubGlobal('fetch', async () => {
if (rotateTo !== undefined) useAuthStore().setSession(rotateTo, '')
return {
ok: false,
status: 401,
url: '/api/v1/x',
json: async () => ({ error: '未认证' }),
} as unknown as Response
})
}
let assignSpy: ReturnType<typeof vi.spyOn>
beforeEach(() => {
localStorage.clear()
setActivePinia(createPinia())
assignSpy = vi.spyOn(location, 'assign').mockImplementation(() => {})
})
afterEach(() => {
vi.unstubAllGlobals()
vi.restoreAllMocks()
})
describe('401 令牌快照', () => {
it('当前 token 收到 401:登出并带 redirect 跳登录', async () => {
setPageURL('http://localhost/instances?tab=list')
useAuthStore().setSession('tokA', '')
stub401()
await expect(request('/x')).rejects.toMatchObject({ status: 401, message: '未认证' })
expect(useAuthStore().token).toBe('')
expect(assignSpy).toHaveBeenCalledWith(
`/login?redirect=${encodeURIComponent('/instances?tab=list')}`,
)
})
it('响应到达前会话已换新:旧 token 的迟到 401 不得清掉新会话', async () => {
setPageURL('http://localhost/settings')
useAuthStore().setSession('tokA', '')
stub401('tokB')
await expect(request('/x')).rejects.toBeInstanceOf(ApiError)
expect(useAuthStore().token).toBe('tokB')
expect(assignSpy).not.toHaveBeenCalled()
})
it('登录页密码错误的 401:不跳转,错误留在表单内提示', async () => {
setPageURL('http://localhost/login')
stub401()
await expect(request('/auth/login', { method: 'POST', body: {} })).rejects.toMatchObject({
message: '未认证',
})
expect(assignSpy).not.toHaveBeenCalled()
})
})