/** * agent-chat-company-gate.test.js —— 38 号文档 Phase 3 * * agent-chat.js(/api/agent/*)一直靠 :agentId 里的命名空间前缀("{companyId}:{role}", * locator.companyStateDir 解析)区分会话属于哪家公司,但 Phase 1/2 都没顾上这个文件的 * 归属校验——只要求登录,没查公司归不归当前用户。真实 curl 验证过是可以跨租户读会话的 * (GET .../conversations 对不是自己公司的 companyId:role 依然 200)。 * * 补的是 router.param('agentId', ...):这里直接拿 Express 暴露的 `router.params.agentId` * 数组单元测(不发真实 HTTP、不起 server),比照 company.js 那道 companyId 收口的判据 * canAccessCompany,行为应该完全对称。 */ process.env.LEMONBOSS_CLAUDE_MODE = 'mock'; // 必须在 require 路由之前 import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import path from 'path'; const REPO_ROOT = path.resolve(__dirname, '../../..'); const { ensureTestCompany, removeTestCompany } = require('../helpers/ensure-test-company'); const agentChatRouter = require('../../routes/agent-chat.js'); const paramHandler = agentChatRouter.params.agentId[0]; const OWNER = { id: 9001, role: 'user' }; const OTHER = { id: 9002, role: 'user' }; const ADMIN = { id: 9003, role: 'admin' }; const INTERNAL = { id: 0, role: 'system', internal: true }; const CID = 'gate-test-co'; function runParam(agentId, user) { return new Promise((resolve) => { const req = { user }; const res = { _status: null, status(code) { this._status = code; return this; }, json(body) { resolve({ status: this._status, body }); }, }; const next = (err) => resolve({ status: null, next: err || true }); paramHandler(req, res, next, agentId); }); } describe('agent-chat.js 的 :agentId 归属校验(38 号文档 Phase 3)', () => { beforeAll(() => { ensureTestCompany(CID, { ownerId: OWNER.id }); }); afterAll(() => { removeTestCompany(CID); }); it('公司自己的用户能过(next 被调用,不是 404)', async () => { const r = await runParam(`${CID}:some-role`, OWNER); expect(r.next).toBe(true); }); it('别的登录用户访问 → 404(不暴露这个公司/agent 存在)', async () => { const r = await runParam(`${CID}:some-role`, OTHER); expect(r.status).toBe(404); expect(r.body.error).toMatch(/不存在/); }); it('admin 不受归属限制', async () => { const r = await runParam(`${CID}:some-role`, ADMIN); expect(r.next).toBe(true); }); it('公司不存在 → 404(不是 next 放行)', async () => { const r = await runParam('no-such-company:role', OWNER); expect(r.status).toBe(404); }); it('没有 companyId 前缀的全局 agent(如 master)不做归属校验,任何登录用户都放行', async () => { const r = await runParam('master', OTHER); expect(r.next).toBe(true); }); it('内部自环调用(req.user.internal)直接放行,不查归属', async () => { const r = await runParam(`${CID}:some-role`, INTERNAL); expect(r.next).toBe(true); const r2 = await runParam('no-such-company:role', INTERNAL); expect(r2.next).toBe(true); }); });