History

Add coverage for corrupt journals, increase timeout for SIGKILL recovery sweep

Griffin Evans committed 4 months ago

Commit 30f87651 parent 1663b1a1 file · +54 −1Browse files

Unified · Split

apps/local/src/db/v1-v2-migration.test.ts+54−1
@@ -1161,6 +1161,59 @@ describe("local v1 -> v2 migration", () => { · 1160 unmodified lines
1161 expect(auth[migratedItemId(scopeId, "stripe-key")]).toBe("sk_test_recovered");1161 expect(auth[migratedItemId(scopeId, "stripe-key")]).toBe("sk_test_recovered");
1162 });1162 });
1163 1163
1164 it("restarts from an unreadable journal when the canonical v1 database is intact", async () => {
1165 const scopeId = "executor-workspace-corruptjournal";
1166 const dataDir = join(workDir, "data");
1167 const dbPath = join(dataDir, "data.db");
1168 const journalPath = `${dbPath}.v1-v2-migration.json`;
1169 mkdirSync(dataDir, { recursive: true });
1170 await seedV1Db(dbPath, scopeId);
1171 await writeLiveWalMarker(dbPath, "wal-corrupt-journal");
1172
1173 const authDir = join(process.env.XDG_DATA_HOME!, "executor");
1174 mkdirSync(authDir, { recursive: true });
1175 writeFileSync(
1176 join(authDir, "auth.json"),
1177 JSON.stringify({ [scopeId]: { "stripe-key": "sk_test_corrupt_journal" } }, null, 2),
1178 );
1179 writeFileSync(journalPath, "{ this is not valid json", { mode: 0o600 });
1180
1181 const result = await migrateLocalV1ToV2IfNeeded({
1182 sqlitePath: dbPath,
1183 tables: collectTables(),
1184 namespace: "executor_local",
1185 tenantId: scopeId,
1186 });
1187
1188 expect(result.migrated).toBe(true);
1189 expect(existsSync(journalPath)).toBe(false);
1190 await assertMigratedStripeDbAndSecret({
1191 dbPath,
1192 scopeId,
1193 secret: "sk_test_corrupt_journal",
1194 walMarker: "wal-corrupt-journal",
1195 });
1196 });
1197
1198 it("fails closed on an unreadable journal when the canonical database is missing", async () => {
1199 const dataDir = join(workDir, "data");
1200 const dbPath = join(dataDir, "data.db");
1201 const journalPath = `${dbPath}.v1-v2-migration.json`;
1202 mkdirSync(dataDir, { recursive: true });
1203 writeFileSync(journalPath, "{ this is not valid json", { mode: 0o600 });
1204
1205 await expect(
1206 migrateLocalV1ToV2IfNeeded({
1207 sqlitePath: dbPath,
1208 tables: collectTables(),
1209 namespace: "executor_local",
1210 tenantId: "executor-workspace-missingdb",
1211 }),
1212 ).rejects.toMatchObject({ _tag: "LocalV1V2MigrationError" });
1213 expect(existsSync(dbPath)).toBe(false);
1214 expect(existsSync(journalPath)).toBe(true);
1215 });
1216
1164 it("recovers after SIGKILL at every migration journal boundary", async () => {1217 it("recovers after SIGKILL at every migration journal boundary", async () => {
1165 // Each iteration rewrites the process-global XDG auth.json; keep this sweep1218 // Each iteration rewrites the process-global XDG auth.json; keep this sweep
1166 // sequential so phase secrets cannot bleed across concurrent migrations.1219 // sequential so phase secrets cannot bleed across concurrent migrations.
@@ -1191,7 +1244,7 @@ describe("local v1 -> v2 migration", () => { · 24 unmodified lines
1191 await assertMigratedStripeDbAndSecret({ dbPath, scopeId, secret, walMarker });1244 await assertMigratedStripeDbAndSecret({ dbPath, scopeId, secret, walMarker });
1192 expect(existsSync(`${dbPath}.v1-v2-migration.json`)).toBe(false);1245 expect(existsSync(`${dbPath}.v1-v2-migration.json`)).toBe(false);
1193 }1246 }
1194 });1247 }, 30_000);
1195 1248
1196 it("completes a built journal recovery before opening the canonical DB", async () => {1249 it("completes a built journal recovery before opening the canonical DB", async () => {
1197 const scopeId = "executor-workspace-abcd1234";1250 const scopeId = "executor-workspace-abcd1234";