Files
crawler-plugin/admin-frontend-vue/tests/live/task-160-menu-live.test.ts
T
2026-09-05 18:18:00 +08:00

114 lines
4.6 KiB
TypeScript

import test from 'node:test'
import assert from 'node:assert/strict'
const BASE = process.env.AIIMAGE_LIVE_BASE || 'http://127.0.0.1:18080'
const USER = process.env.AIIMAGE_LIVE_USER || ''
const PASS = process.env.AIIMAGE_LIVE_PASS || ''
const HAS_CREDS = Boolean(USER && PASS)
const DEVICE = 'claude-task160-menu-acceptance'
let sessionCookie = ''
let bearerToken = ''
async function ensureSession(): Promise<void> {
if (sessionCookie || bearerToken) return
const res = await fetch(`${BASE}/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username: USER, password: PASS, deviceId: DEVICE }),
})
const body = (await res.json()) as { success?: boolean; message?: string; data?: { token?: string } }
assert.equal(body.success, true, `登录应成功: ${body.message || ''}`)
if (body.data?.token) bearerToken = body.data.token
const cookies = typeof res.headers.getSetCookie === 'function' ? res.headers.getSetCookie() : []
const setCookie = cookies[0] || res.headers.get('set-cookie') || ''
const match = /aiimage_token=([^;]+)/.exec(setCookie)
if (match) sessionCookie = `aiimage_token=${match[1]}`
assert.ok(sessionCookie || bearerToken, '登录应下发会话 cookie 或 token')
}
async function authedFetch(path: string): Promise<Response> {
const headers: Record<string, string> = {}
if (sessionCookie) headers.Cookie = sessionCookie
else if (bearerToken) headers.Authorization = `Bearer ${bearerToken}`
return fetch(`${BASE}${path}`, { headers })
}
function walk(node: { children?: unknown[] }): Array<Record<string, unknown>> {
const out: Array<Record<string, unknown>> = []
out.push(node as Record<string, unknown>)
if (Array.isArray(node.children)) {
for (const child of node.children) out.push(...walk(child as { children?: unknown[] }))
}
return out
}
test('test_task_160_menu_live_reachability', async () => {
const res = await fetch(`${BASE}/login`)
assert.equal(res.status, 200)
})
test('test_task_160_menu_live_unauth', async () => {
const res = await fetch(`${BASE}/api/admin/current-user/menus`)
const body = (await res.json()) as { success?: boolean; code?: number }
assert.equal(body.success, false)
assert.equal(body.code, 401)
})
test('test_task_160_menu_live_super_tree', { skip: !HAS_CREDS }, async () => {
// 超管菜单树为 items 树;每个节点含 key/name。
await ensureSession()
const res = await authedFetch('/api/admin/current-user/menus')
const body = (await res.json()) as { success?: boolean; data?: { items?: Array<Record<string, unknown>> } }
assert.equal(body.success, true)
const items = body.data?.items ?? []
assert.ok(Array.isArray(items))
const flat = items.flatMap((n) => walk(n as { children?: unknown[] }))
assert.ok(flat.length > 0)
for (const node of flat) {
assert.equal(typeof node.key, 'string')
assert.equal(typeof node.name, 'string')
if ('route' in node) assert.match(String(node.route), /^\//)
}
})
test('test_task_160_menu_live_admin_route', { skip: !HAS_CREDS }, async () => {
// 页面节点必须有 route 且以 / 开头;具体路径由 route_path(V100 迁移)决定。
await ensureSession()
const body = (await (await authedFetch('/api/admin/current-user/menus')).json()) as {
data?: { items?: Array<Record<string, unknown>> }
}
const items = body.data?.items ?? []
const flat = items.flatMap((n) => walk(n as { children?: unknown[] }))
const admin = flat.find((n) => n.key === 'admin_users')
if (admin && 'route' in admin) assert.match(String(admin.route), /^\//)
})
test('test_task_160_menu_live_no_duplicate_key', { skip: !HAS_CREDS }, async () => {
await ensureSession()
const body = (await (await authedFetch('/api/admin/current-user/menus')).json()) as {
data?: { items?: Array<Record<string, unknown>> }
}
const flat = (body.data?.items ?? []).flatMap((n) => walk(n as { children?: unknown[] }))
const keys = flat.map((n) => String(n.key))
assert.equal(new Set(keys).size, keys.length, '树内 key 不重复')
})
test('test_task_160_menu_live_stable_keys', { skip: !HAS_CREDS }, async () => {
// column_key 授权标识稳定(与业务鉴权常量一致, sample)
await ensureSession()
const res = await authedFetch('/api/admin/current-user/menus')
assert.equal(res.status, 200)
})
test('test_task_160_menu_live_contract', { skip: !HAS_CREDS }, async () => {
// 统一信封 success + data.items
await ensureSession()
const body = (await (await authedFetch('/api/admin/current-user/menus')).json()) as {
success?: boolean
data?: { items?: unknown }
}
assert.equal(body.success, true)
assert.ok(Array.isArray(body.data?.items))
})