import { StreamingToolCallBuffer } from '../StreamingToolCallBuffer'; describe('StreamingToolCallBuffer', () => { let buffer: StreamingToolCallBuffer; beforeEach(() => { buffer = new StreamingToolCallBuffer(); }); describe('append', () => { it('should accumulate arg chunks for a tool call ID', () => { buffer.append('tc_1', '{"action":"wri'); buffer.append('tc_1', 'te","content":"hello'); expect(buffer.getRawArgs('tc_1')).toBe( '{"action":"write","content":"hello' ); }); it('should track multiple tool calls independently', () => { buffer.append('tc_1', '{"action":"write"}'); buffer.append('tc_2', '{"action":"read"}'); expect(buffer.getRawArgs('tc_1')).toBe('{"action":"write"}'); expect(buffer.getRawArgs('tc_2')).toBe('{"action":"read"}'); }); it('should return undefined for unknown tool call IDs', () => { expect(buffer.getRawArgs('unknown')).toBeUndefined(); }); it('should skip exact duplicate fragments', () => { // First append succeeds expect(buffer.append('tc_1', '{"actio')).toBe(true); // Exact duplicate is skipped expect(buffer.append('tc_1', '{"actio')).toBe(false); // Next fragment is different — appended expect(buffer.append('tc_1', 'n": "wri')).toBe(true); // Duplicate of new fragment — skipped expect(buffer.append('tc_1', 'n": "wri')).toBe(false); // Final result has no duplicates expect(buffer.getRawArgs('tc_1')).toBe('{"action": "wri'); }); it('should handle alternating duplicate pairs (Bedrock pattern)', () => { // Simulate real Bedrock output: each fragment arrives twice const fragments = [ '{"a', 'ction', '": "w', 'rite"', ', "name"', ': "test.tsx"', ]; for (const frag of fragments) { buffer.append('tc_1', frag); // first — appended buffer.append('tc_1', frag); // duplicate — skipped } expect(buffer.getRawArgs('tc_1')).toBe( '{"action": "write", "name": "test.tsx"' ); }); it('should return false for empty args', () => { expect(buffer.append('tc_1', '')).toBe(false); }); it('should handle mixed fragment and accumulated patterns', () => { // Fragment mode (normal append) buffer.append('tc_1', '{"action":"wri'); buffer.append('tc_1', 'te"}'); expect(buffer.getRawArgs('tc_1')).toBe('{"action":"write"}'); }); }); describe('setToolName / getToolName', () => { it('should store and retrieve tool names', () => { buffer.setToolName('tc_1', 'content_tool'); expect(buffer.getToolName('tc_1')).toBe('content_tool'); }); it('should not overwrite an existing tool name', () => { buffer.setToolName('tc_1', 'content_tool'); buffer.setToolName('tc_1', 'other_tool'); expect(buffer.getToolName('tc_1')).toBe('content_tool'); }); it('should return undefined for unknown tool calls', () => { expect(buffer.getToolName('unknown')).toBeUndefined(); }); }); describe('extractFieldValue', () => { it('should extract a simple string field', () => { buffer.append('tc_1', '{"action":"write","filename":"test.tsx"}'); expect(buffer.extractFieldValue('tc_1', 'action')).toBe('write'); expect(buffer.extractFieldValue('tc_1', 'filename')).toBe('test.tsx'); }); it('should extract content with escaped characters', () => { buffer.append('tc_1', '{"content":"line1\\nline2\\ttab"}'); expect(buffer.extractFieldValue('tc_1', 'content')).toBe( 'line1\nline2\ttab' ); }); it('should extract content with escaped quotes', () => { buffer.append('tc_1', '{"content":"say \\"hello\\""}'); expect(buffer.extractFieldValue('tc_1', 'content')).toBe('say "hello"'); }); it('should extract content with escaped backslashes', () => { buffer.append('tc_1', '{"content":"path\\\\to\\\\file"}'); expect(buffer.extractFieldValue('tc_1', 'content')).toBe( 'path\\to\\file' ); }); it('should handle truncated content (no closing quote)', () => { buffer.append( 'tc_1', '{"action":"write","content":"function App() {\\n return
He' ); const content = buffer.extractFieldValue('tc_1', 'content'); expect(content).toBe('function App() {\n return
He'); }); it('should handle field with space before colon', () => { buffer.append('tc_1', '{"content": "value with space"}'); expect(buffer.extractFieldValue('tc_1', 'content')).toBe( 'value with space' ); }); it('should handle field with spaces around colon', () => { buffer.append('tc_1', '{"content" : "value"}'); expect(buffer.extractFieldValue('tc_1', 'content')).toBe('value'); }); it('should return undefined for missing field', () => { buffer.append('tc_1', '{"action":"write"}'); expect(buffer.extractFieldValue('tc_1', 'content')).toBeUndefined(); }); it('should return undefined for unknown tool call ID', () => { expect(buffer.extractFieldValue('unknown', 'content')).toBeUndefined(); }); it('should extract large content (simulating 30k+ token file)', () => { const largeContent = 'x'.repeat(50000); buffer.append('tc_1', `{"action":"write","content":"${largeContent}"}`); expect(buffer.extractFieldValue('tc_1', 'content')).toBe(largeContent); }); it('should extract from incrementally streamed chunks', () => { // Simulate real streaming: args arrive in small chunks const chunks = [ '{"action":"w', 'rite","file', 'name":"app.tsx","con', 'tent":"import React', " from 'react';\\n", 'export default func', 'tion App() {\\n ret', 'urn
Hello;\\n}', ]; for (const chunk of chunks) { buffer.append('tc_1', chunk); } expect(buffer.extractFieldValue('tc_1', 'action')).toBe('write'); expect(buffer.extractFieldValue('tc_1', 'filename')).toBe('app.tsx'); expect(buffer.extractFieldValue('tc_1', 'content')).toBe( "import React from 'react';\nexport default function App() {\n return
Hello
;\n}" ); }); }); describe('has', () => { it('should return true for tracked tool calls', () => { buffer.append('tc_1', '{}'); expect(buffer.has('tc_1')).toBe(true); }); it('should return false for untracked tool calls', () => { expect(buffer.has('tc_1')).toBe(false); }); }); describe('clear', () => { it('should remove a specific tool call', () => { buffer.append('tc_1', '{"a":1}'); buffer.setToolName('tc_1', 'test_tool'); buffer.clear('tc_1'); expect(buffer.has('tc_1')).toBe(false); expect(buffer.getRawArgs('tc_1')).toBeUndefined(); expect(buffer.getToolName('tc_1')).toBeUndefined(); }); it('should not affect other tool calls', () => { buffer.append('tc_1', '{"a":1}'); buffer.append('tc_2', '{"b":2}'); buffer.clear('tc_1'); expect(buffer.has('tc_2')).toBe(true); }); }); describe('clearAll', () => { it('should remove all tool calls', () => { buffer.append('tc_1', '{"a":1}'); buffer.append('tc_2', '{"b":2}'); buffer.setToolName('tc_1', 'tool_a'); buffer.setToolName('tc_2', 'tool_b'); buffer.clearAll(); expect(buffer.has('tc_1')).toBe(false); expect(buffer.has('tc_2')).toBe(false); expect(buffer.getToolName('tc_1')).toBeUndefined(); expect(buffer.getToolName('tc_2')).toBeUndefined(); }); it('should clear index-to-id mappings', () => { buffer.setIndexMapping(0, 'tc_1'); buffer.setIndexMapping(1, 'tc_2'); buffer.clearAll(); expect(buffer.getIdByIndex(0)).toBeUndefined(); expect(buffer.getIdByIndex(1)).toBeUndefined(); }); }); describe('setIndexMapping / getIdByIndex', () => { it('should store and retrieve index-to-id mappings', () => { buffer.setIndexMapping(0, 'tc_1'); buffer.setIndexMapping(1, 'tc_2'); expect(buffer.getIdByIndex(0)).toBe('tc_1'); expect(buffer.getIdByIndex(1)).toBe('tc_2'); }); it('should return undefined for unmapped index', () => { expect(buffer.getIdByIndex(99)).toBeUndefined(); }); it('should overwrite existing index mapping', () => { buffer.setIndexMapping(0, 'tc_old'); buffer.setIndexMapping(0, 'tc_new'); expect(buffer.getIdByIndex(0)).toBe('tc_new'); }); it('should support Bedrock streaming pattern end-to-end', () => { // START chunk: id + name + index buffer.setIndexMapping(0, 'tooluse_abc'); buffer.setToolName('tooluse_abc', 'content_tool'); // DELTA chunks: args + index (no id) const resolvedId = buffer.getIdByIndex(0); expect(resolvedId).toBe('tooluse_abc'); buffer.append(resolvedId!, '{"action":"wri'); buffer.append(resolvedId!, 'te","content":"hello"}'); expect(buffer.getRawArgs('tooluse_abc')).toBe( '{"action":"write","content":"hello"}' ); expect(buffer.getToolName('tooluse_abc')).toBe('content_tool'); }); }); });