/* Migration 9 — sync protocol 2's columns, per-device counters, JSON keys. A database as migration 8 left it, carrying protocol 1's bookkeeping and a tombstone of every shape protocol 1 wrote. */ import { describe, it, expect, beforeEach, afterEach } from "vitest"; import { SqliteWasmDb } from "@app/db/sqlite-wasm-core.js"; import { migrate } from "@app/db/migrate.js"; import type { Db } from "@app/db/types.js"; let db: Db; let device: string; beforeEach(async () => { db = await SqliteWasmDb.open({ memory: true }); await migrate(db, 8); device = (await db.get<{ v: string }>("SELECT v FROM meta WHERE k = 'sync.device'"))!.v; await db.exec(` INSERT INTO study_log (day, reviews, correct, drills, updated_at) VALUES (20000, 5, 4, 1, 700); INSERT INTO peek (form, count, updated_at) VALUES ('몇 명', 3, 800); INSERT INTO card (lemma_id, state, reps, updated_at) VALUES (55855054575946, 1, 2, 900); INSERT INTO meta (k, v, updated_at) VALUES ('sync.cursor', '41', 0), ('sync.pushedAt', '900', 0); INSERT INTO tombstone (tbl, pk, updated_at) VALUES ('card', '1434356355562999', 10), ('study_log', '19999', 11), ('peek', '몇 명', 12), ('meta', 'prefs.goal', 13), ('custom_word', '["던전","noun"]', 14); `); await migrate(db); }); afterEach(async () => { await db.close(); }); describe("migration 9", () => { it("gives every syncable row base_seq, dirty and rev, all zero", async () => { expect(await db.get("SELECT base_seq, dirty, rev, updated_at FROM card")).toEqual({ base_seq: 0, dirty: 0, rev: 0, updated_at: 900, }); }); it("hands this install's counters to this install", async () => { expect(await db.get("SELECT day, device, reviews, correct, drills, updated_at FROM study_log")).toEqual({ day: 20000, device, reviews: 5, correct: 4, drills: 1, updated_at: 700, }); expect(await db.get("SELECT form, device, count FROM peek")).toEqual({ form: "몇 명", device, count: 3 }); }); it("writes every tombstone key as a JSON array, a space included", async () => { const graves = await db.all<{ tbl: string; pk: string }>("SELECT tbl, pk FROM tombstone ORDER BY updated_at"); expect(graves).toEqual([ { tbl: "card", pk: "[1434356355562999]" }, { tbl: "study_log", pk: JSON.stringify([19999, device]) }, { tbl: "peek", pk: JSON.stringify(["몇 명", device]) }, { tbl: "meta", pk: '["prefs.goal"]' }, { tbl: "custom_word", pk: '["던전","noun"]' }, ]); }); it("drops protocol 1's position, so the next sync hydrates from scratch", async () => { expect(await db.all("SELECT k FROM meta WHERE k IN ('sync.cursor', 'sync.pushedAt')")).toEqual([]); }); });