Closer/functions/dist/notifications/idempotency.test.js

19 lines
1.0 KiB
JavaScript
Raw Permalink Normal View History

refactor(functions): B1 migrate 13 Firestore triggers to v2 + harden Migrate all Firestore triggers off the v1 API to firebase-functions/v2/firestore (onDocumentCreated/Updated/Written); context.params→event.params, snap→event.data, change.before/after→event.data.before/after. Region stays us-central1 (global option). Hardening folded in (all reuse in-repo patterns): - Adopt the shared sendPushToUser()/getUserTokens() helper in every trigger, removing ~7 copied token readers and the copied send/prune blocks. FCM tokens are no longer logged in plaintext anywhere here (redacted inside push.ts). - console.* → firebase-functions/logger (structured). - Idempotency: new claimOnce() (atomic create-if-absent marker under couples/{id}/notif_marks) dedupes at-least-once redelivery on the non-idempotent senders (onAnswerWritten/Revealed, onMessageWritten, onCoupleLeave, onEntitlementChanged, onDateReflectionWritten/Revealed, onDateHistoryCreated). Fail-open. onGameSessionUpdate/ onGamePartFinished/notifyOnDateMatch already had transactional claim-flags — preserved. - onRestoreRequested: the plan's "claim" is implemented as the existing 60s time-WINDOW (lastRestorePartnerAlertAt), not a permanent recipientUid marker — a permanent marker would wrongly block legitimate re-requests (restore docs are deleted+recreated by design). Faithful port of onGameSessionUpdate/onGamePartFinished (broad wildcard + allowlist kept); the trigger split and read reorder are deferred to B6 as separate commits. Build clean; 70 tests green (+ new idempotency.test.ts, push token/prune tests). Emulator discovery loads all triggers as v2 in us-central1. firebase-functions still v5.1.1 (v6 bump deferred to B6). dist rebuilt. Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-07 23:46:17 -05:00
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const idempotency_1 = require("./idempotency");
function markRef(create) {
return { path: 'couples/c1/notif_marks/m1', create };
}
describe('claimOnce', () => {
it('claims (true) when the marker did not exist', async () => {
expect(await (0, idempotency_1.claimOnce)(markRef(async () => undefined))).toBe(true);
});
it('does not claim (false) when the marker already exists (ALREADY_EXISTS = 6)', async () => {
expect(await (0, idempotency_1.claimOnce)(markRef(async () => Promise.reject({ code: 6 })))).toBe(false);
});
it('fails open (true) on any other create error so a ping is not silently dropped', async () => {
expect(await (0, idempotency_1.claimOnce)(markRef(async () => Promise.reject({ code: 14 })))).toBe(true);
expect(await (0, idempotency_1.claimOnce)(markRef(async () => Promise.reject(new Error('boom'))))).toBe(true);
});
});
//# sourceMappingURL=idempotency.test.js.map