- NoteStatus 에서 'archived' 제거 (active | completed | trashed 3분기) - MoveStatusModal ALL_STATUSES 에서 'archived' 제거 + statusLabel switch 정리 - classifyStatus VALID/FALLBACK/PROMPT 에서 archived 제거 → completed fallback - inboxApi IPC set-status VALID 배열에서 archived 제거, classify-status fallback → completed - store InboxView 에서 'archived' 제거, InboxCounts.archived 제거, archived: 0 spread 제거 - ImportService.applySyncFromDir — 기존 파일의 status=archived 를 completed 로 coerce - 영향 받는 tests 13개 파일 모두 update (archived → completed, 없어진 UI 옵션 제거) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
110 lines
3.3 KiB
TypeScript
110 lines
3.3 KiB
TypeScript
import { describe, it, expect, vi } from 'vitest';
|
|
import { classifyStatus } from '../../src/main/ai/classifyStatus';
|
|
import type { InferenceProvider } from '../../src/main/ai/InferenceProvider';
|
|
|
|
function makeProvider(generateRaw?: (p: string) => Promise<string>): InferenceProvider {
|
|
return {
|
|
name: 'mock',
|
|
generate: vi.fn(async () => {
|
|
throw new Error('not used');
|
|
}),
|
|
healthCheck: vi.fn(async () => ({ ok: true })),
|
|
...(generateRaw !== undefined ? { generateRaw } : {})
|
|
} as InferenceProvider;
|
|
}
|
|
|
|
describe('classifyStatus', () => {
|
|
it('parses recommended status and rationale from valid AI response', async () => {
|
|
const provider = makeProvider(
|
|
vi.fn(async () => '{"recommended":"completed","rationale":"처리됨"}')
|
|
);
|
|
const r = await classifyStatus({
|
|
provider,
|
|
rawText: 't',
|
|
summary: '',
|
|
reason: '결재 끝'
|
|
});
|
|
expect(r.recommended).toBe('completed');
|
|
expect(r.rationale).toBe('처리됨');
|
|
});
|
|
|
|
it('falls back to completed on parse failure (invalid JSON)', async () => {
|
|
const provider = makeProvider(vi.fn(async () => 'not json'));
|
|
const r = await classifyStatus({
|
|
provider,
|
|
rawText: 't',
|
|
summary: '',
|
|
reason: 'r'
|
|
});
|
|
expect(r.recommended).toBe('completed');
|
|
expect(r.rationale).toMatch(/판단 실패|보관/);
|
|
});
|
|
|
|
it('falls back to completed on invalid status value', async () => {
|
|
const provider = makeProvider(
|
|
vi.fn(async () => '{"recommended":"unknown","rationale":"x"}')
|
|
);
|
|
const r = await classifyStatus({
|
|
provider,
|
|
rawText: 't',
|
|
summary: '',
|
|
reason: 'r'
|
|
});
|
|
expect(r.recommended).toBe('completed');
|
|
});
|
|
|
|
it('handles provider throw', async () => {
|
|
const provider = makeProvider(
|
|
vi.fn(async () => {
|
|
throw new Error('network');
|
|
})
|
|
);
|
|
const r = await classifyStatus({
|
|
provider,
|
|
rawText: 't',
|
|
summary: '',
|
|
reason: 'r'
|
|
});
|
|
expect(r.recommended).toBe('completed');
|
|
expect(r.rationale).toMatch(/판단 실패|보관/);
|
|
});
|
|
|
|
it('falls back when provider lacks generateRaw method', async () => {
|
|
const provider = makeProvider();
|
|
const r = await classifyStatus({
|
|
provider,
|
|
rawText: 't',
|
|
summary: '',
|
|
reason: 'r'
|
|
});
|
|
expect(r.recommended).toBe('completed');
|
|
expect(r.rationale).toMatch(/판단 실패|보관/);
|
|
});
|
|
|
|
it('substitutes empty inputs with placeholder text in prompt', async () => {
|
|
const generateRaw = vi.fn(
|
|
async (_p: string) => '{"recommended":"completed","rationale":"ok"}'
|
|
);
|
|
const provider = makeProvider(generateRaw);
|
|
await classifyStatus({ provider, rawText: '', summary: '', reason: '' });
|
|
const prompt = generateRaw.mock.calls[0]?.[0] ?? '';
|
|
expect(prompt).toContain('(빈 메모)');
|
|
expect(prompt).toContain('(요약 없음)');
|
|
expect(prompt).toContain('(사유 없음)');
|
|
});
|
|
|
|
it('rationale defaults to empty string when missing/non-string', async () => {
|
|
const provider = makeProvider(
|
|
vi.fn(async () => '{"recommended":"completed"}')
|
|
);
|
|
const r = await classifyStatus({
|
|
provider,
|
|
rawText: 't',
|
|
summary: '',
|
|
reason: 'r'
|
|
});
|
|
expect(r.recommended).toBe('completed');
|
|
expect(r.rationale).toBe('');
|
|
});
|
|
});
|