All files / apps/notifications-service/src/notifications processed-events.store.ts

100% Statements 12/12
100% Branches 2/2
100% Functions 2/2
100% Lines 10/10

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 342x   2x                     2x 10x     15x           10008x   1x 1x 1x     10008x      
import { Injectable } from '@nestjs/common';
 
const MAX_ENTRIES = 10_000;
 
/**
 * Consumer-side idempotency: remembers which eventIds were fully processed so
 * an at-least-once redelivery doesn't send the same notification twice.
 *
 * In-memory on purpose — one instance, bounded, zero infrastructure. The
 * documented production upgrade is a shared store (Redis SETNX or a DB unique
 * insert, as the audit projection already does); the seam stays the same.
 */
@Injectable()
export class ProcessedEventsStore {
  private readonly seen = new Set<string>();
 
  has(eventId: string): boolean {
    return this.seen.has(eventId);
  }
 
  /** Call only after the side effect succeeded — marking first would turn a
   * crash between mark and send into a silently lost notification. */
  mark(eventId: string): void {
    if (this.seen.size >= MAX_ENTRIES) {
      // Sets iterate in insertion order, so the first entry is the oldest.
      for (const oldest of this.seen) {
        this.seen.delete(oldest);
        break;
      }
    }
    this.seen.add(eventId);
  }
}