Files
crawler-plugin/admin-frontend-vue/tests/task-25.test.ts
T
huangzd1997 7c8374424d task-25(会话/菜单/权限): 锁定 Cookie/Session 请求配置契约
Cookie 规则回归契约:withCredentials 字面量开启、同源根路径、有界超时、
前端不写 localStorage/sessionStorage/document.cookie 持久化凭证、不注入
Authorization/Bearer;current-user/menus/logout 三类会话调用统一复用共享
http 实例;登出走 X-Requested-With AJAX 标识;401 统一拦截并携带目标路径。
8 用例全过,171 单测 + vue-tsc/vite build 通过。
2026-09-05 14:43:56 +08:00

75 lines
4.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import test from 'node:test'
import assert from 'node:assert/strict'
import { readSource } from './helpers.ts'
import { loginRedirectTarget } from '../src/api/envelope.ts'
const httpSrc = readSource('src/api/http.ts')
const sessionSrc = readSource('src/api/session.ts')
test('test_task_025_cookie_session_config_normal_primary_path', () => {
// 正常主路径:共享传输开启同源 Cookie/SessionAxios withCredentials 必须开启)。
assert.match(httpSrc, /withCredentials:\s*true/, 'Cookie 规则:Axios withCredentials 必须开启')
assert.match(httpSrc, /baseURL:\s*'\/'/, '请求使用同源 Cookie/Session')
})
test('test_task_025_cookie_session_config_normal_variant_input', () => {
// 正常变体:current-user / menus / logout 三类会话调用全部复用同一共享 http 实例。
assert.match(sessionSrc, /import \{ http \} from '\.\/http'/)
const getCalls = (sessionSrc.match(/\bhttp\.get\b/g) || []).length
assert.equal(getCalls, 2, 'current-user 与 menus 两个 GET 均走共享 http')
assert.equal((sessionSrc.match(/\bhttp\.post\b/g) || []).length, 1, 'logout 走共享 http')
assert.match(sessionSrc, /current-user/)
assert.match(sessionSrc, /current-user\/menus/)
})
test('test_task_025_cookie_session_config_normal_repeated_operation_is_idempotent', () => {
// 正常重复:传输模块只创建一次 axios 实例,会话模块不另建客户端、不污染状态。
assert.equal((httpSrc.match(/axios\.create/g) || []).length, 1, '共享实例只创建一次')
assert.equal((sessionSrc.match(/axios\.create/g) || []).length, 0, '会话模块不自行创建新客户端')
assert.equal((sessionSrc.match(/import \{ http \}/g) || []).length, 1)
})
test('test_task_025_cookie_session_config_boundary_empty_input', () => {
// 边界空值:无登录态时请求层不把凭证写入本地/会话存储(前端不保存 JWT/密码)。
for (const rel of ['src/api/http.ts', 'src/api/session.ts']) {
const src = readSource(rel)
assert.equal((src.match(/localStorage/g) || []).length, 0, `${rel} 不得使用 localStorage`)
assert.equal((src.match(/sessionStorage/g) || []).length, 0, `${rel} 不得使用 sessionStorage`)
assert.equal(/document\.cookie\s*=/.test(src), false, `${rel} 不得直接写 document.cookie`)
}
})
test('test_task_025_cookie_session_config_boundary_single_item', () => {
// 边界单元素:登出是单次 POST,以 AJAX 标识发到共享实例,后端据此返回 JSON 而非页面。
assert.match(sessionSrc, /X-Requested-With/, '登出走 AJAX 标识以便后端识别为 JSON 请求')
assert.match(sessionSrc, /http\.post/)
assert.match(sessionSrc, /\/api\/admin\/logout/)
})
test('test_task_025_cookie_session_config_boundary_limit_or_missing_field', () => {
// 边界上限/缺字段:即便会话在身,传输层也不注入 token/Authorization 头——
// 凭证只由同源 Cookie 携带,前端禁止保存并在后续请求回填。
for (const rel of ['src/api/http.ts', 'src/api/session.ts', 'src/api/session-model.ts']) {
const src = readSource(rel)
assert.equal(/\bAuthorization\b/.test(src), false, `${rel} 不应注入 Authorization 头`)
assert.equal(/\bBearer\b/.test(src), false, `${rel} 不应出现 Bearer token`)
}
})
test('test_task_025_cookie_session_config_invalid_input_rejected', () => {
// 异常输入:Cookie 规则要求 withCredentials 必须开启、同源根路径、有界超时。
assert.match(httpSrc, /withCredentials:\s*true/, '传输配置必须字面量开启 withCredentials')
assert.match(httpSrc, /baseURL:\s*'\/'/, '传输配置必须使用同源根路径')
assert.match(httpSrc, /timeout:\s*\d+/, '传输配置必须声明数值型有界超时')
})
test('test_task_025_cookie_session_config_dependency_failure_returns_actionable_message', () => {
// 依赖失败:会话失效/网络异常走同一拦截与解包兜底,且登录跳转携带当前目标路径。
assert.match(httpSrc, /interceptors\.response/, '存在 401/异常统一响应拦截')
assert.match(httpSrc, /unwrap/)
assert.match(httpSrc, /isUnauthorized/)
assert.match(httpSrc, /login\?redirect=/)
const target = loginRedirectTarget({ pathname: '/admin-vue/account/users', search: '?x=1' })
assert.equal(target, encodeURIComponent('/admin-vue/account/users?x=1'), 'redirect 需携带当前目标路径')
})