import { describe, expect, it, vi, beforeEach } from 'vitest'; const listNotesMock = vi.fn(); const getNoteMock = vi.fn(); const createNoteMock = vi.fn(); const updateNoteMock = vi.fn(); const enqueueNoteCreateMock = vi.fn(); const enqueueNoteUpdateMock = vi.fn(); vi.mock('../api/notes', () => ({ listNotes: (...args: unknown[]) => listNotesMock(...args), getNote: (...args: unknown[]) => getNoteMock(...args), createNote: (...args: unknown[]) => createNoteMock(...args), updateNote: (...args: unknown[]) => updateNoteMock(...args), })); vi.mock('../lib/offline-queue', () => ({ enqueueNoteCreate: (...args: unknown[]) => enqueueNoteCreateMock(...args), enqueueNoteUpdate: (...args: unknown[]) => enqueueNoteUpdateMock(...args), })); import { useNotesStore } from './notes-store'; function resetStore() { useNotesStore.setState({ notes: [], selectedNote: null, isLoading: false, }); } const fakeNote = { id: 'n1', workspaceId: 'ws-1', title: 'Test', body: 'Body', workspaceName: 'Work', status: 'draft' as const, updatedAt: '2025-01-01T00:00:00Z', }; describe('useNotesStore', () => { beforeEach(() => { vi.clearAllMocks(); resetStore(); }); it('hydrate loads notes', async () => { listNotesMock.mockResolvedValueOnce([fakeNote]); await useNotesStore.getState().hydrate(); expect(useNotesStore.getState().notes).toHaveLength(1); expect(useNotesStore.getState().notes[0].id).toBe('n1'); expect(useNotesStore.getState().isLoading).toBe(false); }); it('openNote sets selectedNote', async () => { useNotesStore.setState({ notes: [fakeNote] }); getNoteMock.mockResolvedValueOnce(fakeNote); await useNotesStore.getState().openNote('n1'); expect(useNotesStore.getState().selectedNote?.id).toBe('n1'); }); it('openNote clears selectedNote for unknown id', async () => { useNotesStore.setState({ notes: [fakeNote] }); await useNotesStore.getState().openNote('missing'); expect(useNotesStore.getState().selectedNote).toBeNull(); }); it('saveDraft creates note and prepends to list', async () => { const created = { ...fakeNote, id: 'n2', title: 'New' }; createNoteMock.mockResolvedValueOnce(created); const ok = await useNotesStore.getState().saveDraft('ws-1', 'New', 'Body'); expect(ok).toBe(true); expect(useNotesStore.getState().notes[0].id).toBe('n2'); expect(useNotesStore.getState().selectedNote?.id).toBe('n2'); }); it('saveDraft returns false without workspaceId', async () => { const ok = await useNotesStore.getState().saveDraft(null, 'Title', 'Body'); expect(ok).toBe(false); expect(createNoteMock).not.toHaveBeenCalled(); }); it('saveDraft enqueues create when persistence fails', async () => { createNoteMock.mockRejectedValueOnce(new Error('offline')); const ok = await useNotesStore.getState().saveDraft('ws-1', 'Queued', 'Body'); expect(ok).toBe(true); expect(enqueueNoteCreateMock).toHaveBeenCalledTimes(1); expect(useNotesStore.getState().notes[0].title).toBe('Queued'); }); it('updateNote persists and updates state', async () => { useNotesStore.setState({ notes: [fakeNote] }); const updated = { ...fakeNote, title: 'Updated' }; updateNoteMock.mockResolvedValueOnce(updated); await useNotesStore.getState().updateNote('n1', 'Updated', 'Body'); expect(useNotesStore.getState().notes[0].title).toBe('Updated'); }); it('updateNote does nothing for unknown id', async () => { await useNotesStore.getState().updateNote('missing', 'Title', 'Body'); expect(updateNoteMock).not.toHaveBeenCalled(); }); it('updateNote enqueues update when persistence fails', async () => { useNotesStore.setState({ notes: [fakeNote] }); updateNoteMock.mockRejectedValueOnce(new Error('offline')); await useNotesStore.getState().updateNote('n1', 'Updated offline', 'Body'); expect(enqueueNoteUpdateMock).toHaveBeenCalledTimes(1); expect(useNotesStore.getState().notes[0].title).toBe('Updated offline'); }); });