// tests/helpers/createTestDb.ts // in-memory SQLite + drizzle-orm 스키마 적용 (src/main/db/index.ts applySchema 와 동일) import Database from 'better-sqlite3' import { drizzle, type BetterSQLite3Database } from 'drizzle-orm/better-sqlite3' import * as schema from '../../src/main/db/schema' /** * 테스트용 in-memory SQLite DB를 생성한다. * 각 테스트에서 독립적인 DB를 사용할 수 있다. */ export function createTestDb(): { db: BetterSQLite3Database sqlite: Database.Database close: () => void } { const sqlite = new Database(':memory:') sqlite.pragma('journal_mode = WAL') sqlite.pragma('foreign_keys = ON') sqlite.pragma('busy_timeout = 5000') sqlite.exec(` CREATE TABLE IF NOT EXISTS history ( id TEXT PRIMARY KEY, original_text TEXT NOT NULL, polished_text TEXT, focused_app TEXT, focused_app_name TEXT, focused_app_window_title TEXT, mode TEXT NOT NULL DEFAULT 'dictation', status TEXT NOT NULL DEFAULT 'completed', error_code TEXT, audio_local_path TEXT, duration REAL NOT NULL, detected_language TEXT, mic_device TEXT, word_count INTEGER NOT NULL DEFAULT 0, stt_model TEXT, llm_model TEXT, stt_latency_ms INTEGER, llm_latency_ms INTEGER, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, app_version TEXT NOT NULL DEFAULT '1.0.0', title TEXT, summary_text TEXT ); CREATE INDEX IF NOT EXISTS idx_history_created_at ON history(created_at DESC); CREATE INDEX IF NOT EXISTS idx_history_status ON history(status); CREATE TABLE IF NOT EXISTS dictionary ( id TEXT PRIMARY KEY, word TEXT NOT NULL, pronunciation TEXT, category TEXT NOT NULL DEFAULT 'user', usage_count INTEGER NOT NULL DEFAULT 0, last_used_at INTEGER, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL ); CREATE UNIQUE INDEX IF NOT EXISTS idx_dictionary_word_category ON dictionary(word, category); CREATE INDEX IF NOT EXISTS idx_dictionary_created_at ON dictionary(created_at); CREATE INDEX IF NOT EXISTS idx_dictionary_usage_count ON dictionary(usage_count DESC); CREATE TABLE IF NOT EXISTS stats ( id INTEGER PRIMARY KEY CHECK (id = 1), total_duration REAL NOT NULL DEFAULT 0, total_words INTEGER NOT NULL DEFAULT 0, session_count INTEGER NOT NULL DEFAULT 0, streak_days INTEGER NOT NULL DEFAULT 0, last_session_at INTEGER, last_updated INTEGER NOT NULL ); INSERT OR IGNORE INTO stats (id, total_duration, total_words, session_count, streak_days, last_updated) VALUES (1, 0, 0, 0, 0, ${Date.now()}); CREATE TABLE IF NOT EXISTS memo_tags ( id TEXT PRIMARY KEY, history_id TEXT NOT NULL, tag TEXT NOT NULL, created_at INTEGER NOT NULL ); CREATE UNIQUE INDEX IF NOT EXISTS idx_memo_tags_unique ON memo_tags(history_id, tag); CREATE INDEX IF NOT EXISTS idx_memo_tags_history_id ON memo_tags(history_id); CREATE INDEX IF NOT EXISTS idx_memo_tags_tag ON memo_tags(tag); CREATE TABLE IF NOT EXISTS daily_usage ( id INTEGER PRIMARY KEY AUTOINCREMENT, date TEXT NOT NULL, feature TEXT NOT NULL, count INTEGER NOT NULL DEFAULT 0 ); CREATE UNIQUE INDEX IF NOT EXISTS idx_daily_usage_date_feature ON daily_usage(date, feature); CREATE INDEX IF NOT EXISTS idx_daily_usage_date ON daily_usage(date); CREATE TABLE IF NOT EXISTS rag_documents ( id TEXT PRIMARY KEY, file_name TEXT NOT NULL, file_path TEXT NOT NULL, file_type TEXT NOT NULL, chunk_count INTEGER NOT NULL DEFAULT 0, indexed INTEGER NOT NULL DEFAULT 0, indexed_at INTEGER, added_at INTEGER NOT NULL ); CREATE INDEX IF NOT EXISTS idx_rag_documents_added_at ON rag_documents(added_at); CREATE TABLE IF NOT EXISTS rag_chunks ( id TEXT PRIMARY KEY, document_id TEXT NOT NULL, content TEXT NOT NULL, embedding TEXT NOT NULL, chunk_index INTEGER NOT NULL ); CREATE INDEX IF NOT EXISTS idx_rag_chunks_document_id ON rag_chunks(document_id); CREATE TABLE IF NOT EXISTS meeting_sessions ( id TEXT PRIMARY KEY, title TEXT, status TEXT NOT NULL DEFAULT 'recording', started_at INTEGER NOT NULL, ended_at INTEGER, duration_ms INTEGER, raw_transcript TEXT, minutes_markdown TEXT, minutes_json TEXT, stt_model TEXT, llm_model TEXT, stt_latency_ms INTEGER, llm_latency_ms INTEGER, error_message TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL, edited_transcript TEXT ); CREATE INDEX IF NOT EXISTS idx_meeting_sessions_created_at ON meeting_sessions(created_at DESC); CREATE INDEX IF NOT EXISTS idx_meeting_sessions_status ON meeting_sessions(status); CREATE TABLE IF NOT EXISTS meeting_memos ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, content TEXT NOT NULL, timestamp_ms INTEGER NOT NULL, created_at INTEGER NOT NULL ); CREATE INDEX IF NOT EXISTS idx_meeting_memos_session_id ON meeting_memos(session_id); CREATE INDEX IF NOT EXISTS idx_meeting_memos_timestamp ON meeting_memos(timestamp_ms); CREATE TABLE IF NOT EXISTS meeting_documents ( id TEXT PRIMARY KEY, session_id TEXT NOT NULL, template_type TEXT NOT NULL, title TEXT NOT NULL, content TEXT NOT NULL DEFAULT '', prompt_used TEXT, llm_model TEXT, llm_latency_ms INTEGER, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL ); CREATE INDEX IF NOT EXISTS idx_meeting_documents_session_id ON meeting_documents(session_id); `) const db = drizzle(sqlite, { schema }) return { db, sqlite, close: () => sqlite.close(), } }