diff --git a/src/databases/officer_db/src/nudges/queries.test.ts b/src/databases/officer_db/src/nudges/queries.test.ts new file mode 100644 index 00000000..b5f27ab7 --- /dev/null +++ b/src/databases/officer_db/src/nudges/queries.test.ts @@ -0,0 +1,14 @@ +import { describe, expect, test } from 'bun:test'; +import { dueNudges } from './queries'; + +describe('dueNudges', () => { + // It failed on EVERY tick and the only symptom was a log line nobody was reading: the row sat there + // enabled, with sent_count 0, looking perfectly armed. A JS Date bound as a parameter on the left of an + // interval subtraction is a type Postgres will not infer, so it refused the whole statement. + // + // Asserting only that it EXECUTES. The due-ness logic is Postgres arithmetic; what broke was the SQL + // being invalid, which no amount of reasoning about intervals would have caught. + test('is valid SQL against the real database', async () => { + expect(Array.isArray(await dueNudges())).toBe(true); + }); +}); diff --git a/src/databases/officer_db/src/nudges/queries.ts b/src/databases/officer_db/src/nudges/queries.ts index e2e09aff..a993cd10 100644 --- a/src/databases/officer_db/src/nudges/queries.ts +++ b/src/databases/officer_db/src/nudges/queries.ts @@ -50,7 +50,7 @@ export async function cancelNudge(userId: number, sessionId: string): Promise { +export async function dueNudges(): Promise { return db .select() .from(nudges) @@ -60,7 +60,10 @@ export async function dueNudges(now: Date = new Date()): Promise lt(nudges.sentCount, nudges.maxCount), or( isNull(nudges.lastFiredAt), - lt(nudges.lastFiredAt, sql`${now} - (${nudges.everySeconds} * interval '1 second')`), + // `now()` in SQL rather than a JS Date bound as a parameter. Postgres cannot infer the type of + // a parameter on the left of an interval subtraction and refuses the whole statement — which + // failed EVERY tick, silently, with the row sitting there looking armed. + lt(nudges.lastFiredAt, sql`now() - (${nudges.everySeconds} * interval '1 second')`), ), ), );