79ee16a374
改用 username 过滤查询以可靠命中新建用户,规避大库分页假阴性。 本地 Java 复验 8/8 通过;全组 live 64 通过 0 失败。
149 lines
7.0 KiB
TypeScript
149 lines
7.0 KiB
TypeScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { parseMenuManageList, buildMenuManageTree } from '../../src/pages/account/menu-manage-model.ts'
|
|
import { parseUserPage } from '../../src/api/users-model.ts'
|
|
|
|
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-task60-account-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, init: RequestInit = {}): Promise<Response> {
|
|
const headers: Record<string, string> = { ...(init.headers as Record<string, string>) }
|
|
if (sessionCookie) headers.Cookie = sessionCookie
|
|
else if (bearerToken) headers.Authorization = `Bearer ${bearerToken}`
|
|
if (init.body && !headers['Content-Type']) headers['Content-Type'] = 'application/json'
|
|
return fetch(`${BASE}${path}`, { ...init, headers })
|
|
}
|
|
|
|
test('test_task_060_account_live_reachability_normal_primary_path', async () => {
|
|
// 正常主路径(无需凭据):真实服务在线,登录页可访问(200 + HTML)。
|
|
const res = await fetch(`${BASE}/login`)
|
|
assert.equal(res.status, 200)
|
|
const html = await res.text()
|
|
assert.ok(html.length > 0)
|
|
})
|
|
|
|
test('test_task_060_account_live_reachability_boundary_empty_input', async () => {
|
|
// 边界空值(无需凭据):无会话请求用户列表 → 401 未登录,前端据此跳登录。
|
|
const res = await fetch(`${BASE}/api/admin/users`)
|
|
const body = (await res.json()) as { success?: boolean; code?: number }
|
|
assert.equal(body.success, false)
|
|
assert.equal(body.code, 401)
|
|
})
|
|
|
|
test('test_task_060_account_live_users_list_normal_primary_path', { skip: !HAS_CREDS }, async () => {
|
|
// 正常主路径:真实登录后拉取用户列表并经同一解析器归一(super 可见全量)。
|
|
await ensureSession()
|
|
const res = await authedFetch('/api/admin/users?page=1&page_size=15')
|
|
const page = parseUserPage(await res.json())
|
|
assert.equal(res.status, 200)
|
|
assert.ok(Array.isArray(page.items))
|
|
assert.equal(typeof page.total, 'number')
|
|
if (page.items.length) assert.equal(typeof page.items[0].id, 'number')
|
|
})
|
|
|
|
test('test_task_060_account_live_menus_list_normal_variant_input', { skip: !HAS_CREDS }, async () => {
|
|
// 正常变体:permission-menus(admin) 真实列表含后台页面 key,解析+建树可用。
|
|
await ensureSession()
|
|
const res = await authedFetch('/api/admin/permission-menus?menuType=admin')
|
|
const list = parseMenuManageList(await res.json())
|
|
const keys = new Set(list.map((n) => n.columnKey))
|
|
for (const key of ['admin_users', 'admin_columns', 'admin_group_manage']) {
|
|
assert.ok(keys.has(key), `菜单管理列表应含 key: ${key}`)
|
|
}
|
|
const tree = buildMenuManageTree(list)
|
|
assert.ok(tree.length > 0)
|
|
})
|
|
|
|
test('test_task_060_account_live_groups_list_normal_single_item', { skip: !HAS_CREDS }, async () => {
|
|
// 边界单元素:真实分组列表为数组(super 返回全部),条目带分组名/组员数。
|
|
const res = await authedFetch('/api/admin/shop-manages/groups')
|
|
const body = (await res.json()) as { success?: boolean; data?: Array<{ groupName?: string; memberCount?: number }> }
|
|
assert.equal(res.status, 200)
|
|
assert.equal(body.success, true)
|
|
assert.ok(Array.isArray(body.data))
|
|
if (body.data.length) {
|
|
assert.equal(typeof body.data[0].groupName, 'string')
|
|
assert.equal(typeof body.data[0].memberCount, 'number')
|
|
}
|
|
})
|
|
|
|
test('test_task_060_account_live_user_cycle_normal_primary_path', { skip: !HAS_CREDS }, async () => {
|
|
// 正常主路径(写):真实创建普通用户 → 列表含新 id → 删除清理(结果来自真实业务非硬编码)。
|
|
await ensureSession()
|
|
const username = `acc_live_${Date.now()}`
|
|
let createdId: number | null = null
|
|
try {
|
|
const create = await authedFetch('/api/admin/user', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ username, password: 'secret1', role: 'normal', columnIds: [] }),
|
|
})
|
|
const created = (await create.json()) as { success?: boolean; message?: string; data?: number }
|
|
assert.equal(create.status, 200)
|
|
assert.equal(created.success, true, created.message || '')
|
|
assert.equal(typeof created.data, 'number')
|
|
createdId = created.data as number
|
|
const list = parseUserPage(await (await authedFetch(`/api/admin/users?username=${encodeURIComponent(username)}&page=1&page_size=50`)).json())
|
|
const found = list.items.find((u) => u.id === createdId)
|
|
assert.ok(found, '创建的用户应出现在列表')
|
|
assert.equal(found?.username, username)
|
|
} finally {
|
|
if (createdId !== null) {
|
|
await authedFetch(`/api/admin/user/${createdId}`, { method: 'DELETE' })
|
|
}
|
|
}
|
|
})
|
|
|
|
test('test_task_060_account_live_user_columns_boundary_limit_or_missing_field', { skip: !HAS_CREDS }, async () => {
|
|
// 边界上限/缺字段:真实授权读取返回 columnIds 数组(刚建普通用户默认空授权)。
|
|
await ensureSession()
|
|
const username = `acc_cols_${Date.now()}`
|
|
let createdId: number | null = null
|
|
try {
|
|
const create = await authedFetch('/api/admin/user', {
|
|
method: 'POST',
|
|
body: JSON.stringify({ username, password: 'secret1', role: 'normal', columnIds: [] }),
|
|
})
|
|
const created = (await create.json()) as { data?: number }
|
|
createdId = created.data as number
|
|
const res = await authedFetch(`/api/admin/permission-users/${createdId}/columns?menuType=admin`)
|
|
const body = (await res.json()) as { success?: boolean; data?: { columnIds?: number[] } }
|
|
assert.equal(body.success, true)
|
|
assert.ok(Array.isArray(body.data?.columnIds))
|
|
} finally {
|
|
if (createdId !== null) await authedFetch(`/api/admin/user/${createdId}`, { method: 'DELETE' })
|
|
}
|
|
})
|
|
|
|
test('test_task_060_account_live_invalid_input_rejected', async () => {
|
|
// 异常输入(无需凭据):伪造 Bearer token 被拒,返回 401 而非业务数据。
|
|
const res = await fetch(`${BASE}/api/admin/users`, {
|
|
headers: { Authorization: 'Bearer not-a-real-token' },
|
|
})
|
|
const body = (await res.json()) as { success?: boolean; code?: number }
|
|
assert.equal(body.success, false)
|
|
assert.equal(body.code, 401)
|
|
})
|