bin-lemon-internal-token.test.js 2.15 KB
/**
 * bin/lemon.js — commit b7b5096(全站鉴权)之后,agent-chat.js 挂了 requireAuthOrInternal,
 * 这个 CLI 跑在 agent 自己的 claude CLI 子进程里,没有登录 cookie,只能靠父进程通过
 * process.env 传下来的 LEMON_INTERNAL_TOKEN 过 internal 校验(server/middleware/jwt-auth.js)。
 *
 * 用一个假的 HTTP 服务器代替真实后端,只验证 bin/lemon.js 发请求时带没带这个 header。
 */
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import http from 'http';
import { execFile } from 'child_process';
import path from 'path';

const LEMON_BIN = path.resolve(__dirname, '../../../bin/lemon.js');

let server, port, lastHeaders;

function runLemon(args, env) {
  return new Promise((resolve) => {
    execFile('node', [LEMON_BIN, ...args], { env }, () => resolve());
  });
}

describe('bin/lemon.js — LEMON_INTERNAL_TOKEN 透传', () => {
  beforeAll(async () => {
    server = http.createServer((req, res) => {
      lastHeaders = req.headers;
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ ok: true }));
    });
    // 用随机端口,避免跟本机真实跑着的 3010 dev server 撞车(LEMON_API_BASE 让 bin/lemon.js 指过来)
    await new Promise((resolve) => server.listen(0, resolve));
    port = server.address().port;
  });

  afterAll(() => new Promise((resolve) => server.close(resolve)));

  it('设了 LEMON_INTERNAL_TOKEN 时,请求头带上 x-internal-token', async () => {
    await runLemon(
      ['task', 'report', '--taskId', 't1', '--from', 'a', '--to', 'b'],
      { ...process.env, LEMON_API_BASE: `http://localhost:${port}/api`, LEMON_INTERNAL_TOKEN: 'test-token-abc' }
    );
    expect(lastHeaders['x-internal-token']).toBe('test-token-abc');
  });

  it('没设 LEMON_INTERNAL_TOKEN 时,不带这个 header(不伪造身份)', async () => {
    const env = { ...process.env, LEMON_API_BASE: `http://localhost:${port}/api` };
    delete env.LEMON_INTERNAL_TOKEN;
    await runLemon(['task', 'report', '--taskId', 't1', '--from', 'a', '--to', 'b'], env);
    expect(lastHeaders['x-internal-token']).toBeUndefined();
  });
});