import { describe, test, expect } from 'bun:test' const { normalizePayload } = await import('../services/transport') // extractContent is not exported; we test it via normalizePayload's content field // ============================================================================= // extractContent (via normalizePayload content field) // ============================================================================= describe('extractContent', () => { test('returns empty string for null payload', () => { const result = normalizePayload('assistant', null) expect(result.content).toBe('') }) test('returns empty string for undefined payload', () => { const result = normalizePayload('assistant', undefined) expect(result.content).toBe('') }) test('returns the string for string payload', () => { const result = normalizePayload('assistant', 'hello world') expect(result.content).toBe('hello world') }) test('extracts content field from object payload', () => { const result = normalizePayload('assistant', { content: 'direct content' }) expect(result.content).toBe('direct content') }) test('extracts message.content string from object payload', () => { const result = normalizePayload('assistant', { message: { content: 'msg content' }, }) expect(result.content).toBe('msg content') }) test('extracts text blocks from message.content array', () => { const payload = { message: { content: [ { type: 'text', text: 'Hello ' }, { type: 'text', text: 'World' }, ], }, } const result = normalizePayload('assistant', payload) expect(result.content).toBe('Hello World') }) test('ignores non-text blocks in message.content array', () => { const payload = { message: { content: [ { type: 'image', url: 'http://example.com/img.png' }, { type: 'text', text: 'only this' }, ], }, } const result = normalizePayload('assistant', payload) expect(result.content).toBe('only this') }) test('returns empty string when no extractable content', () => { const result = normalizePayload('assistant', { foo: 'bar' }) expect(result.content).toBe('') }) test('prefers direct content over message.content', () => { const result = normalizePayload('assistant', { content: 'direct', message: { content: 'nested' }, }) expect(result.content).toBe('direct') }) }) // ============================================================================= // normalizePayload — field preservation // ============================================================================= describe('normalizePayload — field preservation', () => { test('preserves raw payload', () => { const payload = { content: 'test', extra: true } const result = normalizePayload('assistant', payload) expect(result.raw).toBe(payload) }) test('preserves uuid field', () => { const result = normalizePayload('assistant', { uuid: 'u-123' }) expect(result.uuid).toBe('u-123') }) test('does not preserve uuid when empty string', () => { const result = normalizePayload('assistant', { uuid: '' }) expect(result.uuid).toBeUndefined() }) test('preserves isSynthetic boolean', () => { const result = normalizePayload('assistant', { isSynthetic: true }) expect(result.isSynthetic).toBe(true) }) test('preserves status string', () => { const result = normalizePayload('assistant', { status: 'running' }) expect(result.status).toBe('running') }) test('preserves subtype string', () => { const result = normalizePayload('assistant', { subtype: 'progress' }) expect(result.subtype).toBe('progress') }) test('preserves tool_name from tool_name field', () => { const result = normalizePayload('tool', { tool_name: 'bash' }) expect(result.tool_name).toBe('bash') }) test('preserves tool_name from name field', () => { const result = normalizePayload('tool', { name: 'read' }) expect(result.tool_name).toBe('read') }) test('preserves tool_input from tool_input field', () => { const input = { command: 'ls' } const result = normalizePayload('tool', { tool_input: input }) expect(result.tool_input).toEqual(input) }) test('preserves tool_input from input field', () => { const input = { path: '/tmp' } const result = normalizePayload('tool', { input }) expect(result.tool_input).toEqual(input) }) test('preserves request_id', () => { const result = normalizePayload('permission', { request_id: 'req-1' }) expect(result.request_id).toBe('req-1') }) test('preserves request object', () => { const req = { subtype: 'permission' } const result = normalizePayload('permission', { request: req }) expect(result.request).toEqual(req) }) test('preserves approved field', () => { const result = normalizePayload('permission', { approved: true }) expect(result.approved).toBe(true) }) test('preserves updated_input', () => { const input = { command: 'rm -rf' } const result = normalizePayload('permission', { updated_input: input }) expect(result.updated_input).toEqual(input) }) test('preserves message field for backward compat', () => { const msg = { role: 'user', content: 'hi' } const result = normalizePayload('assistant', { message: msg }) expect(result.message).toEqual(msg) }) }) // ============================================================================= // normalizePayload — task_state special handling // ============================================================================= describe('normalizePayload — task_state type', () => { test('preserves task_list_id (snake_case)', () => { const result = normalizePayload('task_state', { task_list_id: 'tl-1' }) expect(result.task_list_id).toBe('tl-1') }) test('preserves taskListId (camelCase)', () => { const result = normalizePayload('task_state', { taskListId: 'tl-2' }) expect(result.taskListId).toBe('tl-2') }) test('preserves tasks array', () => { const tasks = [{ id: 't1', title: 'Task 1' }] const result = normalizePayload('task_state', { tasks }) expect(result.tasks).toEqual(tasks) }) test('does not preserve task fields for non-task_state type', () => { const result = normalizePayload('assistant', { task_list_id: 'tl-1', taskListId: 'tl-2', tasks: [], }) expect(result.task_list_id).toBeUndefined() expect(result.taskListId).toBeUndefined() expect(result.tasks).toBeUndefined() }) }) // ============================================================================= // normalizePayload — SDK system events (task_started/progress/notification) // ============================================================================= describe('normalizePayload — SDK system task events', () => { test('preserves task_started fields', () => { const result = normalizePayload('system', { subtype: 'task_started', task_id: 'task-1', tool_use_id: 'tu-1', description: 'Investigate auth flow', task_type: 'local_agent', workflow_name: 'team', prompt: 'find the auth bug', }) expect(result.subtype).toBe('task_started') expect(result.task_id).toBe('task-1') expect(result.tool_use_id).toBe('tu-1') expect(result.description).toBe('Investigate auth flow') expect(result.task_type).toBe('local_agent') expect(result.workflow_name).toBe('team') expect(result.prompt).toBe('find the auth bug') }) test('preserves task_progress usage and summary', () => { const usage = { total_tokens: 1234, tool_uses: 5, duration_ms: 60000 } const result = normalizePayload('system', { subtype: 'task_progress', task_id: 'task-1', usage, last_tool_name: 'Read', summary: 'Reading auth files', }) expect(result.subtype).toBe('task_progress') expect(result.task_id).toBe('task-1') expect(result.usage).toEqual(usage) expect(result.last_tool_name).toBe('Read') expect(result.summary).toBe('Reading auth files') }) test('preserves task_notification terminal fields', () => { const usage = { total_tokens: 5000, tool_uses: 12, duration_ms: 120000 } const result = normalizePayload('system', { subtype: 'task_notification', task_id: 'task-1', status: 'completed', output_file: '/tmp/out.json', summary: 'Done: found bug in line 42', usage, }) expect(result.subtype).toBe('task_notification') expect(result.task_id).toBe('task-1') expect(result.status).toBe('completed') expect(result.output_file).toBe('/tmp/out.json') expect(result.summary).toBe('Done: found bug in line 42') expect(result.usage).toEqual(usage) }) test('does not preserve SDK task fields for non-task system subtypes', () => { const result = normalizePayload('system', { subtype: 'init', task_id: 'should-not-leak', }) expect(result.task_id).toBeUndefined() expect(result.description).toBeUndefined() expect(result.usage).toBeUndefined() }) test('does not preserve SDK task fields for non-system types', () => { const result = normalizePayload('assistant', { subtype: 'task_started', task_id: 'task-1', description: 'should-not-leak', }) expect(result.task_id).toBeUndefined() expect(result.description).toBeUndefined() }) })