Skip to content

Commit 4f7031c

Browse files
fix(db): add $defaultFn(nowIso) to upstreamDriftReports.updatedAt (#8369)
Match the schema house rule every sibling updatedAt already follows so an insert that omits the column gets a real ISO-8601 timestamp. Existing writers keep their explicit updatedAt writes; this is only the schema-level safety net. Regression covers the omit path in schema-timestamp-defaults.
1 parent af270b9 commit 4f7031c

2 files changed

Lines changed: 35 additions & 2 deletions

File tree

src/db/schema.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1034,7 +1034,9 @@ export const upstreamDriftReports = sqliteTable(
10341034
issueUrl: text("issue_url"),
10351035
payloadJson: text("payload_json").notNull().default("{}"),
10361036
generatedAt: text("generated_at").notNull(),
1037-
updatedAt: text("updated_at").notNull(),
1037+
// Same $defaultFn house rule as every sibling updatedAt (#8369): an insert that omits the column
1038+
// must get a real ISO-8601 timestamp, not a schema-level gap waiting for a future writer to trip over.
1039+
updatedAt: text("updated_at").notNull().$defaultFn(() => nowIso()),
10381040
},
10391041
(table) => ({
10401042
fingerprint: uniqueIndex("upstream_drift_reports_fingerprint_unique").on(table.fingerprint),

test/unit/schema-timestamp-defaults.test.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
11
import { eq } from "drizzle-orm";
22
import { describe, expect, it } from "vitest";
33
import { getDb } from "../../src/db/client";
4-
import { aiReviewCache, aiSlopCache, linkedIssueSatisfactionCache, orbRelayPending, repositorySettings, webhookEvents } from "../../src/db/schema";
4+
import {
5+
aiReviewCache,
6+
aiSlopCache,
7+
linkedIssueSatisfactionCache,
8+
orbRelayPending,
9+
repositorySettings,
10+
upstreamDriftReports,
11+
webhookEvents,
12+
} from "../../src/db/schema";
513
import { createTestEnv } from "../helpers/d1";
614

715
const ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/;
@@ -102,4 +110,27 @@ describe("timestamp column defaults", () => {
102110
expect(row?.createdAt).toMatch(ISO);
103111
expect(row?.createdAt).not.toBe("CURRENT_TIMESTAMP");
104112
});
113+
114+
it("applies upstreamDriftReports.updatedAt default on omit (#8369)", async () => {
115+
const env = createTestEnv();
116+
const db = getDb(env.DB);
117+
// Omit updatedAt — the schema $defaultFn must inject a real ISO timestamp (same house rule as every
118+
// sibling updatedAt). generatedAt stays explicit; this issue only covers the updatedAt gap.
119+
await db.insert(upstreamDriftReports).values({
120+
id: "drift-ts-default-1",
121+
fingerprint: "fp-ts-default-1",
122+
severity: "low",
123+
summary: "schema defaultFn regression",
124+
generatedAt: "2026-07-24T00:00:00.000Z",
125+
});
126+
const [row] = await db
127+
.select()
128+
.from(upstreamDriftReports)
129+
.where(eq(upstreamDriftReports.id, "drift-ts-default-1"))
130+
.limit(1);
131+
expect(row?.updatedAt).toMatch(ISO);
132+
expect(row?.updatedAt).not.toBe("CURRENT_TIMESTAMP");
133+
expect(row?.updatedAt).not.toBe("");
134+
expect(row?.generatedAt).toBe("2026-07-24T00:00:00.000Z");
135+
});
105136
});

0 commit comments

Comments
 (0)