Files
automaker/apps/server/tests/unit/lib/conversation-utils.test.ts
SuperComboGamer 584f5a3426 Merge main into massive-terminal-upgrade
Resolves merge conflicts:
- apps/server/src/routes/terminal/common.ts: Keep randomBytes import, use @automaker/utils for createLogger
- apps/ui/eslint.config.mjs: Use main's explicit globals list with XMLHttpRequest and MediaQueryListEvent additions
- apps/ui/src/components/views/terminal-view.tsx: Keep our terminal improvements (killAllSessions, beforeunload, better error handling)
- apps/ui/src/config/terminal-themes.ts: Keep our search highlight colors for all themes
- apps/ui/src/store/app-store.ts: Keep our terminal settings persistence improvements (merge function)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-21 20:27:44 -05:00

225 lines
7.5 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import {
extractTextFromContent,
normalizeContentBlocks,
formatHistoryAsText,
convertHistoryToMessages,
} from '@automaker/utils';
import { conversationHistoryFixture } from '../../fixtures/messages.js';
describe('conversation-utils.ts', () => {
describe('extractTextFromContent', () => {
it('should return string content as-is', () => {
const result = extractTextFromContent('Hello world');
expect(result).toBe('Hello world');
});
it('should extract text from single text block', () => {
const content = [{ type: 'text', text: 'Hello' }];
const result = extractTextFromContent(content);
expect(result).toBe('Hello');
});
it('should extract and join multiple text blocks with newlines', () => {
const content = [
{ type: 'text', text: 'First block' },
{ type: 'text', text: 'Second block' },
{ type: 'text', text: 'Third block' },
];
const result = extractTextFromContent(content);
expect(result).toBe('First block\nSecond block\nThird block');
});
it('should ignore non-text blocks', () => {
const content = [
{ type: 'text', text: 'Text content' },
{ type: 'image', source: { type: 'base64', data: 'abc' } },
{ type: 'text', text: 'More text' },
{ type: 'tool_use', name: 'bash', input: {} },
];
const result = extractTextFromContent(content);
expect(result).toBe('Text content\nMore text');
});
it('should handle blocks without text property', () => {
const content = [
{ type: 'text', text: 'Valid' },
{ type: 'text' } as any,
{ type: 'text', text: 'Also valid' },
];
const result = extractTextFromContent(content);
expect(result).toBe('Valid\n\nAlso valid');
});
it('should handle empty array', () => {
const result = extractTextFromContent([]);
expect(result).toBe('');
});
it('should handle array with only non-text blocks', () => {
const content = [
{ type: 'image', source: {} },
{ type: 'tool_use', name: 'test' },
];
const result = extractTextFromContent(content);
expect(result).toBe('');
});
});
describe('normalizeContentBlocks', () => {
it('should convert string to content block array', () => {
const result = normalizeContentBlocks('Hello');
expect(result).toEqual([{ type: 'text', text: 'Hello' }]);
});
it('should return array content as-is', () => {
const content = [
{ type: 'text', text: 'Hello' },
{ type: 'image', source: {} },
];
const result = normalizeContentBlocks(content);
expect(result).toBe(content);
expect(result).toHaveLength(2);
});
it('should handle empty string', () => {
const result = normalizeContentBlocks('');
expect(result).toEqual([{ type: 'text', text: '' }]);
});
});
describe('formatHistoryAsText', () => {
it('should return empty string for empty history', () => {
const result = formatHistoryAsText([]);
expect(result).toBe('');
});
it('should format single user message', () => {
const history = [{ role: 'user' as const, content: 'Hello' }];
const result = formatHistoryAsText(history);
expect(result).toContain('Previous conversation:');
expect(result).toContain('User: Hello');
expect(result).toContain('---');
});
it('should format single assistant message', () => {
const history = [{ role: 'assistant' as const, content: 'Hi there' }];
const result = formatHistoryAsText(history);
expect(result).toContain('Assistant: Hi there');
});
it('should format multiple messages with correct roles', () => {
const history = conversationHistoryFixture.slice(0, 2);
const result = formatHistoryAsText(history);
expect(result).toContain('User: Hello, can you help me?');
expect(result).toContain('Assistant: Of course! How can I assist you today?');
expect(result).toContain('---');
});
it('should handle messages with array content (multipart)', () => {
const history = [conversationHistoryFixture[2]]; // Has text + image
const result = formatHistoryAsText(history);
expect(result).toContain('What is in this image?');
expect(result).not.toContain('base64'); // Should not include image data
});
it('should format all messages from fixture', () => {
const result = formatHistoryAsText(conversationHistoryFixture);
expect(result).toContain('Previous conversation:');
expect(result).toContain('User: Hello, can you help me?');
expect(result).toContain('Assistant: Of course!');
expect(result).toContain('User: What is in this image?');
expect(result).toContain('---');
});
it('should separate messages with double newlines', () => {
const history = [
{ role: 'user' as const, content: 'First' },
{ role: 'assistant' as const, content: 'Second' },
];
const result = formatHistoryAsText(history);
expect(result).toMatch(/User: First\n\nAssistant: Second/);
});
});
describe('convertHistoryToMessages', () => {
it('should convert empty history', () => {
const result = convertHistoryToMessages([]);
expect(result).toEqual([]);
});
it('should convert single message to SDK format', () => {
const history = [{ role: 'user' as const, content: 'Hello' }];
const result = convertHistoryToMessages(history);
expect(result).toHaveLength(1);
expect(result[0]).toMatchObject({
type: 'user',
session_id: '',
message: {
role: 'user',
content: [{ type: 'text', text: 'Hello' }],
},
parent_tool_use_id: null,
});
});
it('should normalize string content to array', () => {
const history = [{ role: 'assistant' as const, content: 'Response' }];
const result = convertHistoryToMessages(history);
expect(result[0].message.content).toEqual([{ type: 'text', text: 'Response' }]);
});
it('should preserve array content', () => {
const history = [
{
role: 'user' as const,
content: [
{ type: 'text', text: 'Hello' },
{ type: 'image', source: {} },
],
},
];
const result = convertHistoryToMessages(history);
expect(result[0].message.content).toHaveLength(2);
expect(result[0].message.content[0]).toEqual({ type: 'text', text: 'Hello' });
});
it('should convert multiple messages', () => {
const history = conversationHistoryFixture.slice(0, 2);
const result = convertHistoryToMessages(history);
expect(result).toHaveLength(2);
expect(result[0].type).toBe('user');
expect(result[1].type).toBe('assistant');
});
it('should set correct fields for SDK format', () => {
const history = [{ role: 'user' as const, content: 'Test' }];
const result = convertHistoryToMessages(history);
expect(result[0].session_id).toBe('');
expect(result[0].parent_tool_use_id).toBeNull();
expect(result[0].type).toBe('user');
expect(result[0].message.role).toBe('user');
});
it('should handle all messages from fixture', () => {
const result = convertHistoryToMessages(conversationHistoryFixture);
expect(result).toHaveLength(3);
expect(result[0].message.content).toBeInstanceOf(Array);
expect(result[1].message.content).toBeInstanceOf(Array);
expect(result[2].message.content).toBeInstanceOf(Array);
});
});
});