Merge pull request #43 from timothyjlaurent/timothyjlaurent/broken-insights
fix(insights): add missing lifecycle column compatibility check
This commit is contained in:
@@ -963,4 +963,56 @@ describe("Migration: pre-33 DB upgrade", () => {
|
||||
rmSync(testDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it("ensureInsightRunsSchemaCompatibility adds lifecycle column to legacy table", () => {
|
||||
const compatDir = mkdtempSync(join(tmpdir(), "fn-insight-compat-"));
|
||||
|
||||
try {
|
||||
// Step 1: Create a fresh DB and run migrations
|
||||
const db1 = createDatabase(compatDir);
|
||||
db1.init();
|
||||
expect(db1.getSchemaVersion()).toBe(62);
|
||||
|
||||
// Step 2: Strip lifecycle and cancelledAt columns by recreating the
|
||||
// table without them. This simulates a DB that was created before the
|
||||
// lifecycle columns were added and already past v59 when they landed.
|
||||
db1.exec(`
|
||||
CREATE TABLE project_insight_runs_legacy AS
|
||||
SELECT id, projectId, trigger, status, summary, error,
|
||||
insightsCreated, insightsUpdated, inputMetadata, outputMetadata,
|
||||
createdAt, startedAt, completedAt
|
||||
FROM project_insight_runs
|
||||
`);
|
||||
db1.exec("DROP TABLE project_insight_runs");
|
||||
db1.exec("ALTER TABLE project_insight_runs_legacy RENAME TO project_insight_runs");
|
||||
|
||||
// Verify lifecycle column is gone
|
||||
const colsBefore = db1.prepare("PRAGMA table_info(project_insight_runs)").all() as Array<{ name: string }>;
|
||||
const colNamesBefore = colsBefore.map((c) => c.name);
|
||||
expect(colNamesBefore).not.toContain("lifecycle");
|
||||
expect(colNamesBefore).not.toContain("cancelledAt");
|
||||
db1.close();
|
||||
|
||||
// Step 3: Re-open — ensureInsightRunsSchemaCompatibility should add the
|
||||
// missing columns unconditionally.
|
||||
const db2 = createDatabase(compatDir);
|
||||
db2.init();
|
||||
|
||||
const colsAfter = db2.prepare("PRAGMA table_info(project_insight_runs)").all() as Array<{ name: string }>;
|
||||
const colNamesAfter = colsAfter.map((c) => c.name);
|
||||
expect(colNamesAfter).toContain("lifecycle");
|
||||
expect(colNamesAfter).toContain("cancelledAt");
|
||||
|
||||
// Step 4: Creating a run must not throw — proves the INSERT path works
|
||||
// with the restored columns.
|
||||
const s = new InsightStore(db2);
|
||||
const run = s.createRun("proj", { trigger: "manual" });
|
||||
expect(run.id).toBeTruthy();
|
||||
expect(run.lifecycle).toBeDefined();
|
||||
|
||||
db2.close();
|
||||
} finally {
|
||||
rmSync(compatDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1068,6 +1068,7 @@ export class Database {
|
||||
|
||||
// Compatibility backfills that must run even when schemaVersion is current.
|
||||
this.ensureRoutinesSchemaCompatibility();
|
||||
this.ensureInsightRunsSchemaCompatibility();
|
||||
|
||||
// Seed config row idempotently with default settings
|
||||
const configNow = new Date().toISOString();
|
||||
@@ -1122,6 +1123,25 @@ export class Database {
|
||||
this.db.exec("CREATE INDEX IF NOT EXISTS idxRoutinesScope ON routines(scope)");
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies idempotent compatibility fixes for the project_insight_runs table.
|
||||
*
|
||||
* The `lifecycle` and `cancelledAt` columns were added to SCHEMA_SQL and
|
||||
* retroactively inserted into migration v33's CREATE TABLE, with a safety-net
|
||||
* in migration v59. However, databases that were already at v59+ when the
|
||||
* commit landed never re-run v59, leaving the columns missing. Running this
|
||||
* unconditionally on every init guarantees the columns exist.
|
||||
*/
|
||||
private ensureInsightRunsSchemaCompatibility(): void {
|
||||
if (!this.hasTable("project_insight_runs")) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.addColumnIfMissing("project_insight_runs", "lifecycle", "TEXT");
|
||||
this.addColumnIfMissing("project_insight_runs", "cancelledAt", "TEXT");
|
||||
this.db.exec(`CREATE INDEX IF NOT EXISTS idxInsightRunsProjectTriggerStatus ON project_insight_runs(projectId, trigger, status)`);
|
||||
}
|
||||
|
||||
private migrate(): void {
|
||||
const version = this.getSchemaVersion() || 1;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user