70 lines
2.7 KiB
TypeScript
70 lines
2.7 KiB
TypeScript
import { describe, expect, it } from 'bun:test';
|
|
import type { SearchResult } from '@/lib/types';
|
|
import {
|
|
extractJsonObject,
|
|
parseCopilotResponse
|
|
} from '@/lib/server/research-copilot-format';
|
|
|
|
function result(overrides: Partial<SearchResult> = {}): SearchResult {
|
|
return {
|
|
chunkId: 1,
|
|
documentId: 1,
|
|
source: 'filings',
|
|
sourceKind: 'filing_brief',
|
|
sourceRef: '0001',
|
|
title: '10-K brief',
|
|
ticker: 'NVDA',
|
|
accessionNumber: '0001',
|
|
filingDate: '2026-02-18',
|
|
citationLabel: 'NVDA · 0001 [1]',
|
|
headingPath: null,
|
|
chunkText: 'Demand stayed strong and margins expanded.',
|
|
snippet: 'Demand stayed strong and margins expanded.',
|
|
score: 0.9,
|
|
vectorRank: 1,
|
|
lexicalRank: 1,
|
|
href: '/analysis/reports/NVDA/0001',
|
|
...overrides
|
|
};
|
|
}
|
|
|
|
describe('research copilot format helpers', () => {
|
|
it('parses strict json responses with suggested actions', () => {
|
|
const parsed = parseCopilotResponse(JSON.stringify({
|
|
answerMarkdown: 'Demand stayed strong [1]. The setup still looks constructive [2].',
|
|
followUps: ['What disconfirms the bull case?', 'Which risks changed most?'],
|
|
suggestedActions: [{
|
|
type: 'draft_memo_section',
|
|
label: 'Use as thesis draft',
|
|
section: 'thesis',
|
|
contentMarkdown: 'Maintain a constructive stance while monitoring concentration.',
|
|
citationIndexes: [1, 2]
|
|
}]
|
|
}), [result(), result({ chunkId: 2, citationLabel: 'NVDA · 0002 [2]', sourceRef: '0002' })], 'What changed?', 'thesis');
|
|
|
|
expect(parsed.citationIndexes).toEqual([1, 2]);
|
|
expect(parsed.followUps).toHaveLength(2);
|
|
expect(parsed.suggestedActions[0]?.type).toBe('draft_memo_section');
|
|
expect(parsed.suggestedActions[0]?.section).toBe('thesis');
|
|
});
|
|
|
|
it('falls back to plain text and default actions when json parsing fails', () => {
|
|
const parsed = parseCopilotResponse(
|
|
'Plain text answer without json wrapper',
|
|
[result(), result({ chunkId: 2, citationLabel: 'NVDA · 0002 [2]', sourceRef: '0002' })],
|
|
'Summarize the setup',
|
|
null
|
|
);
|
|
|
|
expect(parsed.answerMarkdown).toContain('Plain text answer');
|
|
expect(parsed.citationIndexes).toEqual([1, 2]);
|
|
expect(parsed.suggestedActions.some((action) => action.type === 'draft_note')).toBe(true);
|
|
expect(parsed.suggestedActions.some((action) => action.type === 'queue_research_brief')).toBe(true);
|
|
});
|
|
|
|
it('extracts the first json object from fenced responses', () => {
|
|
const extracted = extractJsonObject('```json\n{"answerMarkdown":"A [1]","followUps":[],"suggestedActions":[]}\n```');
|
|
expect(extracted).toBe('{"answerMarkdown":"A [1]","followUps":[],"suggestedActions":[]}');
|
|
});
|
|
});
|