modify metrics finish

This commit is contained in:
dal 2025-08-12 11:33:39 -06:00
parent eb68b4edff
commit cf3dc3387b
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
1 changed files with 68 additions and 132 deletions

View File

@ -1,10 +1,10 @@
import { updateMessageFields } from '@buster/database'; import { updateMessageEntries } from '@buster/database';
import { beforeEach, describe, expect, it, vi } from 'vitest'; import { beforeEach, describe, expect, it, vi } from 'vitest';
import { createModifyMetricsFinish } from './modify-metrics-finish'; import { createModifyMetricsFinish } from './modify-metrics-finish';
import type { ModifyMetricsInput, ModifyMetricsState } from './modify-metrics-tool'; import type { ModifyMetricsInput, ModifyMetricsState } from './modify-metrics-tool';
vi.mock('@buster/database', () => ({ vi.mock('@buster/database', () => ({
updateMessageFields: vi.fn(), updateMessageEntries: vi.fn(),
})); }));
describe('createModifyMetricsFinish', () => { describe('createModifyMetricsFinish', () => {
@ -24,8 +24,6 @@ describe('createModifyMetricsFinish', () => {
argsText: '', argsText: '',
files: [], files: [],
toolCallId: 'tool-123', toolCallId: 'tool-123',
reasoningEntryId: 'reasoning-123',
processingStartTime: Date.now() - 1000, // Started 1 second ago
}; };
context = { context = {
userId: 'user-123', userId: 'user-123',
@ -46,9 +44,12 @@ describe('createModifyMetricsFinish', () => {
}; };
const finishHandler = createModifyMetricsFinish(context, state); const finishHandler = createModifyMetricsFinish(context, state);
await finishHandler(input); await finishHandler({ input, toolCallId: 'tool-123', messages: [] });
expect(state.parsedArgs).toEqual(input); // Verify files were updated with input data
expect(state.files?.map((f) => ({ id: f.id, yml_content: f.yml_content }))).toEqual(
input.files
);
}); });
it('should update state files with final data', async () => { it('should update state files with final data', async () => {
@ -60,80 +61,87 @@ describe('createModifyMetricsFinish', () => {
}; };
const finishHandler = createModifyMetricsFinish(context, state); const finishHandler = createModifyMetricsFinish(context, state);
await finishHandler(input); await finishHandler({ input, toolCallId: 'tool-123', messages: [] });
expect(state.files).toHaveLength(2); expect(state.files).toHaveLength(2);
expect(state.files[0]).toEqual({ expect(state.files?.[0]).toMatchObject({
id: 'metric-1', id: 'metric-1',
yml_content: 'content1', yml_content: 'content1',
name: undefined, file_type: 'metric',
status: 'processing', status: 'loading',
}); });
expect(state.files[1]).toEqual({ expect(state.files?.[1]).toMatchObject({
id: 'metric-2', id: 'metric-2',
yml_content: 'content2', yml_content: 'content2',
name: undefined, file_type: 'metric',
status: 'processing', status: 'loading',
}); });
}); });
it('should update database when messageId and reasoningEntryId exist', async () => { it('should update database when messageId and toolCallId exist', async () => {
const input: ModifyMetricsInput = { const input: ModifyMetricsInput = {
files: [{ id: 'metric-1', yml_content: 'content1' }], files: [{ id: 'metric-1', yml_content: 'content1' }],
}; };
const finishHandler = createModifyMetricsFinish(context, state); const finishHandler = createModifyMetricsFinish(context, state);
await finishHandler(input); await finishHandler({ input, toolCallId: 'tool-123', messages: [] });
expect(updateMessageFields).toHaveBeenCalledWith('msg-123', { expect(updateMessageEntries).toHaveBeenCalledWith({
reasoning: expect.arrayContaining([ messageId: 'msg-123',
expect.objectContaining({ mode: 'update',
id: 'tool-123', responseEntry: expect.objectContaining({
type: 'files', id: 'tool-123',
title: 'Modifying metrics...', type: 'files',
status: 'loading', title: 'Modifying metrics...',
file_ids: ['metric-1'], status: 'loading',
}), file_ids: ['metric-1'],
]), files: expect.any(Object),
rawLlmMessages: expect.arrayContaining([ }),
expect.objectContaining({ rawLlmMessage: expect.objectContaining({
type: 'tool-call', role: 'assistant',
toolCallId: 'tool-123', content: expect.arrayContaining([
toolName: 'modify-metrics-file', expect.objectContaining({
args: input, type: 'tool-call',
}), toolCallId: 'tool-123',
]), toolName: 'modifyMetrics',
input: {
files: [{ id: 'metric-1', yml_content: 'content1' }],
},
}),
]),
}),
}); });
}); });
it('should not update database when messageId is missing', async () => { it('should not update database when messageId is missing', async () => {
context.messageId = undefined; const contextWithoutMessageId = { ...context };
delete contextWithoutMessageId.messageId;
const input: ModifyMetricsInput = { const input: ModifyMetricsInput = {
files: [{ id: 'metric-1', yml_content: 'content1' }], files: [{ id: 'metric-1', yml_content: 'content1' }],
}; };
const finishHandler = createModifyMetricsFinish(context, state); const finishHandler = createModifyMetricsFinish(contextWithoutMessageId, state);
await finishHandler(input); await finishHandler({ input, toolCallId: 'tool-123', messages: [] });
expect(updateMessageFields).not.toHaveBeenCalled(); expect(updateMessageEntries).not.toHaveBeenCalled();
}); });
it('should not update database when reasoningEntryId is missing', async () => { it('should not update database when toolCallId is missing in state', async () => {
state.reasoningEntryId = undefined; state.toolCallId = undefined;
const input: ModifyMetricsInput = { const input: ModifyMetricsInput = {
files: [{ id: 'metric-1', yml_content: 'content1' }], files: [{ id: 'metric-1', yml_content: 'content1' }],
}; };
const finishHandler = createModifyMetricsFinish(context, state); const finishHandler = createModifyMetricsFinish(context, state);
await finishHandler(input); await finishHandler({ input, toolCallId: 'tool-123', messages: [] });
expect(updateMessageFields).not.toHaveBeenCalled(); expect(updateMessageEntries).not.toHaveBeenCalled();
}); });
it('should handle database update errors gracefully', async () => { it('should handle database update errors gracefully', async () => {
(updateMessageFields as any).mockRejectedValue(new Error('Database error')); vi.mocked(updateMessageEntries).mockRejectedValue(new Error('Database error'));
const input: ModifyMetricsInput = { const input: ModifyMetricsInput = {
files: [{ id: 'metric-1', yml_content: 'content1' }], files: [{ id: 'metric-1', yml_content: 'content1' }],
@ -142,54 +150,18 @@ describe('createModifyMetricsFinish', () => {
const finishHandler = createModifyMetricsFinish(context, state); const finishHandler = createModifyMetricsFinish(context, state);
// Should not throw // Should not throw
await expect(finishHandler(input)).resolves.not.toThrow(); await expect(
finishHandler({ input, toolCallId: 'tool-123', messages: [] })
).resolves.not.toThrow();
// State should still be updated // State should still be updated
expect(state.parsedArgs).toEqual(input);
expect(state.files).toHaveLength(1); expect(state.files).toHaveLength(1);
}); expect(state.files?.map((f) => ({ id: f.id, yml_content: f.yml_content }))).toEqual(
input.files
it('should log processing time when processingStartTime exists', async () => {
const consoleSpy = vi.spyOn(console, 'info').mockImplementation(() => {});
const input: ModifyMetricsInput = {
files: [{ id: 'metric-1', yml_content: 'content1' }],
};
const finishHandler = createModifyMetricsFinish(context, state);
await finishHandler(input);
expect(consoleSpy).toHaveBeenCalledWith(
'[modify-metrics] Input processing time',
expect.objectContaining({
processingTimeMs: expect.any(Number),
processingTimeSeconds: expect.any(String),
})
); );
consoleSpy.mockRestore();
}); });
it('should not log processing time when processingStartTime is missing', async () => { // Tests for processingStartTime removed as it's not part of ModifyMetricsState
const consoleSpy = vi.spyOn(console, 'info').mockImplementation(() => {});
state.processingStartTime = undefined;
const input: ModifyMetricsInput = {
files: [{ id: 'metric-1', yml_content: 'content1' }],
};
const finishHandler = createModifyMetricsFinish(context, state);
await finishHandler(input);
const calls = consoleSpy.mock.calls;
const processingTimeCall = calls.find(
(call) => call[0] === '[modify-metrics] Input processing time'
);
expect(processingTimeCall).toBeUndefined();
consoleSpy.mockRestore();
});
it('should handle empty files array', async () => { it('should handle empty files array', async () => {
const input: ModifyMetricsInput = { const input: ModifyMetricsInput = {
@ -197,69 +169,33 @@ describe('createModifyMetricsFinish', () => {
}; };
const finishHandler = createModifyMetricsFinish(context, state); const finishHandler = createModifyMetricsFinish(context, state);
await finishHandler(input); await finishHandler({ input, toolCallId: 'tool-123', messages: [] });
expect(state.files).toHaveLength(0); expect(state.files).toHaveLength(0);
expect(state.parsedArgs).toEqual(input);
expect(updateMessageFields).toHaveBeenCalledWith('msg-123', { // When files array is empty, no database update happens
reasoning: expect.arrayContaining([ expect(updateMessageEntries).not.toHaveBeenCalled();
expect.objectContaining({
file_ids: [],
files: {},
}),
]),
rawLlmMessages: expect.any(Array),
});
}); });
it('should use fallback toolCallId when not set in state', async () => { it('should set toolCallId on state when provided', async () => {
state.toolCallId = undefined;
const input: ModifyMetricsInput = { const input: ModifyMetricsInput = {
files: [{ id: 'metric-1', yml_content: 'content1' }], files: [{ id: 'metric-1', yml_content: 'content1' }],
}; };
const finishHandler = createModifyMetricsFinish(context, state); const finishHandler = createModifyMetricsFinish(context, state);
await finishHandler(input); await finishHandler({ input, toolCallId: 'tool-123', messages: [] });
expect(updateMessageFields).toHaveBeenCalledWith('msg-123', { // The toolCallId is set on state from the original value
reasoning: expect.arrayContaining([ expect(state.toolCallId).toBe('tool-123');
expect.objectContaining({
id: expect.stringMatching(/^modify-metrics-\d+$/),
}),
]),
rawLlmMessages: expect.arrayContaining([
expect.objectContaining({
toolCallId: expect.stringMatching(/^modify-metrics-\d+$/),
}),
]),
});
});
it('should log correct information', async () => { // Database update should happen with the toolCallId
const consoleSpy = vi.spyOn(console, 'info').mockImplementation(() => {}); expect(updateMessageEntries).toHaveBeenCalledWith(
const input: ModifyMetricsInput = {
files: [
{ id: 'metric-1', yml_content: 'content1' },
{ id: 'metric-2', yml_content: 'content2' },
],
};
const finishHandler = createModifyMetricsFinish(context, state);
await finishHandler(input);
expect(consoleSpy).toHaveBeenCalledWith(
'[modify-metrics] Input fully available',
expect.objectContaining({ expect.objectContaining({
fileCount: 2,
fileIds: ['metric-1', 'metric-2'],
messageId: 'msg-123', messageId: 'msg-123',
timestamp: expect.any(String), mode: 'update',
}) })
); );
consoleSpy.mockRestore();
}); });
// Test for logging removed as implementation doesn't log this message
}); });