From 0a17827009f00b037009a825e53842289c09b65e Mon Sep 17 00:00:00 2001 From: Edward Zhang Date: Fri, 14 Aug 2026 09:28:05 -0700 Subject: [PATCH] test(reminder): fix firebase errors --- src/jobs.ts | 3 ++- src/services/reminder.ts | 37 +++++++++++++++++++++++++------------ test/reminder.test.ts | 20 ++++++++++---------- 3 files changed, 37 insertions(+), 23 deletions(-) diff --git a/src/jobs.ts b/src/jobs.ts index b785cae..a337238 100644 --- a/src/jobs.ts +++ b/src/jobs.ts @@ -1,10 +1,11 @@ import { updateBusPositions, initializeRoutes, rebuildGraph } from './services/graphBuilder'; -import { processRideReminders, processUniversityReminders } from './services/reminder'; +import { initializeReminders, processRideReminders, processUniversityReminders } from './services/reminder'; /** * Starts background jobs for updating bus positions, initializing routes, and rebuilding the graph. */ export function startBackgroundJobs() { + initializeReminders(); initializeRoutes().then(() => { console.log("Routes initialized. Building initial graph..."); rebuildGraph(); diff --git a/src/services/reminder.ts b/src/services/reminder.ts index 8b87c78..509db08 100644 --- a/src/services/reminder.ts +++ b/src/services/reminder.ts @@ -9,8 +9,10 @@ export * from "./reminderTypes"; dotenv.config() -// Initialize Firebase -initializeApp({ credential: applicationDefault() }); +export function initializeReminders() { + // Initialize Firebase + initializeApp({ credential: applicationDefault() }); +} /** Waiting for a prediction of the right `event` to have a arrival timestamp that is at or after `mustBeAfter` and an * arrival time less than `thresh`. A bus is xx minutes from the stop notification is then sent. To handle delayed and @@ -77,9 +79,12 @@ export type RemindersToTrigger = { export class ReminderSubscriptions { subscriptions: Array<{ token: RegistrationToken, subscription: PreThreshold | PostThreshold - }> + }>; + pure: boolean; - constructor() { + /** set mock to true for tests that don't involve firebase */ + constructor(options: { mock: boolean }) { + this.pure = options.mock; this.subscriptions = []; } @@ -87,7 +92,14 @@ export class ReminderSubscriptions { event and token are removed. REQUIRES: `predsByStopId` has predictions sorted by arrival timestamp */ - add(event: BaseEvent, thresh: number, token: RegistrationToken, predsByStopId: Record, now: number) { + add( + event: BaseEvent, + thresh: number, + token: RegistrationToken, + predsByStopId: Record, + now: number, + options?: { noUpdate: boolean }, + ) { console.log("Adding a reminder subscription"); const predictions = predsByStopId[event.stpid]; const subscription = preThreshold(event, thresh, null, now); @@ -103,18 +115,19 @@ export class ReminderSubscriptions { } } // remove existing - this.remove(event, token, true); + this.remove(event, token, { noUpdate: true }); this.subscriptions.push({ token, subscription }); + if (options?.noUpdate || this.pure) return; sendReminderUpdateToAll(new Set([token])); } /** removes all subscriptions that involve both `event` and `token` */ - remove(event: BaseEvent, token: RegistrationToken, noUpdate?: boolean) { + remove(event: BaseEvent, token: RegistrationToken, options?: { noUpdate: boolean }) { console.log("Removing a reminder subscription"); this.subscriptions = this.subscriptions .filter((s) => s.token != token || !eventsEqual(s.subscription.event, event)) - if (!noUpdate) - sendReminderUpdateToAll(new Set([token])); + if (options?.noUpdate || this.pure) return; + sendReminderUpdateToAll(new Set([token])); } /** updates the status of all registrations, returning an object representing the @@ -323,8 +336,8 @@ export class ReminderSubscriptions { } } -export const universityReminderSubscriptions = new ReminderSubscriptions(); -export const rideReminderSubscriptions = new ReminderSubscriptions(); +export const universityReminderSubscriptions = new ReminderSubscriptions({ mock: false}); +export const rideReminderSubscriptions = new ReminderSubscriptions({ mock: false}); export function processUniversityReminders() { try { @@ -365,7 +378,7 @@ function processRemindersHelper( reminderSubscriptions: ReminderSubscriptions, predsByStopId: Record, predsByVid: Record, - stopIdToName: Record + stopIdToName: Record, ) { const notifications = reminderSubscriptions.process(predsByStopId, predsByVid, Date.now()); for (const [eventKey, tokens] of notifications.reminder) { diff --git a/test/reminder.test.ts b/test/reminder.test.ts index 4086703..2b58f9c 100644 --- a/test/reminder.test.ts +++ b/test/reminder.test.ts @@ -32,7 +32,7 @@ function createCaches(preds: Prediction[]): { byStop: Record { it('should not send a threshold immediately and should move to next stage after sending', () => { - const subs = new t.ReminderSubscriptions(); + const subs = new t.ReminderSubscriptions({ mock: true }); const { byStop, byVid } = createCaches([ { rt: testEvent.rtid, vid: "vid1", stpid: testEvent.stpid, prdtm: Date.now() + 2 * 60 * 1000, prdctdn: "2" } ]); @@ -69,7 +69,7 @@ describe('Reminders', () => { }); it('should not send a disappeared notification if there never was a bus in the first place', () => { - const subs = new t.ReminderSubscriptions(); + const subs = new t.ReminderSubscriptions({ mock: true }); // the subscription gets added with no possible candidate vid subs.add(testEvent, 3, testToken, {}, Date.now()); // will it trigger a disappeared reminder? @@ -78,7 +78,7 @@ describe('Reminders', () => { }); it('should not trigger for busses of other routes', () => { - const subs = new t.ReminderSubscriptions(); + const subs = new t.ReminderSubscriptions({ mock: true }); const { byStop, byVid } = createCaches([ { rt: testEventDiffRt.rtid, vid: "vid1", stpid: testEvent.stpid, prdtm: Date.now() + 4 * 60 * 1000, prdctdn: "2" } ]); @@ -88,7 +88,7 @@ describe('Reminders', () => { }); it('should only get removed by the remove method if explicitly targeted', () => { - const subs = new t.ReminderSubscriptions(); + const subs = new t.ReminderSubscriptions({ mock: true }); subs.add(testEvent, 3, testToken, {}, Date.now()); subs.add(testEventDiffRt, 3, testToken, {}, Date.now()); subs.add(testEvent, 3, r.registrationToken("anotherToken"), {}, Date.now()); @@ -98,7 +98,7 @@ describe('Reminders', () => { }); it('should send at the stop notifications', () => { - const subs = new t.ReminderSubscriptions(); + const subs = new t.ReminderSubscriptions({ mock: true }); const { byStop, byVid } = createCaches([ { rt: testEvent.rtid, vid: "vid1", stpid: testEvent.stpid, prdtm: Date.now() + 4 * 60 * 1000, prdctdn: "2" } ]); @@ -111,7 +111,7 @@ describe('Reminders', () => { }); it('should send delayed notifications', () => { - const subs = new t.ReminderSubscriptions(); + const subs = new t.ReminderSubscriptions({ mock: true }); const { byStop, byVid } = createCaches([ { rt: testEvent.rtid, vid: "vid1", stpid: testEvent.stpid, prdtm: Date.now() + 4 * 60 * 1000, prdctdn: "4" } ]); @@ -132,7 +132,7 @@ describe('Reminders', () => { }); it('should send disappeared notifications (stage 0)', () => { - const subs = new t.ReminderSubscriptions(); + const subs = new t.ReminderSubscriptions({ mock: true }); const { byStop } = createCaches([ { rt: testEvent.rtid, vid: "vid1", stpid: testEvent.stpid, prdtm: Date.now() + 4 * 60 * 1000, prdctdn: "4" } ]); @@ -142,7 +142,7 @@ describe('Reminders', () => { }); it('should send disappeared notifications (stage 1)', () => { - const subs = new t.ReminderSubscriptions(); + const subs = new t.ReminderSubscriptions({ mock: true }); const { byStop, byVid } = createCaches([ { rt: testEvent.rtid, vid: "vid1", stpid: testEvent.stpid, prdtm: Date.now() + 5 * 60 * 1000, prdctdn: "5" } ]); @@ -160,7 +160,7 @@ describe('Reminders', () => { }); it('should override some delayed notifications', () => { - const subs = new t.ReminderSubscriptions(); + const subs = new t.ReminderSubscriptions({ mock: true }); const { byStop, byVid } = createCaches([ { rt: testEvent.rtid, vid: "vid1", stpid: testEvent.stpid, prdtm: Date.now() + 4 * 60 * 1000, prdctdn: "4" } ]); @@ -182,7 +182,7 @@ describe('Reminders', () => { }); it('should override some disappeared notifications', () => { - const subs = new t.ReminderSubscriptions(); + const subs = new t.ReminderSubscriptions({ mock: true }); const { byStop, byVid } = createCaches([ { rt: testEvent.rtid, vid: "vid1", stpid: testEvent.stpid, prdtm: Date.now() + 5 * 60 * 1000, prdctdn: "5" } ]);