Felipe97/llama-cpp-compiled
01.2k
1// Field updates to the active conversation must keep the object identity2// stable: effects that track the identity ( the chat screen's sibling-info3// refresh ) refire on every identity change, which used to trigger a full4// message refetch on every send and tool result.5 6import { beforeEach, describe, expect, it, vi } from 'vitest';7 8vi.mock('$lib/services/database.service', () => ({9 DatabaseService: {10 getConversation: vi.fn(),11 getConversationMessages: vi.fn(),12 updateConversation: vi.fn(),13 updateCurrentNode: vi.fn()14 }15}));16 17import { DatabaseService } from '$lib/services/database.service';18import { conversationsStore } from '$lib/stores/conversations/index.svelte';19import type { DatabaseConversation, DatabaseMessage } from '$lib/types/database';20 21const getConversationMock = vi.mocked(DatabaseService.getConversation);22const getMessagesMock = vi.mocked(DatabaseService.getConversationMessages);23const updateCurrentNodeMock = vi.mocked(DatabaseService.updateCurrentNode);24 25function makeConversation(overrides: Partial<DatabaseConversation> = {}): DatabaseConversation {26 return {27 currNode: 'node-1',28 id: 'conv-1',29 lastModified: 1000,30 name: 'conversation',31 ...overrides32 };33}34 35async function loadActive(conversation: DatabaseConversation, messages: DatabaseMessage[]) {36 getConversationMock.mockResolvedValue(conversation);37 getMessagesMock.mockResolvedValue(messages);38 39 expect(await conversationsStore.loadConversation(conversation.id)).toBe(true);40}41 42beforeEach(() => {43 getConversationMock.mockReset();44 getMessagesMock.mockReset();45 updateCurrentNodeMock.mockReset();46 updateCurrentNodeMock.mockResolvedValue(undefined);47 vi.mocked(DatabaseService.updateConversation).mockReset();48 vi.mocked(DatabaseService.updateConversation).mockResolvedValue(undefined);49});50 51describe('active conversation identity', () => {52 it('hands the load read off exactly once', async () => {53 await loadActive(makeConversation(), []);54 55 expect(conversationsStore.consumeLastLoadedMessages('conv-1')).toEqual([]);56 // a second consume is a miss: branch actions must fall back to a refetch57 expect(conversationsStore.consumeLastLoadedMessages('conv-1')).toBeNull();58 });59 60 it('writes currNode in place on updateCurrentNode', async () => {61 await loadActive(makeConversation(), []);62 const before = conversationsStore.activeConversation;63 64 await conversationsStore.updateCurrentNode('node-2');65 66 expect(conversationsStore.activeConversation).toBe(before);67 expect(conversationsStore.activeConversation?.currNode).toBe('node-2');68 });69 70 it('writes renamed and pinned fields in place on applyConversationUpdate', async () => {71 await loadActive(makeConversation(), []);72 const before = conversationsStore.activeConversation;73 74 conversationsStore.applyConversationUpdate('conv-1', { name: 'renamed', pinned: true });75 76 expect(conversationsStore.activeConversation).toBe(before);77 expect(conversationsStore.activeConversation?.name).toBe('renamed');78 expect(conversationsStore.activeConversation?.pinned).toBe(true);79 });80 81 it('writes lastModified in place on updateConversationTimestamp', async () => {82 await loadActive(makeConversation(), []);83 const before = conversationsStore.activeConversation;84 85 conversationsStore.updateConversationTimestamp('conv-1');86 87 expect(conversationsStore.activeConversation).toBe(before);88 expect(conversationsStore.activeConversation?.lastModified).toBeGreaterThan(1000);89 });90});91 