Files
inkling/tests/unit/ContinuityService.test.ts
altair823 c0ee8c0981 feat(continuity): WeeklyContinuity service (7 notes/week, recovery detection)
Task 9 of the slice plan. Computes the data behind the Inbox
ContinuityBadge and recovery toast (Strategy §5):
- KST Mon-Sun grouping via toKstDateKey/kstMondayOf helpers
  (UTC + 9h shift, then bucket to local Monday).
- weekCount + weekTarget=7 + consecutiveCompleteWeeks (walk
  backward from current week, or previous if current incomplete,
  counting only weeks with >=7 notes).
- showRecoveryToast=true when the latest note lands on today
  (KST) and the prior note is at least 7 days earlier — this is
  the "흐름을 다시 이어갑니다" trigger.
- now() injected for deterministic tests.

Verification: `npx vitest run tests/unit/ContinuityService.test.ts`
6 passed. All 24 unit tests across migrations / NoteRepository /
MediaStore / ContinuityService green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 12:06:45 +09:00

92 lines
3.4 KiB
TypeScript

import { describe, it, expect } from 'vitest';
import Database from 'better-sqlite3';
import { runMigrations } from '@main/db/migrations/index.js';
import { ContinuityService } from '@main/services/ContinuityService.js';
function dbWithDates(isoDates: string[]): Database.Database {
const db = new Database(':memory:');
runMigrations(db);
const insert = db.prepare(
`INSERT INTO notes (id, raw_text, ai_status, created_at, updated_at)
VALUES (?, ?, 'pending', ?, ?)`
);
for (const [i, d] of isoDates.entries()) insert.run(`n${i}`, 'x', d, d);
return db;
}
describe('ContinuityService', () => {
it('empty db returns zero counts and no recovery toast', () => {
const db = new Database(':memory:');
runMigrations(db);
const svc = new ContinuityService(db, () => new Date('2026-04-25T10:00:00+09:00'));
const r = svc.get();
expect(r.weekCount).toBe(0);
expect(r.weekTarget).toBe(7);
expect(r.consecutiveCompleteWeeks).toBe(0);
expect(r.showRecoveryToast).toBe(false);
expect(r.lastNoteAt).toBeNull();
});
it('counts notes in current KST week (월~일)', () => {
const db = dbWithDates([
'2026-04-20T01:00:00+09:00',
'2026-04-22T03:00:00+09:00',
'2026-04-25T22:00:00+09:00'
]);
const svc = new ContinuityService(db, () => new Date('2026-04-25T23:00:00+09:00'));
const r = svc.get();
expect(r.weekStart).toBe('2026-04-20');
expect(r.weekCount).toBe(3);
});
function isoKst(year: number, month: number, day: number, hour = 10): string {
const mm = String(month).padStart(2, '0');
const dd = String(day).padStart(2, '0');
const hh = String(hour).padStart(2, '0');
return `${year}-${mm}-${dd}T${hh}:00:00+09:00`;
}
it('consecutiveCompleteWeeks counts weeks with >=7 notes ending immediately before current week', () => {
const dates: string[] = [];
for (let d = 6; d <= 12; d++) dates.push(isoKst(2026, 4, d));
for (let d = 13; d <= 19; d++) dates.push(isoKst(2026, 4, d));
dates.push(isoKst(2026, 4, 20));
dates.push(isoKst(2026, 4, 21));
const db = dbWithDates(dates);
const svc = new ContinuityService(db, () => new Date('2026-04-25T12:00:00+09:00'));
const r = svc.get();
expect(r.consecutiveCompleteWeeks).toBe(2);
expect(r.weekCount).toBe(2);
});
it('consecutiveCompleteWeeks includes current week if it already hit 7', () => {
const dates: string[] = [];
for (let d = 13; d <= 19; d++) dates.push(isoKst(2026, 4, d));
for (let d = 20; d <= 26; d++) dates.push(isoKst(2026, 4, d));
const db = dbWithDates(dates);
const svc = new ContinuityService(db, () => new Date('2026-04-26T23:00:00+09:00'));
const r = svc.get();
expect(r.weekCount).toBe(7);
expect(r.consecutiveCompleteWeeks).toBe(2);
});
it('showRecoveryToast=true when last note is >=7 days ago AND a fresh note exists today', () => {
const dates = [
'2026-04-15T10:00:00+09:00',
'2026-04-25T11:00:00+09:00'
];
const db = dbWithDates(dates);
const svc = new ContinuityService(db, () => new Date('2026-04-25T12:00:00+09:00'));
expect(svc.get().showRecoveryToast).toBe(true);
});
it('showRecoveryToast=false when there are notes within last 7 days', () => {
const db = dbWithDates([
'2026-04-22T10:00:00+09:00',
'2026-04-25T11:00:00+09:00'
]);
const svc = new ContinuityService(db, () => new Date('2026-04-25T12:00:00+09:00'));
expect(svc.get().showRecoveryToast).toBe(false);
});
});