关键测试解析(下):Context Engine、Phase Runner、Hook System

📑 目录

这是「GSD 全景代码解析」专题的第 50 篇。

一、Context Engine 测试解析

context-engine.test.js 是 GSD 测试体系中最复杂的单元测试之一,它验证上下文预算管理、截断策略和引用注入的正确性。

1.1 上下文预算测试

describe('ContextEngine', () => {
  test('应正确计算当前上下文大小', () => {
    const engine = new ContextEngine({ budget: 4000 });
    engine.addSystemPrompt('你是一个助手'); // ~10 tokens
    engine.addUserMessage('请帮我写代码'); // ~8 tokens
    
    expect(engine.getCurrentUsage()).toBe(18);
    expect(engine.getRemainingBudget()).toBe(3982);
  });

  test('超出预算应触发截断', () => {
    const engine = new ContextEngine({ budget: 100 });
    engine.addSystemPrompt('x'.repeat(500)); // 远超预算
    
    expect(engine.isTruncated).toBe(true);
    expect(engine.getCurrentUsage()).toBeLessThanOrEqual(100);
  });
});

1.2 截断策略测试

describe('Truncation Strategies', () => {
  test('FIFO 截断应移除最早的消息', () => {
    const engine = new ContextEngine({ 
      budget: 50, 
      strategy: 'fifo' 
    });
    engine.addMessage('msg1');
    engine.addMessage('msg2');
    engine.addMessage('msg3'); // 触发截断
    
    const messages = engine.getMessages();
    expect(messages.map(m => m.content)).not.toContain('msg1');
  });

  test('Summary 截断应保留摘要', () => {
    const engine = new ContextEngine({ 
      budget: 100, 
      strategy: 'summary' 
    });
    
    // 添加足够多的消息触发截断
    for (let i = 0; i < 20; i++) {
      engine.addMessage(`重要信息 ${i}`);
    }
    
    const summary = engine.getSummary();
    expect(summary).toContain('先前对话');
    expect(engine.getCurrentUsage()).toBeLessThanOrEqual(100);
  });

  test('Priority 截断应保留高优先级内容', () => {
    const engine = new ContextEngine({ 
      budget: 50, 
      strategy: 'priority' 
    });
    engine.addMessage({ content: '低优先级', priority: 1 });
    engine.addMessage({ content: '高优先级', priority: 10 });
    engine.addMessage({ content: '中等优先级', priority: 5 });
    
    // 强制截断
    engine.truncate();
    
    const messages = engine.getMessages();
    expect(messages.map(m => m.content)).toContain('高优先级');
    expect(messages.map(m => m.content)).not.toContain('低优先级');
  });
});

1.3 @Reference 解析测试

test('应正确解析 @ 引用', async () => {
  const engine = new ContextEngine();
  const prompt = '请查看 @src/utils/helper.ts 的代码';
  
  const resolved = await engine.resolveReferences(prompt, {
    'src/utils/helper.ts': 'export function helper() {}'
  });
  
  expect(resolved).toContain('export function helper() {}');
  expect(resolved).not.toContain('@src/utils/helper.ts');
});

test('不存在的引用应抛出 ReferenceError', async () => {
  const engine = new ContextEngine();
  const prompt = '请查看 @non-existent.md';
  
  await expect(engine.resolveReferences(prompt, {}))
    .rejects.toThrow(ReferenceError);
});

二、Phase Runner 集成测试

phase-runner.integration.test.js 验证 Phase Runner 作为执行引擎核心组件的集成行为。

2.1 完整 Phase 执行测试

describe('PhaseRunner Integration', () => {
  test('应完成完整的 plan-phase 执行', async () => {
    const runner = new PhaseRunner();
    const plan = await loadTestPlan('simple-project');
    
    const result = await runner.run('plan-phase', plan);
    
    expect(result.status).toBe('COMPLETED');
    expect(result.artifacts).toContain('PROJECT.md');
    expect(result.artifacts).toContain('ROADMAP.md');
    expect(result.duration).toBeGreaterThan(0);
  });

  test('execute-phase 应正确调度任务', async () => {
    const runner = new PhaseRunner();
    const plan = await loadTestPlan('with-tasks');
    
    const taskOrder = [];
    runner.onTaskStart((task) => taskOrder.push(task.id));
    
    await runner.run('execute-phase', plan);
    
    // 验证依赖顺序:任务 2 依赖任务 1
    expect(taskOrder.indexOf('1')).toBeLessThan(taskOrder.indexOf('2'));
  });
});

