mirror of https://github.com/buster-so/buster.git
191 lines
6.6 KiB
TypeScript
191 lines
6.6 KiB
TypeScript
import {
|
|
getLatestMessageForChat,
|
|
updateMessageReasoning,
|
|
updateMessageStreamingFields,
|
|
} from '@buster/database';
|
|
import { createTestMessageWithContext } from '@buster/test-utils';
|
|
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
|
|
import { cleanupTestEnvironment, setupTestEnvironment } from '../../src/envHelpers/env-helpers';
|
|
|
|
describe('Message Update Helpers', () => {
|
|
beforeEach(async () => {
|
|
await setupTestEnvironment();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await cleanupTestEnvironment();
|
|
});
|
|
|
|
describe('updateMessageReasoning', () => {
|
|
test('successfully updates reasoning JSONB field', async () => {
|
|
const { messageId, chatId } = await createTestMessageWithContext();
|
|
|
|
const newReasoning = {
|
|
steps: ['analyze data', 'identify patterns', 'generate insights'],
|
|
conclusion: 'Analysis complete',
|
|
confidence: 0.89,
|
|
metadata: { duration: '45s' },
|
|
};
|
|
|
|
const result = await updateMessageReasoning(messageId, newReasoning);
|
|
|
|
expect(result.success).toBe(true);
|
|
|
|
// Verify the update was persisted
|
|
const updatedMessage = await getLatestMessageForChat(chatId);
|
|
expect(updatedMessage?.reasoning).toEqual(newReasoning);
|
|
});
|
|
|
|
test('handles simple reasoning object', async () => {
|
|
const { messageId, chatId } = await createTestMessageWithContext();
|
|
|
|
const simpleReasoning = {
|
|
thought: 'This is a simple reasoning step',
|
|
};
|
|
|
|
const result = await updateMessageReasoning(messageId, simpleReasoning);
|
|
|
|
expect(result.success).toBe(true);
|
|
|
|
const updatedMessage = await getLatestMessageForChat(chatId);
|
|
expect(updatedMessage?.reasoning).toEqual(simpleReasoning);
|
|
});
|
|
|
|
test('throws error when trying to set reasoning to null', async () => {
|
|
const { messageId } = await createTestMessageWithContext();
|
|
|
|
await expect(updateMessageReasoning(messageId, null)).rejects.toThrow(
|
|
'Reasoning cannot be null - database constraint violation'
|
|
);
|
|
});
|
|
|
|
test('throws error when trying to set reasoning to undefined', async () => {
|
|
const { messageId } = await createTestMessageWithContext();
|
|
|
|
await expect(updateMessageReasoning(messageId, undefined)).rejects.toThrow(
|
|
'Reasoning cannot be null - database constraint violation'
|
|
);
|
|
});
|
|
|
|
test('throws error for non-existent message ID', async () => {
|
|
const nonExistentId = '00000000-0000-0000-0000-000000000000';
|
|
|
|
await expect(updateMessageReasoning(nonExistentId, { thought: 'test' })).rejects.toThrow(
|
|
`Message not found or has been deleted: ${nonExistentId}`
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('updateMessageStreamingFields', () => {
|
|
test('successfully updates both responseMessages and reasoning in single query', async () => {
|
|
const { messageId, chatId } = await createTestMessageWithContext();
|
|
|
|
const newResponseMessages = {
|
|
content: 'Streaming response',
|
|
tokens: 100,
|
|
};
|
|
|
|
const newReasoning = {
|
|
step: 'processing',
|
|
progress: 0.75,
|
|
};
|
|
|
|
const result = await updateMessageStreamingFields(
|
|
messageId,
|
|
newResponseMessages,
|
|
newReasoning
|
|
);
|
|
|
|
expect(result.success).toBe(true);
|
|
|
|
// Verify both fields were updated
|
|
const updatedMessage = await getLatestMessageForChat(chatId);
|
|
expect(updatedMessage?.responseMessages).toEqual(newResponseMessages);
|
|
expect(updatedMessage?.reasoning).toEqual(newReasoning);
|
|
});
|
|
|
|
test('handles large streaming data efficiently', async () => {
|
|
const { messageId, chatId } = await createTestMessageWithContext();
|
|
|
|
// Simulate large streaming data
|
|
const largeResponseMessages = {
|
|
content: 'A'.repeat(10000), // Large content
|
|
chunks: Array.from({ length: 1000 }, (_, i) => ({ id: i, data: `chunk-${i}` })),
|
|
};
|
|
|
|
const largeReasoning = {
|
|
steps: Array.from({ length: 500 }, (_, i) => `reasoning-step-${i}`),
|
|
detailed_analysis: 'B'.repeat(5000),
|
|
};
|
|
|
|
const result = await updateMessageStreamingFields(
|
|
messageId,
|
|
largeResponseMessages,
|
|
largeReasoning
|
|
);
|
|
|
|
expect(result.success).toBe(true);
|
|
|
|
const updatedMessage = await getLatestMessageForChat(chatId);
|
|
expect(updatedMessage?.responseMessages).toEqual(largeResponseMessages);
|
|
expect(updatedMessage?.reasoning).toEqual(largeReasoning);
|
|
});
|
|
|
|
test('throws error when reasoning is null', async () => {
|
|
const { messageId } = await createTestMessageWithContext();
|
|
|
|
const responseMessages = { content: 'valid response' };
|
|
const reasoning = null;
|
|
|
|
await expect(
|
|
updateMessageStreamingFields(messageId, responseMessages, reasoning)
|
|
).rejects.toThrow('Reasoning cannot be null - database constraint violation');
|
|
});
|
|
|
|
test('throws error when reasoning is undefined', async () => {
|
|
const { messageId } = await createTestMessageWithContext();
|
|
|
|
const responseMessages = { content: 'valid response' };
|
|
const reasoning = undefined;
|
|
|
|
await expect(
|
|
updateMessageStreamingFields(messageId, responseMessages, reasoning)
|
|
).rejects.toThrow('Reasoning cannot be null - database constraint violation');
|
|
});
|
|
|
|
test('overwrites previous data completely', async () => {
|
|
const { messageId, chatId } = await createTestMessageWithContext();
|
|
|
|
// First update
|
|
const initialResponse = { content: 'initial', version: 1 };
|
|
const initialReasoning = { step: 'initial', data: 'old' };
|
|
|
|
await updateMessageStreamingFields(messageId, initialResponse, initialReasoning);
|
|
|
|
// Second update should completely replace
|
|
const newResponse = { content: 'updated', version: 2, newField: 'added' };
|
|
const newReasoning = { step: 'updated', different: 'structure' };
|
|
|
|
const result = await updateMessageStreamingFields(messageId, newResponse, newReasoning);
|
|
|
|
expect(result.success).toBe(true);
|
|
|
|
const updatedMessage = await getLatestMessageForChat(chatId);
|
|
expect(updatedMessage?.responseMessages).toEqual(newResponse);
|
|
expect(updatedMessage?.reasoning).toEqual(newReasoning);
|
|
|
|
// Verify old fields are gone
|
|
expect(updatedMessage?.responseMessages).not.toHaveProperty('version', 1);
|
|
expect(updatedMessage?.reasoning).not.toHaveProperty('data');
|
|
});
|
|
|
|
test('throws error for non-existent message ID', async () => {
|
|
const nonExistentId = '00000000-0000-0000-0000-000000000000';
|
|
|
|
await expect(
|
|
updateMessageStreamingFields(nonExistentId, { content: 'test' }, { step: 'test' })
|
|
).rejects.toThrow(`Message not found or has been deleted: ${nonExistentId}`);
|
|
});
|
|
});
|
|
});
|