0a97324f1b
R56–R59: agent.ts 2731→1529줄 god-file 분해 (25 modules) · attrParsers + LLM 메서드 8개 (callNonStreaming, streamChatOnce 등) · executeActions 415줄 → 8 handler 그룹 (file/run/list/brain/calendar/sheets/tasks) · handlePrompt 1100줄 → 7 phase 모듈 (system prompt + budget + autoContinue 등) R50–R55: extension.ts 1145→349줄 (telegram/settings/provider commands 분리) Stocks feature 신규: /stocks slash command (v2.2.152~158) · .astra/stocks.json 저장소 + Yahoo Finance 현재가 갱신 · 8 키워드 필터 (ROE/성장성/유동성/수익성/영업효율/기술력/안정성/PBR) · Naver 시가총액 페이지 JSON API (m.stock.naver.com) 발굴 · LLM Top 5 매력도 분석 + Telegram 자동 보고서 · KST 09:00/15:00 watcher 자동 모니터링 대화 연속성 (v2.2.150~157): · [PRIOR TURN CONCLUSION] block 으로 직전 결론 anchor · thin follow-up 분류 → boilerplate 헤더 suppression · slash 명령 결과 chatHistory mirror (capture wrapper) · echo/parrot 금지 system prompt rule 기타: /stocks 슬래시 자동완성 dropdown UI, Naver JSON API 전환 (cheerio 제거) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
87 lines
2.9 KiB
TypeScript
87 lines
2.9 KiB
TypeScript
/**
|
|
* MockLLMClient 자체의 sanity test.
|
|
*
|
|
* 이게 통과하면 dispatcher / ceoPlanner / ChunkedWriter 등 IAIService 를 받는
|
|
* 코드가 *실제 LLM 없이* 단위 / integration 테스트 가능.
|
|
*
|
|
* 향후 dispatcher 의 multi-stage flow 같은 큰 integration 테스트는 이 mock 을
|
|
* 주입해서 실제 fetch 없이 검증.
|
|
*/
|
|
import { MockLLMClient } from '../helpers/mockLLMClient';
|
|
|
|
describe('MockLLMClient', () => {
|
|
test('큐된 응답을 순차적으로 소비한다', async () => {
|
|
const ai = new MockLLMClient();
|
|
ai.setNextResponse('first');
|
|
ai.setNextResponse('second');
|
|
|
|
const r1 = await ai.chat({ user: 'a' });
|
|
const r2 = await ai.chat({ user: 'b' });
|
|
|
|
expect(r1.content).toBe('first');
|
|
expect(r2.content).toBe('second');
|
|
expect(ai.calls).toHaveLength(2);
|
|
expect(ai.calls[0].user).toBe('a');
|
|
expect(ai.calls[1].user).toBe('b');
|
|
});
|
|
|
|
test('큐가 비면 responder 함수로 동적 응답', async () => {
|
|
const ai = new MockLLMClient();
|
|
ai.setResponder((req) =>
|
|
req.user.includes('summary') ? '요약 결과' : '일반 답변',
|
|
);
|
|
|
|
const r1 = await ai.chat({ user: 'give me summary' });
|
|
const r2 = await ai.chat({ user: 'hello' });
|
|
|
|
expect(r1.content).toBe('요약 결과');
|
|
expect(r2.content).toBe('일반 답변');
|
|
});
|
|
|
|
test('큐가 responder 보다 우선', async () => {
|
|
const ai = new MockLLMClient();
|
|
ai.setResponder(() => 'from responder');
|
|
ai.setNextResponse('from queue');
|
|
|
|
const r1 = await ai.chat({ user: 'x' });
|
|
const r2 = await ai.chat({ user: 'y' });
|
|
|
|
expect(r1.content).toBe('from queue');
|
|
expect(r2.content).toBe('from responder');
|
|
});
|
|
|
|
test('signal 이 이미 abort 된 상태면 AbortError 던진다', async () => {
|
|
const ai = new MockLLMClient();
|
|
const ctrl = new AbortController();
|
|
ctrl.abort();
|
|
|
|
await expect(ai.chat({ user: 'x', signal: ctrl.signal })).rejects.toThrow(/AbortError/);
|
|
// 호출은 기록됨 (signalAborted: true).
|
|
expect(ai.calls).toHaveLength(1);
|
|
expect(ai.calls[0].signalAborted).toBe(true);
|
|
});
|
|
|
|
test('legacy call(prompt) 도 동작', async () => {
|
|
const ai = new MockLLMClient();
|
|
ai.setNextResponse('plain');
|
|
|
|
const result = await ai.call('hi');
|
|
|
|
expect(result).toBe('plain');
|
|
expect(ai.calls).toHaveLength(1);
|
|
});
|
|
|
|
test('reset() 으로 상태 초기화', async () => {
|
|
const ai = new MockLLMClient();
|
|
ai.setNextResponse('x');
|
|
await ai.chat({ user: 'a' });
|
|
|
|
ai.reset();
|
|
|
|
expect(ai.calls).toHaveLength(0);
|
|
// 큐도 비었으니 기본 fallback 응답.
|
|
const r = await ai.chat({ user: 'b' });
|
|
expect(r.content).toContain('mock response');
|
|
});
|
|
});
|