2.2 错误恢复测试

test('任务失败应触发 rollback', async () => {
  const runner = new PhaseRunner();
  const plan = await loadTestPlan('failing-task');
  
  // 让第二个任务失败
  mockAgent('coder').mockRejectedValue(new Error('编译失败'));
  
  const result = await runner.run('execute-phase', plan);
  
  expect(result.status).toBe('FAILED');
  expect(result.rollbackActions).toHaveLengthGreaterThan(0);
  
  // 验证已完成的任务被回滚
  const rolledBackTasks = result.rollbackActions.map(a => a.taskId);
  expect(rolledBackTasks).toContain('1');
});

test('部分失败应支持 continue 模式', async () => {
  const runner = new PhaseRunner({ onFailure: 'continue' });
  const plan = await loadTestPlan('failing-task');
  
  mockAgent('coder').mockRejectedValue(new Error('编译失败'));
  
  const result = await runner.run('execute-phase', plan);
  
  expect(result.status).toBe('COMPLETED_WITH_WARNINGS');
  expect(result.completedTasks).toHaveLength(2);
  expect(result.failedTasks).toHaveLength(1);
});

三、Hook System 生命周期测试

hook-system.integration.test.js 验证钩子系统的注册、触发顺序和错误处理。

3.1 钩子注册与触发

describe('HookSystem', () => {
  test('应在正确生命周期点触发钩子', async () => {
    const hooks = new HookSystem();
    const events = [];
    
    hooks.register('beforeTask', () => events.push('before'));
    hooks.register('afterTask', () => events.push('after'));
    hooks.register('onError', () => events.push('error'));
    
    await hooks.trigger('beforeTask', { taskId: '1' });
    await hooks.trigger('afterTask', { taskId: '1' });
    
    expect(events).toEqual(['before', 'after']);
  });

  test('多个同类型钩子应按注册顺序执行', async () => {
    const hooks = new HookSystem();
    const order = [];
    
    hooks.register('beforeTask', () => order.push(1));
    hooks.register('beforeTask', () => order.push(2));
    hooks.register('beforeTask', () => order.push(3));
    
    await hooks.trigger('beforeTask', {});
    
    expect(order).toEqual([1, 2, 3]);
  });
});

3.2 钩子错误传播

test('钩子错误不应中断主流程', async () => {
  const hooks = new HookSystem();
  let mainFlowCompleted = false;
  
  hooks.register('beforeTask', () => {
    throw new Error('钩子崩溃');
  });
  
  try {
    await hooks.trigger('beforeTask', {});
    mainFlowCompleted = true;
  } catch (e) {
    // 不应进入这里
  }
  
  expect(mainFlowCompleted).toBe(true);
});

test('应支持同步和异步钩子', async () => {
  const hooks = new HookSystem();
  const results = [];
  
  hooks.register('afterTask', () => results.push('sync'));
  hooks.register('afterTask', async () => {
    await delay(10);
    results.push('async');
  });
  
  await hooks.trigger('afterTask', {});
  
  expect(results).toEqual(['sync', 'async']);
});

四、Mock Agent 工厂

function createMockAgent(name, behavior = 'success') {
  const responses = {
    success: (task) => ({ result: `completed: ${task.name}` }),
    failure: () => { throw new Error('Agent failed'); },
    slow: async (task) => {
      await delay(5000);
      return { result: `slow: ${task.name}` };
    }
  };
  
  return {
    name,
    invoke: jest.fn(async (task) => responses[behavior](task))
  };
}

五、测试性能基准

Plan Parser:     ~50ms  / 100 plans
State Machine:   ~5ms   / 1000 transitions
Agent Delegator: ~20ms  / 100 selections
Context Engine:  ~30ms  / 100 truncations
Phase Runner:    ~200ms / 1 full phase (mocked)
Hook System:     ~10ms  / 100 triggers

下一篇预告: 第 51 篇《安全体系:secret-audit、credential-gate 与防注入机制》

我们将深入解析 GSD 的安全防护体系,包括敏感信息审计、凭据门控和提示词注入防御机制。敬请期待。