import assert from "node:assert/strict"; const api = process.env.AI_DRAMA_API_BASE || "http://127.0.0.1:8787"; async function request(path, headers, options = {}) { const response = await fetch(`${api}${path}`, { ...options, headers: { "content-type": "application/json", ...(headers || {}), ...(options.headers || {}) } }); const contentType = response.headers.get("content-type") || ""; const payload = contentType.includes("json") ? await response.json().catch(() => ({})) : await response.text(); return { response, payload }; } async function login(email) { const result = await request("/api/auth/login", null, { method: "POST", body: JSON.stringify({ email, password: "Demo@123456" }) }); assert.equal(result.response.status, 200, `${email} 登录失败`); return { authorization: `Bearer ${result.payload.session.token}` }; } const systemAdmin = await login("producer@local.test"); const organizationAdmin = await login("producer2@local.test"); const ordinaryUser = await login("writer@local.test"); const globalList = await request("/api/audit?page=1&pageSize=2&result=ok", systemAdmin); assert.equal(globalList.response.status, 200, "系统管理员读取审计列表失败"); assert.equal(globalList.payload.scope.mode, "global", "系统管理员应获得全局审计范围"); assert.equal(globalList.payload.auditLog.length, 2, "服务端分页未按 pageSize 返回"); assert.ok(globalList.payload.pagination.total > 2 && globalList.payload.pagination.hasMore, "审计分页元数据不完整"); const auditId = globalList.payload.auditLog[0]?.id; assert.ok(auditId, "审计列表缺少事件 ID"); const detail = await request(`/api/audit/${encodeURIComponent(auditId)}`, systemAdmin); assert.equal(detail.response.status, 200, "系统管理员读取审计详情失败"); assert.equal(detail.payload.event.id, auditId, "审计详情 ID 不一致"); assert.ok(Array.isArray(detail.payload.relatedAudit), "审计详情缺少关联审计列表"); assert.ok(Array.isArray(detail.payload.relatedSecurityEvents), "审计详情缺少安全事件列表"); const exported = await request("/api/audit/export?query=project&pageSize=5", systemAdmin); assert.equal(exported.response.status, 200, "审计 JSON 导出失败"); assert.ok(Array.isArray(exported.payload.auditLog), "审计 JSON 导出缺少事件数组"); assert.ok(Number.isInteger(exported.payload.total), "审计 JSON 导出缺少总数"); const csv = await request("/api/audit/export?format=csv&pageSize=2", systemAdmin); assert.equal(csv.response.status, 200, "审计 CSV 导出失败"); assert.match(csv.response.headers.get("content-type") || "", /text\/csv/); assert.match(csv.response.headers.get("content-disposition") || "", /audit-/); assert.match(csv.payload, /id,|"id"/); const orgList = await request("/api/audit?page=1&pageSize=5", organizationAdmin); assert.equal(orgList.response.status, 200, "组织管理员读取审计列表失败"); assert.equal(orgList.payload.scope.mode, "organization", "组织管理员应限制在组织范围"); assert.equal(orgList.payload.scope.organizationId, "org-northstar", "组织管理员组织范围错误"); const crossOrganization = await request("/api/audit?organizationId=org-studio-lab", organizationAdmin); assert.equal(crossOrganization.response.status, 403, "组织管理员不应读取其他组织审计"); const denied = await request("/api/audit", ordinaryUser); assert.equal(denied.response.status, 403, "普通用户不应读取审计中心"); console.log(`audit smoke passed: ${api}`);