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(); }); });