import { describe, it, expect, beforeEach, vi } from 'vitest'; vi.mock('electron', () => ({ default: { ipcMain: { handle: vi.fn(), on: vi.fn() } } })); import electron from 'electron'; import { registerNotebookApi } from '../../src/main/ipc/notebookApi.js'; function getHandler(channel: string): (...args: unknown[]) => unknown { const handle = (electron.ipcMain as unknown as { handle: ReturnType }).handle; const call = handle.mock.calls.find((c) => c[0] === channel); if (!call) throw new Error(`channel ${channel} not registered`); return call[1] as (...args: unknown[]) => unknown; } function makeRepo() { return { list: vi.fn(() => [] as never[]), create: vi.fn(), rename: vi.fn(), setColor: vi.fn(), delete: vi.fn(() => ({ ok: true })), moveNote: vi.fn(), findById: vi.fn(() => null), reorder: vi.fn(() => ({ ok: true })) }; } describe('notebookApi IPC', () => { beforeEach(() => { (electron.ipcMain as unknown as { handle: ReturnType }).handle.mockClear(); }); it('notebook:list — repo.list 결과 반환', async () => { const repo = makeRepo(); repo.list.mockReturnValue([{ id: '1', name: '기본', color: null, createdAt: 't', updatedAt: 't', noteCount: 0 }] as never); registerNotebookApi({ repo: repo as never }); const h = getHandler('notebook:list'); const r = await h({}) as unknown[]; expect(r).toHaveLength(1); }); it('notebook:create — UNIQUE 위반 시 ok:false reason="duplicate_name"', async () => { const repo = makeRepo(); repo.create.mockImplementation(() => { throw new Error('UNIQUE constraint failed: notebooks.name'); }); registerNotebookApi({ repo: repo as never }); const h = getHandler('notebook:create'); const r = await h({}, { name: '회사' }) as { ok: boolean; reason?: string }; expect(r.ok).toBe(false); expect(r.reason).toBe('duplicate_name'); }); it('notebook:create — 빈 이름 시 ok:false reason="empty_name"', async () => { const repo = makeRepo(); registerNotebookApi({ repo: repo as never }); const h = getHandler('notebook:create'); const r = await h({}, { name: ' ' }) as { ok: boolean; reason?: string }; expect(r.ok).toBe(false); expect(r.reason).toBe('empty_name'); expect(repo.create).not.toHaveBeenCalled(); }); it('notebook:rename — UNIQUE 위반 ok:false reason="duplicate_name"', async () => { const repo = makeRepo(); repo.rename.mockImplementation(() => { throw new Error('UNIQUE constraint failed: notebooks.name'); }); registerNotebookApi({ repo: repo as never }); const h = getHandler('notebook:rename'); const r = await h({}, 'id1', '회사') as { ok: boolean; reason?: string }; expect(r.ok).toBe(false); expect(r.reason).toBe('duplicate_name'); }); it('notebook:delete — has_notes 시 ok:false reason 전달', async () => { const repo = makeRepo(); repo.delete.mockReturnValue({ ok: false, reason: 'has_notes' } as never); registerNotebookApi({ repo: repo as never }); const h = getHandler('notebook:delete'); const r = await h({}, 'id1'); expect(r).toEqual({ ok: false, reason: 'has_notes' }); }); it('notebook:move-note — repo.moveNote 호출 + ok:true', async () => { const repo = makeRepo(); registerNotebookApi({ repo: repo as never }); const h = getHandler('notebook:move-note'); const r = await h({}, 'n1', 'nb-2'); expect(repo.moveNote).toHaveBeenCalledWith('n1', 'nb-2'); expect(r).toEqual({ ok: true }); }); it('notebook:set-color — repo.setColor 호출 + ok:true', async () => { const repo = makeRepo(); registerNotebookApi({ repo: repo as never }); const h = getHandler('notebook:set-color'); const r = await h({}, 'id1', '#fff'); expect(repo.setColor).toHaveBeenCalledWith('id1', '#fff'); expect(r).toEqual({ ok: true }); }); it('notebook:reorder — repo.reorder 호출 + ok:true 전달', async () => { const repo = makeRepo(); repo.reorder.mockReturnValue({ ok: true } as never); registerNotebookApi({ repo: repo as never }); const h = getHandler('notebook:reorder'); const r = await h({}, 'nb-1', 'up'); expect(repo.reorder).toHaveBeenCalledWith('nb-1', 'up'); expect(r).toEqual({ ok: true }); }); it('notebook:reorder — 첫 번째 항목 up 시 ok:false 전달', async () => { const repo = makeRepo(); repo.reorder.mockReturnValue({ ok: false } as never); registerNotebookApi({ repo: repo as never }); const h = getHandler('notebook:reorder'); const r = await h({}, 'nb-first', 'up'); expect(r).toEqual({ ok: false }); }); });