/** * 46 号文档 Phase 2:deep-research 按需触发 org-hunt 的轻量调研钩子。 * * 最重要的一条是"默认关闭"这条安全阀——`ENABLE_DEEP_RESEARCH` 不为 `'1'` 时, * 连注入的假实现都不应该被调用到,直接短路返回 null(避免真事故:Phase 1 就因为 * 一个新触发点疏于隔离,一次单测跑出 44 个残留 CLI 子进程)。 */ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { triggerDeepResearch, setTriggerDeepResearch, buildResearchPrompt, } from '../../services/deep-research.js'; describe('deep-research · triggerDeepResearch', () => { const ORIGINAL_ENABLE = process.env.ENABLE_DEEP_RESEARCH; beforeEach(() => { setTriggerDeepResearch(null); }); afterEach(() => { setTriggerDeepResearch(null); if (ORIGINAL_ENABLE === undefined) delete process.env.ENABLE_DEEP_RESEARCH; else process.env.ENABLE_DEEP_RESEARCH = ORIGINAL_ENABLE; }); it('ENABLE_DEEP_RESEARCH 未开启时直接返回 null,不调用任何实现(哪怕注入了假实现)', async () => { delete process.env.ENABLE_DEEP_RESEARCH; let called = false; setTriggerDeepResearch(async () => { called = true; return { findings: 'x', sources: [], tokens: 1 }; }); const out = await triggerDeepResearch({ companyId: 'co-x', task: { id: 'T-1' }, topic: '贪吃蛇怎么做' }); expect(out).toBeNull(); expect(called).toBe(false); }); it('开启后走注入实现,返回其结果', async () => { process.env.ENABLE_DEEP_RESEARCH = '1'; setTriggerDeepResearch(async (ctx) => { expect(ctx.topic).toBe('贪吃蛇怎么做'); return { findings: '业界通常先定义棋盘再做碰撞检测', sources: ['https://example.com'], tokens: 123 }; }); const out = await triggerDeepResearch({ companyId: 'co-x', task: { id: 'T-1' }, topic: '贪吃蛇怎么做' }); expect(out).toEqual({ findings: '业界通常先定义棋盘再做碰撞检测', sources: ['https://example.com'], tokens: 123, }); }); it('开启后注入实现抛错:吞掉,返回 null,不向调用方传播', async () => { process.env.ENABLE_DEEP_RESEARCH = '1'; setTriggerDeepResearch(async () => { throw new Error('CLI 起不来'); }); await expect( triggerDeepResearch({ companyId: 'co-x', task: { id: 'T-1' }, topic: 't' }) ).resolves.toBeNull(); }); it('VITEST 环境且未注入时即使开启开关也跳过真实调用', async () => { process.env.ENABLE_DEEP_RESEARCH = '1'; // 不调用 setTriggerDeepResearch,保持默认实现——process.env.VITEST 由 vitest 自动置位 const out = await triggerDeepResearch({ companyId: 'co-x', task: { id: 'T-1' }, topic: 't' }); expect(out).toBeNull(); }); }); describe('deep-research · buildResearchPrompt', () => { it('提示词包含调研主题、交回格式,明确说明不套用训练场景三件套契约', () => { const prompt = buildResearchPrompt({ topic: '贪吃蛇怎么做' }); expect(prompt).toContain('贪吃蛇怎么做'); expect(prompt).toContain('findings'); expect(prompt).toContain('sources'); expect(prompt).toContain('不需要'); }); it('主题缺失时给出明确占位', () => { const prompt = buildResearchPrompt({}); expect(prompt).toContain('调用方没给主题'); }); });