98 lines
4.3 KiB
TypeScript
98 lines
4.3 KiB
TypeScript
import * as fs from 'fs';
|
|
import * as os from 'os';
|
|
import * as path from 'path';
|
|
import {
|
|
buildSecondBrainTrace,
|
|
renderSecondBrainTraceContext,
|
|
renderSecondBrainTraceMarkdown
|
|
} from '../src/features/secondBrainTrace';
|
|
|
|
describe('Second Brain Trace', () => {
|
|
let brainRoot: string;
|
|
|
|
beforeEach(() => {
|
|
brainRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'second-brain-trace-'));
|
|
fs.mkdirSync(path.join(brainRoot, 'records', 'ProjectChronicle', 'decisions'), { recursive: true });
|
|
fs.writeFileSync(
|
|
path.join(brainRoot, 'records', 'ProjectChronicle', 'decisions', 'ADR-0002-low-dependency-design.md'),
|
|
[
|
|
'# ADR-0002 Low Dependency Design',
|
|
'',
|
|
'Project Chronicle Guard should start with Markdown files and an independent module.',
|
|
'Vector DB and relational DB are later expansion options, not MVP dependencies.'
|
|
].join('\n'),
|
|
'utf8'
|
|
);
|
|
fs.writeFileSync(
|
|
path.join(brainRoot, 'general-note.md'),
|
|
'# General Note\n\nThis unrelated note talks about coffee and weather.',
|
|
'utf8'
|
|
);
|
|
fs.mkdirSync(path.join(brainRoot, '00_Raw', 'conversations'), { recursive: true });
|
|
fs.writeFileSync(
|
|
path.join(brainRoot, '00_Raw', 'conversations', '2026-05-01.md'),
|
|
[
|
|
'# Raw Conversation',
|
|
'',
|
|
'dependency complexity schema drift documentation gap recommendations',
|
|
'This is a noisy transcript and should not be selected before curated records.'
|
|
].join('\n'),
|
|
'utf8'
|
|
);
|
|
fs.writeFileSync(
|
|
path.join(brainRoot, 'Index_692.md'),
|
|
'# Index\n\ndependency complexity schema drift documentation gap'
|
|
);
|
|
});
|
|
|
|
afterEach(() => {
|
|
fs.rmSync(brainRoot, { recursive: true, force: true });
|
|
});
|
|
|
|
it('retrieves and marks relevant Second Brain notes for project-specific questions', () => {
|
|
const trace = buildSecondBrainTrace('Project Chronicle Guard MVP에서 Vector DB는 어떻게 다뤄야 해?', brainRoot);
|
|
|
|
expect(trace.shouldUseSecondBrain).toBe(true);
|
|
expect(trace.secondBrainUsed).toBe(true);
|
|
expect(trace.retrievedDocuments[0].path).toContain('ADR-0002-low-dependency-design.md');
|
|
expect(trace.retrievedDocuments[0].usedInAnswer).toBe(true);
|
|
expect(trace.retrievedDocuments[0].selectedForAnswerContext).toBe(true);
|
|
expect(trace.groundingScore).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('renders user-facing markdown and debug JSON', () => {
|
|
const trace = buildSecondBrainTrace('Second Brain을 참고해서 low dependency 원칙 알려줘', brainRoot, { force: true });
|
|
const markdown = renderSecondBrainTraceMarkdown(trace, true);
|
|
const context = renderSecondBrainTraceContext(trace);
|
|
|
|
expect(markdown).toContain('<details>');
|
|
expect(markdown).toContain('<summary>2nd Brain Trace: 사용함');
|
|
expect(markdown).toContain('## 2nd Brain 사용 여부');
|
|
expect(markdown).toContain('## 답변 컨텍스트로 선택된 2nd Brain 문서');
|
|
expect(markdown).toContain('## Second Brain Debug JSON');
|
|
expect(context).toContain('[SECOND BRAIN TRACE]');
|
|
expect(context).toContain('Retrieval query:');
|
|
expect(context).toContain('Do not imitate dramatic wording');
|
|
});
|
|
|
|
it('explains when Second Brain is not needed', () => {
|
|
const trace = buildSecondBrainTrace('오늘 날짜가 뭐야?', brainRoot);
|
|
|
|
expect(trace.shouldUseSecondBrain).toBe(false);
|
|
expect(trace.secondBrainUsed).toBe(false);
|
|
expect(renderSecondBrainTraceMarkdown(trace)).toContain('사용하지 않음');
|
|
expect(renderSecondBrainTraceMarkdown(trace)).toContain('<details>');
|
|
});
|
|
|
|
it('prefers curated notes over raw conversations and index files', () => {
|
|
const trace = buildSecondBrainTrace(
|
|
'dependency complexity schema drift documentation gap 문제 대응 가이드',
|
|
brainRoot
|
|
);
|
|
|
|
expect(trace.retrievedDocuments[0].path).toContain('ADR-0002-low-dependency-design.md');
|
|
expect(trace.retrievedDocuments.find((doc) => doc.path.includes('00_Raw'))).toBeUndefined();
|
|
expect(trace.retrievedDocuments[0].path).not.toContain('Index_692.md');
|
|
});
|
|
});
|