import { describe, it, expect } from 'vitest'; import Database from 'better-sqlite3'; import { runMigrations } from '@main/db/migrations/index.js'; describe('migrations', () => { it('creates schema with intent + edited columns', () => { const db = new Database(':memory:'); runMigrations(db); const cols = db.prepare(`PRAGMA table_info(notes)`).all().map((r: any) => r.name); expect(cols).toEqual( expect.arrayContaining([ 'id', 'raw_text', 'ai_title', 'ai_summary', 'ai_status', 'ai_error', 'ai_provider', 'ai_generated_at', 'title_edited_by_user', 'summary_edited_by_user', 'user_intent', 'intent_prompted_at', 'created_at', 'updated_at' ]) ); db.close(); }); it('is idempotent', () => { const db = new Database(':memory:'); runMigrations(db); const before = (db.prepare('PRAGMA user_version').get() as any).user_version; runMigrations(db); const after = (db.prepare('PRAGMA user_version').get() as any).user_version; expect(after).toBe(before); db.close(); }); }); describe('migration v3 — soft delete columns', () => { it('adds deleted_at, last_recalled_at, recall_dismissed_at to notes', () => { const db = new Database(':memory:'); runMigrations(db); const cols = db.prepare(`PRAGMA table_info(notes)`).all().map((r: any) => r.name); expect(cols).toEqual( expect.arrayContaining(['deleted_at', 'last_recalled_at', 'recall_dismissed_at']) ); db.close(); }); it('creates idx_notes_deleted_at index', () => { const db = new Database(':memory:'); runMigrations(db); const indexes = db .prepare(`SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='notes'`) .all() as Array<{ name: string }>; expect(indexes.map((i) => i.name)).toContain('idx_notes_deleted_at'); db.close(); }); it('user_version reaches latest (7)', () => { const db = new Database(':memory:'); runMigrations(db); const row = db.prepare('PRAGMA user_version').get() as { user_version: number }; expect(row.user_version).toBe(7); db.close(); }); it('all 3 new columns default to NULL', () => { const db = new Database(':memory:'); runMigrations(db); db.prepare( `INSERT INTO notes (id, raw_text, ai_status, created_at, updated_at) VALUES ('n1', 't', 'pending', '2026-05-01T00:00:00Z', '2026-05-01T00:00:00Z')` ).run(); const row = db.prepare('SELECT deleted_at, last_recalled_at, recall_dismissed_at FROM notes WHERE id=?').get('n1') as any; expect(row.deleted_at).toBeNull(); expect(row.last_recalled_at).toBeNull(); expect(row.recall_dismissed_at).toBeNull(); db.close(); }); }); describe('migration v5 — ai_status disabled enum', () => { it("CHECK constraint accepts 'disabled'", () => { const db = new Database(':memory:'); runMigrations(db); expect(() => { db.prepare( `INSERT INTO notes (id, raw_text, ai_status, created_at, updated_at) VALUES ('d1', 't', 'disabled', '2026-05-01T00:00:00Z', '2026-05-01T00:00:00Z')` ).run(); }).not.toThrow(); db.close(); }); it('preserves existing notes (status, due_date, deleted_at, recall fields)', () => { // m004 까지만 적용된 상태에서 데이터 insert 후 m005 까지 마이그레이션 → 데이터 보존 확인. // runMigrations 가 user_version 으로 idempotent 라 한 번에 5 까지 가지만, // 본 테스트는 single runMigrations 후 m004 시점에 가까운 row 를 넣고 cols 확인. const db = new Database(':memory:'); runMigrations(db); db.prepare( `INSERT INTO notes (id, raw_text, ai_status, created_at, updated_at, status, due_date, deleted_at) VALUES ('p1', 'old', 'done', '2026-04-01T00:00:00Z', '2026-04-01T00:00:00Z', 'archived', '2026-05-10', NULL)` ).run(); const row = db.prepare('SELECT status, due_date, ai_status FROM notes WHERE id=?').get('p1') as any; expect(row.status).toBe('archived'); expect(row.due_date).toBe('2026-05-10'); expect(row.ai_status).toBe('done'); db.close(); }); it('preserves idx_notes_ai_status + idx_notes_created_at + idx_notes_deleted_at', () => { const db = new Database(':memory:'); runMigrations(db); const indexes = db .prepare(`SELECT name FROM sqlite_master WHERE type='index' AND tbl_name='notes'`) .all() as Array<{ name: string }>; const names = indexes.map((i) => i.name); expect(names).toContain('idx_notes_ai_status'); expect(names).toContain('idx_notes_created_at'); expect(names).toContain('idx_notes_deleted_at'); db.close(); }); });