Files

56 lines
3.3 KiB
JavaScript

import assert from "node:assert/strict";
const api = process.env.AI_DRAMA_API_BASE || "http://127.0.0.1:8787";
const scope = {
"x-organization-id": "org-studio-lab",
"x-workspace-id": "ws-local-aidrama",
"x-project-id": "thunder-mouth"
};
async function request(path, options = {}) {
const response = await fetch(`${api}${path}`, {
...options,
headers: { "content-type": "application/json", ...(options.headers || {}) }
});
const payload = await response.json().catch(() => ({}));
return { response, payload };
}
async function login(email) {
const result = await request("/api/auth/login", {
method: "POST",
body: JSON.stringify({ email, password: "Demo@123456" })
});
assert.equal(result.response.status, 200, `${email} 登录失败`);
return { authorization: `Bearer ${result.payload.session.token}` };
}
const ownerHeaders = { ...(await login("producer@local.test")), ...scope };
const writerHeaders = { ...(await login("writer@local.test")), ...scope };
const unauthenticated = await request("/api/search?q=%E9%9B%B7%E9%9B%A8", { headers: scope });
assert.equal(unauthenticated.response.status, 401, "未登录搜索必须返回 401");
const ownerSearch = await request("/api/search?q=%E9%9B%B7%E9%9B%A8&scope=workspace", { headers: ownerHeaders });
assert.equal(ownerSearch.response.status, 200, "组织所有者搜索失败");
assert.ok(ownerSearch.payload.total >= 3, "搜索结果应覆盖项目、镜头和资产等多个生产对象");
assert.ok(ownerSearch.payload.results.some((item) => item.type === "project" && item.projectId === "thunder-mouth"), "搜索必须返回当前项目");
assert.ok(ownerSearch.payload.results.some((item) => item.type === "shot" && item.targetTab === "director"), "搜索必须返回可跳转的镜头结果");
assert.ok(ownerSearch.payload.results.every((item) => item.organizationId === "org-studio-lab"), "搜索结果必须返回当前组织作用域");
const projectSearch = await request("/api/search?q=%E9%9B%B7%E9%9B%A8&scope=project", { headers: ownerHeaders });
assert.equal(projectSearch.response.status, 200, "当前项目搜索失败");
assert.ok(projectSearch.payload.results.length > 0 && projectSearch.payload.results.every((item) => item.projectId === "thunder-mouth"), "当前项目搜索不能返回其他项目对象");
const crossOrganization = await request("/api/search?q=%E5%B1%B1%E6%B5%B7&scope=workspace", { headers: ownerHeaders });
assert.equal(crossOrganization.response.status, 200, "跨组织关键词搜索不应报错");
assert.equal(crossOrganization.payload.results.length, 0, "当前组织搜索不得泄露北辰组织项目");
const writerSearch = await request("/api/search?q=%E9%9B%B7%E9%9B%A8&scope=workspace", { headers: writerHeaders });
assert.equal(writerSearch.response.status, 200, "普通成员应能搜索自己可访问的生产内容");
assert.ok(writerSearch.payload.results.length > 0, "普通成员应能搜索当前项目内容");
assert.ok(writerSearch.payload.results.every((item) => item.projectId === "thunder-mouth"), "普通成员搜索不能越过项目边界");
assert.ok(writerSearch.payload.results.every((item) => item.organizationId === "org-studio-lab"), "普通成员搜索必须返回当前组织作用域");
console.log(`search smoke passed: owner=${ownerSearch.payload.total}, project=${projectSearch.payload.total}, writer=${writerSearch.payload.total}`);