文件
hyb-oa/server/tests/unit/routes/search.test.js
T

154 行
5.1 KiB
JavaScript

const Fastify = require("fastify");
const { AppError } = require("../../../src/utils/AppError");
const searchRoutes = require("../../../src/routes/search");
const NOW = new Date("2026-03-22T10:00:00.000Z");
function buildFeatureFlagRecord(flagKey) {
return {
id: `flag-${flagKey}`,
flag_key: flagKey,
override_mode: "force_on",
rollout_type: "global",
department_ids: [],
note: "unit-test",
version: 1,
created_at: NOW,
created_by: null,
updated_at: NOW,
updated_by: null,
};
}
function createPrisma() {
const contractRow = {
id: "11111111-1111-4111-8111-111111111111",
contract_no: "HT-001",
counterparty_name: "涉密客户",
confidentiality_level: "restricted",
summary: "极密摘要内容",
updated_at: NOW,
project: { project_no: "PRJ-001" },
};
const docRow = {
id: "22222222-2222-4222-8222-222222222222",
title: "极密技术方案",
document_no: "DOC-001",
offline_archive_no: "ARC-001",
confidentiality_level: "confidential",
updated_at: NOW,
project: { project_no: "PRJ-001" },
};
return {
featureFlag: {
findUnique: vi.fn().mockImplementation(async ({ where }) => buildFeatureFlagRecord(where.flag_key)),
},
employee: { findMany: vi.fn().mockResolvedValue([]) },
portalAnnouncement: { findMany: vi.fn().mockResolvedValue([]) },
assetCard: { findMany: vi.fn().mockResolvedValue([]) },
reimbursementForm: { findMany: vi.fn().mockResolvedValue([]) },
leaveRequest: { findMany: vi.fn().mockResolvedValue([]) },
weeklyReport: { findMany: vi.fn().mockResolvedValue([]) },
todoItem: { findMany: vi.fn().mockResolvedValue([]) },
purchaseRequest: { findMany: vi.fn().mockResolvedValue([]) },
travelRequest: { findMany: vi.fn().mockResolvedValue([]) },
project: { findMany: vi.fn().mockResolvedValue([]) },
contractRegistry: {
findMany: vi.fn().mockImplementation(async ({ where }) => {
const query = where?.OR?.[0]?.contract_no?.contains ?? "";
return ["HT-001", "PRJ-001"].includes(query) ? [contractRow] : [];
}),
},
confidentialDocumentRef: {
findMany: vi.fn().mockImplementation(async ({ where }) => {
const query = where?.OR?.[0]?.document_no?.contains ?? "";
return ["DOC-001", "PRJ-001", "ARC-001"].includes(query) ? [docRow] : [];
}),
},
};
}
async function buildApp({ employee, prisma }) {
const app = Fastify();
app.decorate("prisma", prisma);
app.decorate("config", {
globalSearchEnabled: true,
announcementsEnabled: false,
weeklyReportEnabled: false,
todoBoardEnabled: false,
projectDomainEnabled: false,
projectLinkingEnabled: true,
confidentialDocRefsEnabled: true,
});
app.decorate("authenticate", async (request) => {
if (!request.headers.authorization) {
throw new AppError({ code: "AUTH_001", message: "未登录", statusCode: 401 });
}
request.employee = employee;
});
app.decorate("hasPermission", (actor, permissionCode) => {
return (actor?.permissionCodes ?? []).includes("*") || (actor?.permissionCodes ?? []).includes(permissionCode);
});
app.setErrorHandler((error, request, reply) => {
if (error instanceof AppError) {
return reply.status(error.statusCode).send({
requestId: request.id,
error: { code: error.code, message: error.message, details: error.details ?? {} },
});
}
return reply.status(500).send({
requestId: request.id,
error: { code: "SYS_001", message: "服务器内部错误", details: {} },
});
});
await app.register(searchRoutes, { prefix: "/api" });
return app;
}
describe("global search R23 confidential boundary", () => {
it("does not match sensitive summary text but still matches minimal identifiers", async () => {
const prisma = createPrisma();
const app = await buildApp({
employee: { id: "employee-1", department_id: null, permissionCodes: ["system.settings"] },
prisma,
});
const sensitiveResponse = await app.inject({
method: "GET",
url: "/api/search?q=%E6%9E%81%E5%AF%86%E6%91%98%E8%A6%81",
headers: { authorization: "Bearer test-token" },
});
expect(sensitiveResponse.statusCode).toBe(200);
expect(sensitiveResponse.json().data.items).toEqual([]);
const contractResponse = await app.inject({
method: "GET",
url: "/api/search?q=HT-001",
headers: { authorization: "Bearer test-token" },
});
expect(contractResponse.statusCode).toBe(200);
expect(contractResponse.json().data.items).toEqual(
expect.arrayContaining([
expect.objectContaining({ entityType: "contract_registry", title: "HT-001", subtitle: "涉密合同台账" }),
]),
);
const docResponse = await app.inject({
method: "GET",
url: "/api/search?q=DOC-001",
headers: { authorization: "Bearer test-token" },
});
expect(docResponse.statusCode).toBe(200);
expect(docResponse.json().data.items).toEqual(
expect.arrayContaining([
expect.objectContaining({ entityType: "confidential_doc_ref", title: "DOC-001", subtitle: "涉密资料引用" }),
]),
);
await app.close();
});
});