Files
xinghuoapi/frontend/src/router/__tests__/guards.spec.ts
2026-02-10 00:37:56 +08:00

268 lines
7.3 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { setActivePinia, createPinia } from 'pinia'
// Mock 导航加载状态
vi.mock('@/composables/useNavigationLoading', () => {
const mockStart = vi.fn()
const mockEnd = vi.fn()
return {
useNavigationLoadingState: () => ({
startNavigation: mockStart,
endNavigation: mockEnd,
isLoading: { value: false },
}),
useNavigationLoading: () => ({
startNavigation: mockStart,
endNavigation: mockEnd,
isLoading: { value: false },
}),
}
})
// Mock 路由预加载
vi.mock('@/composables/useRoutePrefetch', () => ({
useRoutePrefetch: () => ({
triggerPrefetch: vi.fn(),
cancelPendingPrefetch: vi.fn(),
resetPrefetchState: vi.fn(),
}),
}))
// Mock API 相关模块
vi.mock('@/api', () => ({
authAPI: {
getCurrentUser: vi.fn().mockResolvedValue({ data: {} }),
logout: vi.fn(),
},
isTotp2FARequired: () => false,
}))
vi.mock('@/api/admin/system', () => ({
checkUpdates: vi.fn(),
}))
vi.mock('@/api/auth', () => ({
getPublicSettings: vi.fn(),
}))
// 用于测试的 auth 状态
interface MockAuthState {
isAuthenticated: boolean
isAdmin: boolean
isSimpleMode: boolean
}
/**
* 将 router/index.ts 中 beforeEach 守卫的核心逻辑提取为可测试的函数
*/
function simulateGuard(
toPath: string,
toMeta: Record<string, any>,
authState: MockAuthState
): string | null {
const requiresAuth = toMeta.requiresAuth !== false
const requiresAdmin = toMeta.requiresAdmin === true
// 不需要认证的路由
if (!requiresAuth) {
if (
authState.isAuthenticated &&
(toPath === '/login' || toPath === '/register')
) {
return authState.isAdmin ? '/admin/dashboard' : '/dashboard'
}
return null // 允许通过
}
// 需要认证但未登录
if (!authState.isAuthenticated) {
return '/login'
}
// 需要管理员但不是管理员
if (requiresAdmin && !authState.isAdmin) {
return '/dashboard'
}
// 简易模式限制
if (authState.isSimpleMode) {
const restrictedPaths = [
'/admin/groups',
'/admin/subscriptions',
'/admin/redeem',
'/subscriptions',
'/redeem',
]
if (restrictedPaths.some((path) => toPath.startsWith(path))) {
return authState.isAdmin ? '/admin/dashboard' : '/dashboard'
}
}
return null // 允许通过
}
describe('路由守卫逻辑', () => {
beforeEach(() => {
setActivePinia(createPinia())
})
// --- 未认证用户 ---
describe('未认证用户', () => {
const authState: MockAuthState = {
isAuthenticated: false,
isAdmin: false,
isSimpleMode: false,
}
it('访问需要认证的页面重定向到 /login', () => {
const redirect = simulateGuard('/dashboard', {}, authState)
expect(redirect).toBe('/login')
})
it('访问管理页面重定向到 /login', () => {
const redirect = simulateGuard('/admin/dashboard', { requiresAdmin: true }, authState)
expect(redirect).toBe('/login')
})
it('访问公开页面允许通过', () => {
const redirect = simulateGuard('/login', { requiresAuth: false }, authState)
expect(redirect).toBeNull()
})
it('访问 /home 公开页面允许通过', () => {
const redirect = simulateGuard('/home', { requiresAuth: false }, authState)
expect(redirect).toBeNull()
})
})
// --- 已认证普通用户 ---
describe('已认证普通用户', () => {
const authState: MockAuthState = {
isAuthenticated: true,
isAdmin: false,
isSimpleMode: false,
}
it('访问 /login 重定向到 /dashboard', () => {
const redirect = simulateGuard('/login', { requiresAuth: false }, authState)
expect(redirect).toBe('/dashboard')
})
it('访问 /register 重定向到 /dashboard', () => {
const redirect = simulateGuard('/register', { requiresAuth: false }, authState)
expect(redirect).toBe('/dashboard')
})
it('访问 /dashboard 允许通过', () => {
const redirect = simulateGuard('/dashboard', {}, authState)
expect(redirect).toBeNull()
})
it('访问管理页面被拒绝,重定向到 /dashboard', () => {
const redirect = simulateGuard('/admin/dashboard', { requiresAdmin: true }, authState)
expect(redirect).toBe('/dashboard')
})
it('访问 /admin/users 被拒绝', () => {
const redirect = simulateGuard('/admin/users', { requiresAdmin: true }, authState)
expect(redirect).toBe('/dashboard')
})
})
// --- 已认证管理员 ---
describe('已认证管理员', () => {
const authState: MockAuthState = {
isAuthenticated: true,
isAdmin: true,
isSimpleMode: false,
}
it('访问 /login 重定向到 /admin/dashboard', () => {
const redirect = simulateGuard('/login', { requiresAuth: false }, authState)
expect(redirect).toBe('/admin/dashboard')
})
it('访问管理页面允许通过', () => {
const redirect = simulateGuard('/admin/dashboard', { requiresAdmin: true }, authState)
expect(redirect).toBeNull()
})
it('访问用户页面允许通过', () => {
const redirect = simulateGuard('/dashboard', {}, authState)
expect(redirect).toBeNull()
})
})
// --- 简易模式 ---
describe('简易模式受限路由', () => {
it('普通用户简易模式访问 /subscriptions 重定向到 /dashboard', () => {
const authState: MockAuthState = {
isAuthenticated: true,
isAdmin: false,
isSimpleMode: true,
}
const redirect = simulateGuard('/subscriptions', {}, authState)
expect(redirect).toBe('/dashboard')
})
it('普通用户简易模式访问 /redeem 重定向到 /dashboard', () => {
const authState: MockAuthState = {
isAuthenticated: true,
isAdmin: false,
isSimpleMode: true,
}
const redirect = simulateGuard('/redeem', {}, authState)
expect(redirect).toBe('/dashboard')
})
it('管理员简易模式访问 /admin/groups 重定向到 /admin/dashboard', () => {
const authState: MockAuthState = {
isAuthenticated: true,
isAdmin: true,
isSimpleMode: true,
}
const redirect = simulateGuard('/admin/groups', { requiresAdmin: true }, authState)
expect(redirect).toBe('/admin/dashboard')
})
it('管理员简易模式访问 /admin/subscriptions 重定向', () => {
const authState: MockAuthState = {
isAuthenticated: true,
isAdmin: true,
isSimpleMode: true,
}
const redirect = simulateGuard(
'/admin/subscriptions',
{ requiresAdmin: true },
authState
)
expect(redirect).toBe('/admin/dashboard')
})
it('简易模式下非受限页面正常访问', () => {
const authState: MockAuthState = {
isAuthenticated: true,
isAdmin: false,
isSimpleMode: true,
}
const redirect = simulateGuard('/dashboard', {}, authState)
expect(redirect).toBeNull()
})
it('简易模式下 /keys 正常访问', () => {
const authState: MockAuthState = {
isAuthenticated: true,
isAdmin: false,
isSimpleMode: true,
}
const redirect = simulateGuard('/keys', {}, authState)
expect(redirect).toBeNull()
})
})
})