Overview
AchievementsNotificationsService.sendBatchNotifications() in src/achievements/achievements-notifications.service.ts is meant to send notifications for achievements unlocked today. It computes a midnight boundary:
const today = new Date();
today.setHours(0, 0, 0, 0);
const achievements = await this.userAchievementRepository.find({
where: {
unlockedAt: new Date(), // <-- exact current timestamp
notificationSent: false,
},
relations: ['user', 'achievement'],
});
There are two problems:
- The filter never matches.
unlockedAt: new Date() compares unlockedAt for exact equality with the current instant (millisecond precision), so it will essentially never match any stored row. The computed today boundary is never used — the intent was clearly a range filter such as MoreThanOrEqual(today).
- The query is unbounded. Unlike
resendFailedNotifications() (which uses take: 100 to batch), this query has no limit, so once the filter is fixed it could load an unbounded number of rows and send them all in a single tight loop.
The net effect is that scheduled batch achievement notifications are silently never sent.
Specifications
Features:
- The batch job selects achievements unlocked within the intended window (e.g. since midnight today) that have not yet been notified.
- The batch is processed in bounded chunks.
Tasks:
- Fix the
where clause to use a range predicate against the computed boundary (e.g. unlockedAt: MoreThanOrEqual(today)) instead of unlockedAt: new Date().
- Add a
take limit (and loop/paginate) so the batch is processed in bounded chunks like resendFailedNotifications().
- Add a unit test asserting that an achievement unlocked earlier today with
notificationSent = false is selected and notified.
Impacted Files:
- src/achievements/achievements-notifications.service.ts
- src/achievements/achievements-notifications.service.spec.ts (new/updated test)
Acceptance Criteria
- Achievements unlocked during the intended window with
notificationSent = false are selected and notified.
- The batch query is bounded and processes large backlogs in chunks.
- A regression test covers the corrected date filter.
Overview
AchievementsNotificationsService.sendBatchNotifications()insrc/achievements/achievements-notifications.service.tsis meant to send notifications for achievements unlocked today. It computes a midnight boundary:There are two problems:
unlockedAt: new Date()comparesunlockedAtfor exact equality with the current instant (millisecond precision), so it will essentially never match any stored row. The computedtodayboundary is never used — the intent was clearly a range filter such asMoreThanOrEqual(today).resendFailedNotifications()(which usestake: 100to batch), this query has no limit, so once the filter is fixed it could load an unbounded number of rows and send them all in a single tight loop.The net effect is that scheduled batch achievement notifications are silently never sent.
Specifications
Features:
Tasks:
whereclause to use a range predicate against the computed boundary (e.g.unlockedAt: MoreThanOrEqual(today)) instead ofunlockedAt: new Date().takelimit (and loop/paginate) so the batch is processed in bounded chunks likeresendFailedNotifications().notificationSent = falseis selected and notified.Impacted Files:
Acceptance Criteria
notificationSent = falseare selected and notified.