agent-chat-company-gate.test.js 3.18 KB
/**
 * 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);
  });
});