65 lines
2.4 KiB
TypeScript
65 lines
2.4 KiB
TypeScript
// Pins the contract that makes streaming cheap: updateMessageAtIndex mutates the
|
|
// existing message object instead of replacing it.
|
|
//
|
|
// Replacing it (`{ ...old, ...updates }`) changes the array slot, which
|
|
// invalidates every consumer that merely walks the list - ChatMessages'
|
|
// `displayMessages` rebuilds entries for EVERY message in the conversation. That
|
|
// made per-token cost scale with conversation length (1.26ms at 1 prior message
|
|
// -> 3.07ms at 40). Mutating in place keeps it flat.
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import { conversationsStore } from '$lib/stores/conversations.svelte';
|
|
import type { DatabaseMessage } from '$lib/types';
|
|
import { MessageRole } from '$lib/enums';
|
|
|
|
function makeMessage(id: string): DatabaseMessage {
|
|
return {
|
|
id,
|
|
convId: 'c1',
|
|
type: 'text',
|
|
timestamp: 0,
|
|
role: MessageRole.ASSISTANT,
|
|
content: '',
|
|
parent: null,
|
|
children: []
|
|
} as DatabaseMessage;
|
|
}
|
|
|
|
describe('conversationsStore.updateMessageAtIndex', () => {
|
|
it('mutates in place, preserving object identity', () => {
|
|
conversationsStore.activeMessages = [makeMessage('a'), makeMessage('b')];
|
|
const before = conversationsStore.activeMessages[1];
|
|
|
|
conversationsStore.updateMessageAtIndex(1, { content: 'hello' });
|
|
|
|
expect(conversationsStore.activeMessages[1].content).toBe('hello');
|
|
expect(conversationsStore.activeMessages[1]).toBe(before);
|
|
|
|
conversationsStore.activeMessages = [];
|
|
});
|
|
|
|
it('leaves other messages and unrelated fields untouched', () => {
|
|
conversationsStore.activeMessages = [makeMessage('a'), makeMessage('b')];
|
|
const untouched = conversationsStore.activeMessages[0];
|
|
|
|
conversationsStore.updateMessageAtIndex(1, { content: 'x', model: 'm1' });
|
|
|
|
expect(conversationsStore.activeMessages[0]).toBe(untouched);
|
|
expect(conversationsStore.activeMessages[0].content).toBe('');
|
|
expect(conversationsStore.activeMessages[1].model).toBe('m1');
|
|
expect(conversationsStore.activeMessages[1].id).toBe('b');
|
|
|
|
conversationsStore.activeMessages = [];
|
|
});
|
|
|
|
it('is a no-op for an index of -1 or out of range', () => {
|
|
conversationsStore.activeMessages = [makeMessage('a')];
|
|
|
|
expect(() => conversationsStore.updateMessageAtIndex(-1, { content: 'x' })).not.toThrow();
|
|
expect(() => conversationsStore.updateMessageAtIndex(9, { content: 'x' })).not.toThrow();
|
|
expect(conversationsStore.activeMessages[0].content).toBe('');
|
|
|
|
conversationsStore.activeMessages = [];
|
|
});
|
|
});
|