Note.notebookId 필드 추가(required), NoteRepository.create/importNote/upsertFromSync INSERT 에 notebook_id 컬럼 포함 — 미지정 시 getDefaultNotebookId() 로 가장 오래된 notebook 자동 할당. hydrate 에 notebookId 반환 추가. 관련 test fixture 5곳 notebookId 보강. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
80 lines
3.3 KiB
TypeScript
80 lines
3.3 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import type { Note } from '@shared/types';
|
|
|
|
vi.mock('../../src/renderer/inbox/api.js', () => ({
|
|
inboxApi: {
|
|
listRecallCandidate: vi.fn(),
|
|
markRecallOpened: vi.fn(),
|
|
dismissRecall: vi.fn(),
|
|
emitRecallShown: vi.fn(),
|
|
emitRecallSnoozed: vi.fn(),
|
|
listNotes: vi.fn(async () => []),
|
|
getContinuity: vi.fn(async () => ({ weekStart: '', weekCount: 0, weekTarget: 7, consecutiveCompleteWeeks: 0, showRecoveryToast: false, lastNoteAt: null })),
|
|
getPendingCount: vi.fn(async () => 0),
|
|
getOllamaStatus: vi.fn(async () => ({ ok: true })),
|
|
getTodayCount: vi.fn(async () => 0),
|
|
getTrashCount: vi.fn(async () => 0),
|
|
listExpired: vi.fn(async () => []),
|
|
getFailedCount: vi.fn(async () => 0)
|
|
}
|
|
}));
|
|
|
|
import { useInbox } from '../../src/renderer/inbox/store.js';
|
|
import { inboxApi } from '../../src/renderer/inbox/api.js';
|
|
|
|
const inboxApiMock = inboxApi as unknown as {
|
|
listRecallCandidate: ReturnType<typeof vi.fn>;
|
|
markRecallOpened: ReturnType<typeof vi.fn>;
|
|
dismissRecall: ReturnType<typeof vi.fn>;
|
|
emitRecallShown: ReturnType<typeof vi.fn>;
|
|
emitRecallSnoozed: ReturnType<typeof vi.fn>;
|
|
};
|
|
|
|
const note = (id: string): Note => ({
|
|
id, rawText: 'x', aiTitle: 't', aiSummary: 'a\nb\nc',
|
|
tags: [], media: [], aiStatus: 'done', aiProvider: null, aiGeneratedAt: null, aiError: null,
|
|
titleEditedByUser: false, summaryEditedByUser: false,
|
|
dueDate: null, dueDateEditedByUser: false,
|
|
userIntent: null, intentPromptedAt: null,
|
|
createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(),
|
|
deletedAt: null, lastRecalledAt: null, recallDismissedAt: null,
|
|
status: 'active', statusChangedAt: null, moveReason: null,
|
|
notebookId: 'nb-default'
|
|
});
|
|
|
|
describe('store recall actions', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
useInbox.setState({
|
|
recallCandidate: null,
|
|
recallSnoozeUntilMs: null,
|
|
} as Parameters<typeof useInbox.setState>[0]);
|
|
});
|
|
|
|
it('snoozeRecall sets snoozeUntilMs to next KST midnight + emits recall_snoozed', async () => {
|
|
useInbox.setState({ recallCandidate: note('n1') } as Parameters<typeof useInbox.setState>[0]);
|
|
await useInbox.getState().snoozeRecall();
|
|
const ms = useInbox.getState().recallSnoozeUntilMs;
|
|
expect(ms).not.toBeNull();
|
|
expect(ms!).toBeGreaterThan(Date.now());
|
|
expect(inboxApiMock.emitRecallSnoozed).toHaveBeenCalledWith('n1');
|
|
});
|
|
|
|
it('openRecall calls API + fetches next candidate', async () => {
|
|
inboxApiMock.markRecallOpened.mockResolvedValueOnce({ note: note('n1') });
|
|
inboxApiMock.listRecallCandidate.mockResolvedValueOnce(null);
|
|
await useInbox.getState().openRecall('n1');
|
|
expect(inboxApiMock.markRecallOpened).toHaveBeenCalledWith('n1');
|
|
expect(inboxApiMock.listRecallCandidate).toHaveBeenCalled();
|
|
expect(useInbox.getState().recallCandidate).toBeNull();
|
|
});
|
|
|
|
it('dismissRecallNote calls API + fetches next candidate', async () => {
|
|
inboxApiMock.dismissRecall.mockResolvedValueOnce({ note: note('n1') });
|
|
inboxApiMock.listRecallCandidate.mockResolvedValueOnce(note('n2'));
|
|
await useInbox.getState().dismissRecallNote('n1');
|
|
expect(inboxApiMock.dismissRecall).toHaveBeenCalledWith('n1');
|
|
expect(useInbox.getState().recallCandidate?.id).toBe('n2');
|
|
});
|
|
});
|