/** * 数据库 schema 版本化测试(v0.6.4 P3-3) * * 契约: * 1. 全新库 initialize() 后 PRAGMA user_version 盖章为 DatabaseService.SCHEMA_VERSION; * 2. 已盖章的库再次 initialize(新实例、同文件)→ 走快速路径,探测批次跳过, * 且版本号保持不变; * 3. user_version 与数据共存:盖章不会丢失/改变已有数据。 * * 运行要求:better-sqlite3 需为 Electron ABI 构建(test:electron 模式), * 系统 Node 下自动跳过。 */ import { describe, it, expect, beforeAll, afterAll } from 'vitest'; import { mkdtempSync, rmSync } from 'fs'; import { tmpdir } from 'os'; import { join } from 'path'; let dbAvailable = true; try { // eslint-disable-next-line @typescript-eslint/no-require-imports const probe = require('better-sqlite3'); const p = new probe(':memory:'); p.close(); } catch { dbAvailable = false; } import type { DatabaseService } from '../database.service'; describe.skipIf(!dbAvailable)('DatabaseService — PRAGMA user_version 版本化', () => { let dir: string; let svc1: DatabaseService; let svc2: DatabaseService; beforeAll(async () => { dir = mkdtempSync(join(tmpdir(), 'metona-dbmig-')); const mod = await import('../database.service'); const Svc = mod.DatabaseService; svc1 = new Svc(dir); svc1.initialize(); svc2 = new Svc(dir); // 模拟第二次启动 svc2.initialize(); }); afterAll(() => { try { svc2?.close(); svc1?.close(); } catch { /* ignore */ } rmSync(dir, { recursive: true, force: true }); }); it('initialize 后 user_version 已盖章为 SCHEMA_VERSION', () => { const version = svc2.getDB().pragma('user_version', { simple: true }) as number; expect(version).toBe(1); }); it('盖章库的探测批次被跳过(重复 initialize 不报错且配置 seed 保持存在)', () => { // 核心断言在日志侧难以捕获 —— 这里验证幂等性的可观察结果: // 种子默认值仍然存在(INSERT OR IGNORE),表结构与首次一致 const row = svc2.getDB().prepare(`SELECT COUNT(*) AS n FROM app_config`).get() as { n: number }; expect(row.n).toBeGreaterThan(0); }); });