|
1 | 1 | /** |
2 | 2 | * Unit tests — template.ts (output parsing & JSON repair). |
3 | 3 | * |
4 | | - * Tests parseSchedulerOutput and parseAgentOutput with various |
5 | | - * malformed JSON patterns that LLMs commonly produce. |
| 4 | + * Tests parseAgentOutput with various malformed JSON patterns |
| 5 | + * that LLMs commonly produce. |
6 | 6 | */ |
7 | 7 |
|
8 | 8 | import { describe, it, expect } from 'vitest'; |
9 | | -import { parseSchedulerOutput, parseAgentOutput } from '../extension/template.js'; |
10 | | - |
11 | | -describe('parseSchedulerOutput — valid JSON', () => { |
12 | | - it('parses minimal valid JSON', () => { |
13 | | - const input = JSON.stringify({ pass_through: true, reasoning: 'test' }); |
14 | | - const result = parseSchedulerOutput(input); |
15 | | - expect(result).not.toBeNull(); |
16 | | - expect(result!.pass_through).toBe(true); |
17 | | - expect(result!.reasoning).toBe('test'); |
18 | | - }); |
19 | | - |
20 | | - it('parses full agent plan', () => { |
21 | | - const input = JSON.stringify({ |
22 | | - intent: 'coding', |
23 | | - agents: [ |
24 | | - { type: 'coding', model: 'sonnet', id: 'c1', files: ['src/index.ts'], task: 'fix bug' }, |
25 | | - ], |
26 | | - parallel_groups: [['c1']], |
27 | | - }); |
28 | | - const result = parseSchedulerOutput(input); |
29 | | - expect(result).not.toBeNull(); |
30 | | - expect(result!.intent).toBe('coding'); |
31 | | - expect(result!.agents).toHaveLength(1); |
32 | | - expect(result!.agents![0].type).toBe('coding'); |
33 | | - }); |
34 | | -}); |
35 | | - |
36 | | -describe('parseSchedulerOutput — JSON repair', () => { |
37 | | - it('repairs trailing comma in object', () => { |
38 | | - const input = '{"pass_through": true,"reasoning": "test",}'; |
39 | | - const result = parseSchedulerOutput(input); |
40 | | - expect(result).not.toBeNull(); |
41 | | - expect(result!.pass_through).toBe(true); |
42 | | - }); |
43 | | - |
44 | | - it('repairs trailing comma in array', () => { |
45 | | - const input = '{"agents": [{"type": "coding", "id": "c1",}]}'; |
46 | | - const result = parseSchedulerOutput(input); |
47 | | - expect(result).not.toBeNull(); |
48 | | - expect(result!.agents).toHaveLength(1); |
49 | | - }); |
50 | | - |
51 | | - it('repairs single quotes to double quotes', () => { |
52 | | - const input = "{'pass_through': true, 'reasoning': 'test'}"; |
53 | | - const result = parseSchedulerOutput(input); |
54 | | - expect(result).not.toBeNull(); |
55 | | - expect(result!.pass_through).toBe(true); |
56 | | - }); |
57 | | - |
58 | | - it('repairs unquoted keys', () => { |
59 | | - const input = '{pass_through: true, reasoning: "test"}'; |
60 | | - const result = parseSchedulerOutput(input); |
61 | | - expect(result).not.toBeNull(); |
62 | | - expect(result!.pass_through).toBe(true); |
63 | | - }); |
64 | | - |
65 | | - it('repairs Python-style booleans', () => { |
66 | | - const input = '{"pass_through": True, "reasoning": "test"}'; |
67 | | - const result = parseSchedulerOutput(input); |
68 | | - expect(result).not.toBeNull(); |
69 | | - expect(result!.pass_through).toBe(true); |
70 | | - }); |
71 | | - |
72 | | - it('repairs None → null', () => { |
73 | | - const input = '{"pass_through": None, "reasoning": "test"}'; |
74 | | - const result = parseSchedulerOutput(input); |
75 | | - expect(result).not.toBeNull(); |
76 | | - expect(result!.reasoning).toBe('test'); |
77 | | - }); |
78 | | - |
79 | | - it('repairs JavaScript undefined → null', () => { |
80 | | - const input = '{"pass_through": undefined}'; |
81 | | - const result = parseSchedulerOutput(input); |
82 | | - expect(result).not.toBeNull(); |
83 | | - }); |
84 | | - |
85 | | - it('handles string without JSON structure gracefully', () => { |
86 | | - // Input with missing closing brace — repairJSON can't extract region |
87 | | - // without matching braces, so it returns null. |
88 | | - const input = '{"pass_through": true, "reasoning": "test"'; |
89 | | - const result = parseSchedulerOutput(input); |
90 | | - expect(result).toBeNull(); |
91 | | - }); |
92 | | - |
93 | | - it('repairs truncated array brackets', () => { |
94 | | - // Brackets are repaired, but the function currently requires matching |
95 | | - // opening/closing pairs. This tests graceful handling. |
96 | | - const input = '[{"a": 1'; |
97 | | - expect(parseSchedulerOutput(input)).toBeNull(); |
98 | | - }); |
99 | | - |
100 | | - it('removes // line comments', () => { |
101 | | - const input = '{\n// this is a comment\n"pass_through": true}'; |
102 | | - const result = parseSchedulerOutput(input); |
103 | | - expect(result).not.toBeNull(); |
104 | | - expect(result!.pass_through).toBe(true); |
105 | | - }); |
106 | | - |
107 | | - it('removes /* block comments */', () => { |
108 | | - const input = '{\n/* block comment */\n"pass_through": true}'; |
109 | | - const result = parseSchedulerOutput(input); |
110 | | - expect(result).not.toBeNull(); |
111 | | - expect(result!.pass_through).toBe(true); |
112 | | - }); |
113 | | -}); |
114 | | - |
115 | | -describe('parseSchedulerOutput — code block extraction', () => { |
116 | | - it('extracts JSON from ```json code block', () => { |
117 | | - const input = 'Here is the plan:\n```json\n{"pass_through": true}\n```\nEnd.'; |
118 | | - const result = parseSchedulerOutput(input); |
119 | | - expect(result).not.toBeNull(); |
120 | | - expect(result!.pass_through).toBe(true); |
121 | | - }); |
122 | | - |
123 | | - it('extracts JSON from bare ``` code block', () => { |
124 | | - const input = 'Output:\n```\n{"pass_through": true}\n```'; |
125 | | - const result = parseSchedulerOutput(input); |
126 | | - expect(result).not.toBeNull(); |
127 | | - expect(result!.pass_through).toBe(true); |
128 | | - }); |
129 | | - |
130 | | - it('extracts and repairs JSON in code block', () => { |
131 | | - const input = '```json\n{pass_through: True}\n```'; |
132 | | - const result = parseSchedulerOutput(input); |
133 | | - expect(result).not.toBeNull(); |
134 | | - expect(result!.pass_through).toBe(true); |
135 | | - }); |
136 | | -}); |
137 | | - |
138 | | -describe('parseSchedulerOutput — fallback / null', () => { |
139 | | - it('returns null for empty string', () => { |
140 | | - expect(parseSchedulerOutput('')).toBeNull(); |
141 | | - }); |
142 | | - |
143 | | - it('returns null for random text without JSON', () => { |
144 | | - expect(parseSchedulerOutput('This is just some random text without any JSON structure.')).toBeNull(); |
145 | | - }); |
146 | | - |
147 | | - it('returns null for invalid JSON that cannot be repaired', () => { |
148 | | - const input = '{invalid: }'; |
149 | | - // The repair process still applies fixes, but it might still fail |
150 | | - // This tests that no exception is thrown |
151 | | - expect(parseSchedulerOutput.bind(null, input)).not.toThrow(); |
152 | | - }); |
153 | | -}); |
| 9 | +import { parseAgentOutput } from '../extension/template.js'; |
154 | 10 |
|
155 | 11 | describe('parseAgentOutput', () => { |
156 | 12 | it('parses a valid coding output', () => { |
|
0 commit comments