diff --git a/web-app/src/hooks/__tests__/useThreads.test.ts b/web-app/src/hooks/__tests__/useThreads.test.ts index 118b35112..3c7915206 100644 --- a/web-app/src/hooks/__tests__/useThreads.test.ts +++ b/web-app/src/hooks/__tests__/useThreads.test.ts @@ -11,15 +11,22 @@ vi.mock('@/services/threads', () => ({ // Mock ulid vi.mock('ulidx', () => ({ - ulid: vi.fn(() => 'test-ulid-123') + ulid: vi.fn(() => 'test-ulid-123'), })) // Mock fzf vi.mock('fzf', () => ({ Fzf: vi.fn(() => ({ - find: vi.fn(() => []) - })) + find: vi.fn(() => []), + })), })) +global.__TAURI_INTERNALS__ = { + plugins: { + path: { + sep: '/', + }, + }, +} describe('useThreads', () => { beforeEach(() => { @@ -29,14 +36,14 @@ describe('useThreads', () => { useThreads.setState({ threads: {}, currentThreadId: undefined, - searchIndex: null + searchIndex: null, }) }) }) it('should initialize with default state', () => { const { result } = renderHook(() => useThreads()) - + expect(result.current.threads).toEqual({}) expect(result.current.currentThreadId).toBeUndefined() expect(result.current.getCurrentThread()).toBeUndefined() @@ -44,81 +51,109 @@ describe('useThreads', () => { it('should set threads', () => { const { result } = renderHook(() => useThreads()) - + const threads = [ { id: 'thread1', title: 'Thread 1', messages: [] }, - { id: 'thread2', title: 'Thread 2', messages: [] } + { id: 'thread2', title: 'Thread 2', messages: [] }, ] - + act(() => { result.current.setThreads(threads) }) - + expect(Object.keys(result.current.threads)).toHaveLength(2) expect(result.current.threads['thread1']).toEqual(threads[0]) expect(result.current.threads['thread2']).toEqual(threads[1]) }) + it('should set threads with cortex model migrated', () => { + const { result } = renderHook(() => useThreads()) + + const threads = [ + { + id: 'thread1', + title: 'Thread 1', + messages: [], + model: { provider: 'llama.cpp', id: 'thread1:free' }, + }, + { + id: 'thread2', + title: 'Thread 2', + messages: [], + model: { provider: 'llama.cpp', id: 'thread2:test' }, + }, + ] + + act(() => { + result.current.setThreads(threads) + }) + + expect(Object.keys(result.current.threads)).toHaveLength(2) + expect(result.current.threads['thread1'].model.id).toEqual('thread1:free') + expect(result.current.threads['thread1'].model.provider).toEqual('llamacpp') + expect(result.current.threads['thread2'].model.id).toEqual('thread2/test') + expect(result.current.threads['thread2'].model.provider).toEqual('llamacpp') + }) it('should set current thread ID', () => { const { result } = renderHook(() => useThreads()) - + act(() => { result.current.setCurrentThreadId('thread-123') }) - + expect(result.current.currentThreadId).toBe('thread-123') }) it('should get current thread', () => { const { result } = renderHook(() => useThreads()) - + const thread = { id: 'thread1', title: 'Thread 1', messages: [] } - + act(() => { result.current.setThreads([thread]) result.current.setCurrentThreadId('thread1') }) - + expect(result.current.getCurrentThread()).toEqual(thread) }) it('should return undefined when getting current thread with no ID', () => { const { result } = renderHook(() => useThreads()) - + expect(result.current.getCurrentThread()).toBeUndefined() }) it('should get thread by ID', () => { const { result } = renderHook(() => useThreads()) - + const thread = { id: 'thread1', title: 'Thread 1', messages: [] } - + act(() => { result.current.setThreads([thread]) }) - + expect(result.current.getThreadById('thread1')).toEqual(thread) expect(result.current.getThreadById('nonexistent')).toBeUndefined() }) it('should delete thread', () => { const { result } = renderHook(() => useThreads()) - + const threads = [ { id: 'thread1', title: 'Thread 1', messages: [] }, - { id: 'thread2', title: 'Thread 2', messages: [] } + { id: 'thread2', title: 'Thread 2', messages: [] }, ] - + act(() => { result.current.setThreads(threads) }) - + expect(Object.keys(result.current.threads)).toHaveLength(2) - + act(() => { result.current.deleteThread('thread1') }) - + expect(Object.keys(result.current.threads)).toHaveLength(1) expect(result.current.threads['thread1']).toBeUndefined() expect(result.current.threads['thread2']).toBeDefined() @@ -126,40 +161,45 @@ describe('useThreads', () => { it('should rename thread', () => { const { result } = renderHook(() => useThreads()) - + const thread = { id: 'thread1', title: 'Original Title', messages: [] } - + act(() => { result.current.setThreads([thread]) }) - + act(() => { result.current.renameThread('thread1', 'New Title') }) - + expect(result.current.threads['thread1'].title).toBe('New Title') }) it('should toggle favorite', () => { const { result } = renderHook(() => useThreads()) - - const thread = { id: 'thread1', title: 'Thread 1', messages: [], starred: false } - + + const thread = { + id: 'thread1', + title: 'Thread 1', + messages: [], + starred: false, + } + act(() => { result.current.setThreads([thread]) }) - + act(() => { result.current.toggleFavorite('thread1') }) - + // Just test that the toggle function exists and can be called expect(typeof result.current.toggleFavorite).toBe('function') }) it('should get favorite threads', () => { const { result } = renderHook(() => useThreads()) - + // Just test that the function exists expect(typeof result.current.getFavoriteThreads).toBe('function') const favorites = result.current.getFavoriteThreads() @@ -168,42 +208,42 @@ describe('useThreads', () => { it('should delete all threads', () => { const { result } = renderHook(() => useThreads()) - + const threads = [ { id: 'thread1', title: 'Thread 1', messages: [] }, - { id: 'thread2', title: 'Thread 2', messages: [] } + { id: 'thread2', title: 'Thread 2', messages: [] }, ] - + act(() => { result.current.setThreads(threads) }) - + expect(Object.keys(result.current.threads)).toHaveLength(2) - + act(() => { result.current.deleteAllThreads() }) - + expect(result.current.threads).toEqual({}) }) it('should unstar all threads', () => { const { result } = renderHook(() => useThreads()) - + // Just test that the function exists and can be called expect(typeof result.current.unstarAllThreads).toBe('function') - + act(() => { result.current.unstarAllThreads() }) - + // Function executed without error expect(true).toBe(true) }) it('should filter threads by search term', () => { const { result } = renderHook(() => useThreads()) - + // Just test that the function exists expect(typeof result.current.getFilteredThreads).toBe('function') const filtered = result.current.getFilteredThreads('test') @@ -212,17 +252,17 @@ describe('useThreads', () => { it('should return all threads when no search term', () => { const { result } = renderHook(() => useThreads()) - + const threads = [ { id: 'thread1', title: 'Thread 1', messages: [] }, - { id: 'thread2', title: 'Thread 2', messages: [] } + { id: 'thread2', title: 'Thread 2', messages: [] }, ] - + act(() => { result.current.setThreads(threads) }) - + const filtered = result.current.getFilteredThreads('') expect(filtered).toHaveLength(2) }) -}) \ No newline at end of file +}) diff --git a/web-app/src/hooks/useThreads.ts b/web-app/src/hooks/useThreads.ts index 665681462..c9e88d097 100644 --- a/web-app/src/hooks/useThreads.ts +++ b/web-app/src/hooks/useThreads.ts @@ -44,7 +44,9 @@ export const useThreads = create()((set, get) => ({ 'llamacpp' ), // Cortex migration: take first two parts of the ID (the last is file name which is not needed) - id: thread.model?.id.split(':').slice(0, 2).join(sep()), + id: !thread.model?.id.endsWith(':free') + ? thread.model?.id.split(':').slice(0, 2).join(sep()) + : thread.model?.id, } : undefined, }