mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 04:50:59 +00:00
test(jobs): follow the queue guards onto the new binding
Thirty-three suites read a registrar as source text and matched `boss.work` next to a queue constant. They now match `createAndWork`, and the seven schedule tuples that grew a `cronIsTheRetry` third element assert that element rather than accepting any arity — a guard that accepts more than it did before is a guard that notices less. Two negative assertions were tightened while they were open: the "has no cron" checks on the arrival and workout-insight queues ended at a literal `]`, so a cron added WITH send options would have slipped past. The behavioural repairs carry the real change. Handlers that resolved to undefined now resolve to their outcome, so the assertions name the counts instead of asserting nothing. The reconcile tick's test said it swallows a failure instead of throwing; it reports a failed outcome now, and the test says that, with the comment pointing at the retryLimit that carries the old anti-retry-loop concern.
This commit is contained in:
1 parent
f0a804a812
commit
ae294573ef
33 files changed
+208
-127
No files matched your search
@@ -73,12 +73,16 @@ describe("periodic reconcile — wiring", () => {
|
||||
/const schedules[\s\S]*?=\s*\[([\s\S]*?)\];/,
|
||||
);
|
||||
expect(schedules).not.toBeNull();
|
||||
// The third element pins the retry policy. It is load-bearing now that
|
||||
// the handler reports a failed outcome instead of swallowing: without
|
||||
// `cronIsTheRetry` a database outage would run the same doomed sweep
|
||||
// three times per tick, and the next tick is already the retry.
|
||||
expect(schedules![1]).toMatch(
|
||||
/\[IMPORT_JOB_RECONCILE_QUEUE,\s*IMPORT_JOB_RECONCILE_CRON\]/,
|
||||
/\[IMPORT_JOB_RECONCILE_QUEUE,\s*IMPORT_JOB_RECONCILE_CRON,\s*cronIsTheRetry\]/,
|
||||
);
|
||||
|
||||
expect(maintenanceSource).toMatch(
|
||||
/boss\.work\(\s*IMPORT_JOB_RECONCILE_QUEUE[\s\S]{0,120}handleImportJobReconcileTick/,
|
||||
/createAndWork\(\s*boss,\s*IMPORT_JOB_RECONCILE_QUEUE[\s\S]{0,120}handleImportJobReconcileTick/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -163,24 +167,30 @@ describe("reconcileOrphanImportJobs", () => {
|
||||
});
|
||||
|
||||
describe("handleImportJobReconcileTick", () => {
|
||||
it("delegates to reconcileOrphanImportJobs and resolves on success", async () => {
|
||||
it("delegates to reconcileOrphanImportJobs and reports a done outcome", async () => {
|
||||
mocks.findMany.mockResolvedValue([]);
|
||||
|
||||
await expect(
|
||||
handleImportJobReconcileTick([] as never),
|
||||
).resolves.toBeUndefined();
|
||||
await expect(handleImportJobReconcileTick([] as never)).resolves.toEqual({
|
||||
ok: true,
|
||||
did: {},
|
||||
});
|
||||
expect(mocks.findMany).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("swallows a reconcile failure instead of throwing", async () => {
|
||||
mocks.findMany.mockRejectedValue(new Error("db unavailable"));
|
||||
it("reports a failed outcome when the reconcile pass throws", async () => {
|
||||
const cause = new Error("db unavailable");
|
||||
mocks.findMany.mockRejectedValue(cause);
|
||||
|
||||
// A background sweep this cheap must never spam pg-boss's
|
||||
// retry/backoff machinery — it just re-runs on the next 15-minute
|
||||
// tick. The handler must resolve even when the underlying reconcile
|
||||
// pass throws.
|
||||
await expect(
|
||||
handleImportJobReconcileTick([] as never),
|
||||
).resolves.toBeUndefined();
|
||||
// The handler used to swallow this and resolve, which left a sweep
|
||||
// that had not run for hours indistinguishable from one that found
|
||||
// nothing to do. It now names the failure, `runJob` rethrows it, and
|
||||
// pg-boss records a failed job. The queue carries `cronIsTheRetry`
|
||||
// (retryLimit 0) because the next 15-minute tick is the retry — a
|
||||
// database that is down stays down for an immediate re-run.
|
||||
await expect(handleImportJobReconcileTick([] as never)).resolves.toEqual({
|
||||
ok: false,
|
||||
reason: "apple health import reconcile failed",
|
||||
cause,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -87,11 +87,14 @@ describe("apple health import — parser revision boundary", () => {
|
||||
expect(allQueues).not.toBeNull();
|
||||
expect(allQueues![1]).toMatch(/\bAPPLE_HEALTH_IMPORT_V2_QUEUE\b/);
|
||||
expect(allQueues![1]).toMatch(/\bAPPLE_HEALTH_IMPORT_LEGACY_QUEUE\b/);
|
||||
// The v2 window is the wider of the two: its binding wraps the batch in
|
||||
// a loop that keeps the first failed outcome, so the handler call sits
|
||||
// further from the queue name than the legacy bridge's does.
|
||||
expect(maintenanceSource).toMatch(
|
||||
/boss\.work[\s\S]{0,200}APPLE_HEALTH_IMPORT_V2_QUEUE[\s\S]{0,400}handleAppleHealthImport/,
|
||||
/createAndWork[\s\S]{0,200}APPLE_HEALTH_IMPORT_V2_QUEUE[\s\S]{0,600}handleAppleHealthImport/,
|
||||
);
|
||||
expect(maintenanceSource).toMatch(
|
||||
/boss\.work[\s\S]{0,200}APPLE_HEALTH_IMPORT_LEGACY_QUEUE[\s\S]{0,400}migrateLegacyAppleHealthImport/,
|
||||
/createAndWork[\s\S]{0,200}APPLE_HEALTH_IMPORT_LEGACY_QUEUE[\s\S]{0,400}migrateLegacyAppleHealthImport/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Same source-text-grep approach as the period-narrative / stress-strain
|
||||
* guards: assert the queue is imported, registered in `allQueues`, and wired
|
||||
* to a `boss.work` handler that runs the refresh pipeline — without booting
|
||||
* to a `createAndWork` handler that runs the refresh pipeline — without booting
|
||||
* pg-boss + Prisma. An unregistered queue silently never runs (the recurring
|
||||
* past bug this guards against; v1.4.37 W10 caught exactly this class).
|
||||
*/
|
||||
@@ -41,9 +41,9 @@ describe("reminder-worker — coach-memory-refresh wiring", () => {
|
||||
expect(allQueuesBlock()).toMatch(/\bCOACH_MEMORY_REFRESH_QUEUE\b/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler that runs the refresh pipeline", () => {
|
||||
it("registers a createAndWork handler that runs the refresh pipeline", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}COACH_MEMORY_REFRESH_QUEUE[\s\S]{0,400}runCoachMemoryRefresh/,
|
||||
/createAndWork[\s\S]{0,200}COACH_MEMORY_REFRESH_QUEUE[\s\S]{0,600}runCoachMemoryRefresh/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -2,7 +2,7 @@
|
||||
* v1.18.7 — coach-message-cleanup queue registration guard.
|
||||
*
|
||||
* Same source-text-grep approach as the other queue-wiring guards: assert the
|
||||
* queue is registered in `allQueues`, scheduled, and wired to a `boss.work`
|
||||
* queue is registered in `allQueues`, scheduled, and wired to a `createAndWork`
|
||||
* handler — without booting pg-boss + Prisma. An unregistered queue silently
|
||||
* never drains (the recurring v1.4.37 dead-queue bug), which would leave the
|
||||
* encrypted Coach history growing unbounded with the retention job dark.
|
||||
@@ -41,13 +41,13 @@ describe("reminder-worker — coach-message-cleanup wiring", () => {
|
||||
|
||||
it("schedules the coach-message-cleanup cron", () => {
|
||||
expect(workerSource).toMatch(
|
||||
/\[COACH_MESSAGE_CLEANUP_QUEUE,\s*COACH_MESSAGE_CLEANUP_CRON\]/,
|
||||
/\[COACH_MESSAGE_CLEANUP_QUEUE,\s*COACH_MESSAGE_CLEANUP_CRON,\s*cronIsTheRetry\]/,
|
||||
);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler for the queue", () => {
|
||||
it("registers a createAndWork handler for the queue", () => {
|
||||
expect(workerSource).toMatch(
|
||||
/boss\.work[\s\S]{0,200}COACH_MESSAGE_CLEANUP_QUEUE[\s\S]{0,200}handleCoachMessageCleanup/,
|
||||
/createAndWork[\s\S]{0,200}COACH_MESSAGE_CLEANUP_QUEUE[\s\S]{0,200}handleCoachMessageCleanup/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* registration guard.
|
||||
*
|
||||
* Same source-text-grep approach as the step-consolidation-repair guard:
|
||||
* assert the queue is registered in `allQueues`, a `boss.work` handler is
|
||||
* assert the queue is registered in `allQueues`, a `createAndWork` handler is
|
||||
* wired against it, and the boot-discovery enqueue helper fires — without
|
||||
* booting pg-boss + Prisma. An unregistered queue silently never drains
|
||||
* (the v1.4.37 dead-queue class).
|
||||
@@ -34,9 +34,9 @@ describe("reminder-worker — cumulative-pr-rederive wiring", () => {
|
||||
expect(allQueuesMatch![1]).toMatch(/\bCUMULATIVE_PR_REDERIVE_QUEUE\b/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler against the queue", () => {
|
||||
it("registers a createAndWork handler against the queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}CUMULATIVE_PR_REDERIVE_QUEUE[\s\S]{0,400}runCumulativePrRederivationForUser/,
|
||||
/createAndWork[\s\S]{0,200}CUMULATIVE_PR_REDERIVE_QUEUE[\s\S]{0,400}runCumulativePrRederivationForUser/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* consolidation registration guards: assert the cycle-reminder queue is
|
||||
* declared, listed in `allQueues` (the v1.4.37 dead-queue lesson — an
|
||||
* unregistered queue is never provisioned and the schedule silently
|
||||
* no-ops), scheduled on its cron, and wired to a `boss.work` handler that
|
||||
* no-ops), scheduled on its cron, and wired to a `createAndWork` handler that
|
||||
* delegates to `runCycleReminderTick`. No pg-boss / Prisma boot.
|
||||
*/
|
||||
import { readFileSync } from "node:fs";
|
||||
@@ -57,9 +57,9 @@ describe("reminder-worker — cycle-reminder wiring", () => {
|
||||
expect(source).toMatch(/\[CYCLE_REMINDER_QUEUE,\s*CYCLE_REMINDER_CRON\]/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler that runs the cycle-reminder tick", () => {
|
||||
it("registers a createAndWork handler that runs the cycle-reminder tick", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}CYCLE_REMINDER_QUEUE[\s\S]{0,200}handleCycleReminderCheck/,
|
||||
/createAndWork[\s\S]{0,200}CYCLE_REMINDER_QUEUE[\s\S]{0,200}handleCycleReminderCheck/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/handleCycleReminderCheck[\s\S]{0,400}runCycleReminderTick/,
|
||||
|
||||
@@ -98,9 +98,9 @@ describe("reminder-worker — daily-briefing wiring", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("wires a boss.work handler that runs the tick", () => {
|
||||
it("wires a createAndWork handler that runs the tick", () => {
|
||||
expect(registrar).toMatch(
|
||||
/boss\.work[\s\S]{0,200}DAILY_BRIEFING_QUEUE[\s\S]{0,400}runDailyBriefingTick/,
|
||||
/createAndWork[\s\S]{0,200}DAILY_BRIEFING_QUEUE[\s\S]{0,400}runDailyBriefingTick/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -39,9 +39,9 @@ describe("data-arrival queue wiring", () => {
|
||||
expect(allQueues![1]).toMatch(/\bDATA_ARRIVAL_QUEUE\b/);
|
||||
});
|
||||
|
||||
it("binds a boss.work handler that drains it", () => {
|
||||
it("binds a createAndWork handler that drains it", () => {
|
||||
expect(statusRegistrar).toMatch(
|
||||
/boss\.work[\s\S]{0,200}DATA_ARRIVAL_QUEUE[\s\S]{0,200}handleDataArrival/,
|
||||
/createAndWork[\s\S]{0,200}DATA_ARRIVAL_QUEUE[\s\S]{0,200}handleDataArrival/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -61,7 +61,11 @@ describe("data-arrival queue wiring", () => {
|
||||
});
|
||||
|
||||
it("has no cron schedule — it is send-only, driven by ingest", () => {
|
||||
expect(statusRegistrar).not.toMatch(/\[DATA_ARRIVAL_QUEUE,\s*\w+_CRON\]/);
|
||||
// Tolerates the optional third schedule element, so a cron sneaking in
|
||||
// with send options still trips this.
|
||||
expect(statusRegistrar).not.toMatch(
|
||||
/\[\s*DATA_ARRIVAL_QUEUE,\s*\w+_CRON\s*[,\]]/,
|
||||
);
|
||||
});
|
||||
|
||||
it("registers the retention job that prunes the markers", () => {
|
||||
@@ -71,10 +75,10 @@ describe("data-arrival queue wiring", () => {
|
||||
expect(allQueues).not.toBeNull();
|
||||
expect(allQueues![1]).toMatch(/\bARRIVAL_REACTION_CLEANUP_QUEUE\b/);
|
||||
expect(maintenanceSource).toMatch(
|
||||
/\[ARRIVAL_REACTION_CLEANUP_QUEUE,\s*ARRIVAL_REACTION_CLEANUP_CRON\]/,
|
||||
/\[\s*ARRIVAL_REACTION_CLEANUP_QUEUE,\s*ARRIVAL_REACTION_CLEANUP_CRON,\s*cronIsTheRetry,?\s*\]/,
|
||||
);
|
||||
expect(maintenanceSource).toMatch(
|
||||
/boss\.work[\s\S]{0,200}ARRIVAL_REACTION_CLEANUP_QUEUE[\s\S]{0,200}handleArrivalReactionCleanup/,
|
||||
/createAndWork[\s\S]{0,200}ARRIVAL_REACTION_CLEANUP_QUEUE[\s\S]{0,200}handleArrivalReactionCleanup/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -50,15 +50,15 @@ describe("reminder-worker — drainPerSampleCumulative nightly schedule", () =>
|
||||
expect(source).toMatch(/\bDRAIN_CUMULATIVE_CUTOFF_HOURS\b/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler against the drain queue", () => {
|
||||
it("registers a createAndWork handler against the drain queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}DRAIN_CUMULATIVE_QUEUE[\s\S]{0,400}drainPerSampleCumulative/,
|
||||
/createAndWork[\s\S]{0,200}DRAIN_CUMULATIVE_QUEUE[\s\S]{0,900}drainPerSampleCumulative/,
|
||||
);
|
||||
});
|
||||
|
||||
it("schedules the drain cron via boss.schedule (allQueues + schedules)", () => {
|
||||
expect(source).toMatch(
|
||||
/\[DRAIN_CUMULATIVE_QUEUE,\s*DRAIN_CUMULATIVE_CRON\]/,
|
||||
/\[DRAIN_CUMULATIVE_QUEUE,\s*DRAIN_CUMULATIVE_CRON,\s*cronIsTheRetry\]/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -58,22 +58,22 @@ describe("reminder-worker — Fitbit sync queues", () => {
|
||||
it("schedules the hourly poll cron + the daily oauth-state sweep", () => {
|
||||
expect(source).toMatch(/\[FITBIT_SYNC_QUEUE,\s*FITBIT_SYNC_CRON\]/);
|
||||
expect(source).toMatch(
|
||||
/\[FITBIT_OAUTH_STATE_CLEANUP_QUEUE,\s*FITBIT_OAUTH_STATE_CLEANUP_CRON\]/,
|
||||
/\[\s*FITBIT_OAUTH_STATE_CLEANUP_QUEUE,\s*FITBIT_OAUTH_STATE_CLEANUP_CRON,\s*cronIsTheRetry,?\s*\]/,
|
||||
);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler for every Fitbit queue", () => {
|
||||
it("registers a createAndWork handler for every Fitbit queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,160}FITBIT_SYNC_QUEUE[\s\S]{0,160}handleFitbitSync/,
|
||||
/createAndWork[\s\S]{0,160}FITBIT_SYNC_QUEUE[\s\S]{0,160}handleFitbitSync/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}FITBIT_BACKFILL_QUEUE[\s\S]{0,260}enqueueIntegrationBackfillAdmission/,
|
||||
/createAndWork[\s\S]{0,200}FITBIT_BACKFILL_QUEUE[\s\S]{0,260}enqueueIntegrationBackfillAdmission/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}FITBIT_SLEEP_REPAIR_QUEUE[\s\S]{0,260}enqueueIntegrationBackfillAdmission/,
|
||||
/createAndWork[\s\S]{0,200}FITBIT_SLEEP_REPAIR_QUEUE[\s\S]{0,260}enqueueIntegrationBackfillAdmission/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,160}FITBIT_OAUTH_STATE_CLEANUP_QUEUE[\s\S]{0,160}handleFitbitOAuthStateCleanup/,
|
||||
/createAndWork[\s\S]{0,160}FITBIT_OAUTH_STATE_CLEANUP_QUEUE[\s\S]{0,160}handleFitbitOAuthStateCleanup/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -61,19 +61,19 @@ describe("reminder-worker — Google Health sync queues", () => {
|
||||
/\[GOOGLE_HEALTH_SYNC_QUEUE,\s*GOOGLE_HEALTH_SYNC_CRON\]/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/\[\s*GOOGLE_HEALTH_OAUTH_STATE_CLEANUP_QUEUE,\s*GOOGLE_HEALTH_OAUTH_STATE_CLEANUP_CRON,?\s*\]/,
|
||||
/\[\s*GOOGLE_HEALTH_OAUTH_STATE_CLEANUP_QUEUE,\s*GOOGLE_HEALTH_OAUTH_STATE_CLEANUP_CRON,\s*cronIsTheRetry,?\s*\]/,
|
||||
);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler for every Google Health queue", () => {
|
||||
it("registers a createAndWork handler for every Google Health queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}GOOGLE_HEALTH_SYNC_QUEUE[\s\S]{0,200}handleGoogleHealthSync/,
|
||||
/createAndWork[\s\S]{0,200}GOOGLE_HEALTH_SYNC_QUEUE[\s\S]{0,200}handleGoogleHealthSync/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,320}GOOGLE_HEALTH_BACKFILL_QUEUE[\s\S]{0,320}enqueueIntegrationBackfillAdmission/,
|
||||
/createAndWork[\s\S]{0,320}GOOGLE_HEALTH_BACKFILL_QUEUE[\s\S]{0,320}enqueueIntegrationBackfillAdmission/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}GOOGLE_HEALTH_OAUTH_STATE_CLEANUP_QUEUE[\s\S]{0,200}handleGoogleHealthOAuthStateCleanup/,
|
||||
/createAndWork[\s\S]{0,200}GOOGLE_HEALTH_OAUTH_STATE_CLEANUP_QUEUE[\s\S]{0,200}handleGoogleHealthOAuthStateCleanup/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -1181,9 +1181,9 @@ describe("queue registration", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler for the queue", () => {
|
||||
it("registers a createAndWork handler for the queue", () => {
|
||||
expect(workerSrc).toMatch(
|
||||
/boss\.work[\s\S]{0,80}INSIGHT_PREGENERATE_QUEUE/,
|
||||
/createAndWork[\s\S]{0,80}INSIGHT_PREGENERATE_QUEUE/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* - an unknown metric is annotated and skipped, not thrown;
|
||||
* - the enqueue helper no-ops without a global boss instance and de-dupes
|
||||
* with a singletonKey when one is present;
|
||||
* - the queue is registered in `allQueues` AND wired to a `boss.work`
|
||||
* - the queue is registered in `allQueues` AND wired to a `createAndWork`
|
||||
* handler in reminder-worker.ts (the v1.4.37 W10 unregistered-queue
|
||||
* catch — a queue built but never registered silently never drains).
|
||||
*/
|
||||
@@ -171,9 +171,9 @@ describe("queue registration", () => {
|
||||
expect(match![1]).toMatch(/\bINSIGHT_STATUS_GENERATE_QUEUE\b/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler for the queue", () => {
|
||||
it("registers a createAndWork handler for the queue", () => {
|
||||
expect(workerSrc).toMatch(
|
||||
/boss\.work[\s\S]{0,120}INSIGHT_STATUS_GENERATE_QUEUE/,
|
||||
/createAndWork[\s\S]{0,120}INSIGHT_STATUS_GENERATE_QUEUE/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Same source-text-grep approach as the drain + step-consolidation +
|
||||
* mean-consolidation regression guards: assert the queue is registered
|
||||
* in `allQueues`, a `boss.work` handler is wired against it, and the
|
||||
* in `allQueues`, a `createAndWork` handler is wired against it, and the
|
||||
* boot-discovery enqueue helper is called — without booting pg-boss +
|
||||
* Prisma. An unregistered queue silently never drains (the past incident
|
||||
* this guard exists to prevent).
|
||||
@@ -42,12 +42,12 @@ describe("reminder-worker — intake-slot-dedup wiring", () => {
|
||||
expect(allQueuesMatch![1]).toMatch(/\bINTAKE_SLOT_DEDUP_QUEUE\b/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler against the intake-slot-dedup queue", () => {
|
||||
it("registers a createAndWork handler against the intake-slot-dedup queue", () => {
|
||||
// v1.15.19 — span widened from {0,400}: the handler gained the daily
|
||||
// cron-tick dispatch branch (no `userId` → discovery fan-out) between
|
||||
// the queue name and the per-user dedup call.
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}INTAKE_SLOT_DEDUP_QUEUE[\s\S]{0,1200}dedupeUserIntakeSlots/,
|
||||
/createAndWork[\s\S]{0,200}INTAKE_SLOT_DEDUP_QUEUE[\s\S]{0,1400}dedupeUserIntakeSlots/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* MCP Phase 3 (M2) — mcp-token-cleanup queue registration guard.
|
||||
*
|
||||
* Source-text-grep guard like the other queue-wiring checks: assert the queue
|
||||
* is in `allQueues`, scheduled, and wired to a `boss.work` handler. An
|
||||
* is in `allQueues`, scheduled, and wired to a `createAndWork` handler. An
|
||||
* unregistered queue silently never drains (the recurring v1.4.37 dead-queue
|
||||
* bug), which would leave expired MCP connector access tokens growing unbounded.
|
||||
*/
|
||||
@@ -40,13 +40,13 @@ describe("reminder-worker — mcp-token-cleanup wiring", () => {
|
||||
|
||||
it("schedules the mcp-token-cleanup cron", () => {
|
||||
expect(workerSource).toMatch(
|
||||
/\[MCP_TOKEN_CLEANUP_QUEUE,\s*MCP_TOKEN_CLEANUP_CRON\]/,
|
||||
/\[MCP_TOKEN_CLEANUP_QUEUE,\s*MCP_TOKEN_CLEANUP_CRON,\s*cronIsTheRetry\]/,
|
||||
);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler for the queue", () => {
|
||||
it("registers a createAndWork handler for the queue", () => {
|
||||
expect(workerSource).toMatch(
|
||||
/boss\.work[\s\S]{0,200}MCP_TOKEN_CLEANUP_QUEUE[\s\S]{0,200}handleMcpTokenCleanup/,
|
||||
/createAndWork[\s\S]{0,200}MCP_TOKEN_CLEANUP_QUEUE[\s\S]{0,200}handleMcpTokenCleanup/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Same source-text-grep approach as the drain + step-consolidation
|
||||
* regression guards: assert the queue is registered in `allQueues`, a
|
||||
* `boss.work` handler is wired against it, and the boot-discovery
|
||||
* `createAndWork` handler is wired against it, and the boot-discovery
|
||||
* enqueue helper is called — without booting pg-boss + Prisma. An
|
||||
* unregistered queue silently never drains.
|
||||
*/
|
||||
@@ -34,9 +34,9 @@ describe("reminder-worker — mean-consolidation wiring", () => {
|
||||
expect(allQueuesMatch![1]).toMatch(/\bMEAN_CONSOLIDATION_QUEUE\b/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler against the mean-consolidation queue", () => {
|
||||
it("registers a createAndWork handler against the mean-consolidation queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}MEAN_CONSOLIDATION_QUEUE[\s\S]{0,400}runMeanConsolidationForUser/,
|
||||
/createAndWork[\s\S]{0,200}MEAN_CONSOLIDATION_QUEUE[\s\S]{0,400}runMeanConsolidationForUser/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* guards: assert the Vorsorge-reminder queue is declared, listed in
|
||||
* `allQueues` (the v1.4.37 dead-queue lesson — an unregistered queue is
|
||||
* never provisioned and the schedule silently no-ops), scheduled on its
|
||||
* cron, and wired to a `boss.work` handler that delegates to
|
||||
* cron, and wired to a `createAndWork` handler that delegates to
|
||||
* `runMeasurementReminderTick`. No pg-boss / Prisma boot.
|
||||
*/
|
||||
import { readFileSync } from "node:fs";
|
||||
@@ -56,9 +56,9 @@ describe("reminder-worker — measurement-reminder wiring", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler that runs the tick", () => {
|
||||
it("registers a createAndWork handler that runs the tick", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}MEASUREMENT_REMINDER_QUEUE[\s\S]{0,200}handleMeasurementReminderCheck/,
|
||||
/createAndWork[\s\S]{0,200}MEASUREMENT_REMINDER_QUEUE[\s\S]{0,200}handleMeasurementReminderCheck/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/handleMeasurementReminderCheck[\s\S]{0,400}runMeasurementReminderTick/,
|
||||
@@ -80,12 +80,12 @@ describe("reminder-worker — eventful reminder-satisfy wiring (v1.18.1)", () =>
|
||||
expect(allQueuesMatch![1]).toMatch(/\bREMINDER_SATISFY_QUEUE\b/);
|
||||
});
|
||||
|
||||
it("binds a boss.work handler for the satisfy queue", () => {
|
||||
it("binds a createAndWork handler for the satisfy queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}REMINDER_SATISFY_QUEUE[\s\S]{0,200}handleReminderSatisfy/,
|
||||
/createAndWork[\s\S]{0,200}REMINDER_SATISFY_QUEUE[\s\S]{0,200}handleReminderSatisfy/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/handleReminderSatisfy[\s\S]{0,600}runReminderSatisfyForUser/,
|
||||
/handleReminderSatisfy[\s\S]{0,800}runReminderSatisfyForUser/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -7,7 +7,7 @@
|
||||
* recently-tombstoned rows untouched.
|
||||
* 2. Source-grep wiring (same approach as the drain-cumulative guard):
|
||||
* the queue is registered in `allQueues`, scheduled, and bound to a
|
||||
* `boss.work` handler — a missing `allQueues` entry silently no-ops
|
||||
* `createAndWork` handler — a missing `allQueues` entry silently no-ops
|
||||
* the schedule under pg-boss v12.
|
||||
*/
|
||||
import { readFileSync } from "node:fs";
|
||||
@@ -114,9 +114,9 @@ describe("reminder-worker — measurement-tombstone-cleanup schedule", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("binds a boss.work handler to the queue", () => {
|
||||
it("binds a createAndWork handler to the queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}MEASUREMENT_TOMBSTONE_CLEANUP_QUEUE[\s\S]{0,200}handleMeasurementTombstoneCleanup/,
|
||||
/createAndWork[\s\S]{0,200}MEASUREMENT_TOMBSTONE_CLEANUP_QUEUE[\s\S]{0,200}handleMeasurementTombstoneCleanup/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -50,9 +50,9 @@ describe("reminder-worker — Nightscout sync queue", () => {
|
||||
expect(source).toMatch(/\[NIGHTSCOUT_SYNC_QUEUE,\s*NIGHTSCOUT_SYNC_CRON\]/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler for the queue", () => {
|
||||
it("registers a createAndWork handler for the queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,160}NIGHTSCOUT_SYNC_QUEUE[\s\S]{0,160}handleNightscoutSync/,
|
||||
/createAndWork[\s\S]{0,160}NIGHTSCOUT_SYNC_QUEUE[\s\S]{0,160}handleNightscoutSync/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -85,12 +85,12 @@ describe("reminder-worker — Polar / Oura sync queues", () => {
|
||||
expect(source).toMatch(/\[OURA_SYNC_QUEUE,\s*OURA_SYNC_CRON\]/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler for each queue", () => {
|
||||
it("registers a createAndWork handler for each queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,160}POLAR_SYNC_QUEUE[\s\S]{0,160}handlePolarSync/,
|
||||
/createAndWork[\s\S]{0,160}POLAR_SYNC_QUEUE[\s\S]{0,160}handlePolarSync/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,160}OURA_SYNC_QUEUE[\s\S]{0,160}handleOuraSync/,
|
||||
/createAndWork[\s\S]{0,160}OURA_SYNC_QUEUE[\s\S]{0,160}handleOuraSync/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -196,8 +196,10 @@ describe("queue registration", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler for the queue", () => {
|
||||
expect(workerSrc).toMatch(/boss\.work[\s\S]{0,120}PERIOD_NARRATIVE_QUEUE/);
|
||||
it("registers a createAndWork handler for the queue", () => {
|
||||
expect(workerSrc).toMatch(
|
||||
/createAndWork[\s\S]{0,120}PERIOD_NARRATIVE_QUEUE/,
|
||||
);
|
||||
});
|
||||
|
||||
it("exposes a sane queue name + nightly cron", () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Same source-text-grep approach as the other queue-wiring guards: assert
|
||||
* the queue is imported, registered in `allQueues`, scheduled, and wired to
|
||||
* a `boss.work` handler — without booting pg-boss + Prisma. An unregistered
|
||||
* a `createAndWork` handler — without booting pg-boss + Prisma. An unregistered
|
||||
* queue silently never drains (the recurring past bug this guards against).
|
||||
*
|
||||
* Also pins the iOS-write rejection of the server-owned COMPUTED source on
|
||||
@@ -57,9 +57,9 @@ describe("reminder-worker — recovery-score wiring", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler that runs the recovery-score pass", () => {
|
||||
it("registers a createAndWork handler that runs the recovery-score pass", () => {
|
||||
expect(workerSource).toMatch(
|
||||
/boss\.work[\s\S]{0,200}RECOVERY_SCORE_QUEUE[\s\S]{0,400}runRecoveryScore/,
|
||||
/createAndWork[\s\S]{0,200}RECOVERY_SCORE_QUEUE[\s\S]{0,400}runRecoveryScore/,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* The reminder-worker imports heavy infrastructure, so we read the source as
|
||||
* text and assert the queue constant is declared, registered in `allQueues`,
|
||||
* wired to a `boss.work` handler, and enqueued at boot — the fast guard that
|
||||
* wired to a `createAndWork` handler, and enqueued at boot — the fast guard that
|
||||
* catches the v1.4.37 dead-queue class (a queue declared but never registered,
|
||||
* which silently never drains).
|
||||
*/
|
||||
@@ -30,9 +30,9 @@ describe("reminder-worker — sleep-timeline backfill queue", () => {
|
||||
expect(block![1]!).toContain("SLEEP_TIMELINE_BACKFILL_QUEUE");
|
||||
});
|
||||
|
||||
it("wires a boss.work handler through the shared admission queue", () => {
|
||||
it("wires a createAndWork handler through the shared admission queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,260}SLEEP_TIMELINE_BACKFILL_QUEUE[\s\S]{0,300}enqueueIntegrationBackfillAdmission/,
|
||||
/createAndWork[\s\S]{0,260}SLEEP_TIMELINE_BACKFILL_QUEUE[\s\S]{0,300}enqueueIntegrationBackfillAdmission/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -196,8 +196,8 @@ describe("mood-status queue registration (dead-queue guard)", () => {
|
||||
expect(workerSrc).toMatch(/MOOD_STATUS_CRON\s*=\s*"30 2 \* \* \*"/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler for the queue", () => {
|
||||
expect(workerSrc).toMatch(/boss\.work[\s\S]{0,120}MOOD_STATUS_QUEUE/);
|
||||
it("registers a createAndWork handler for the queue", () => {
|
||||
expect(workerSrc).toMatch(/createAndWork[\s\S]{0,120}MOOD_STATUS_QUEUE/);
|
||||
});
|
||||
|
||||
it("drives all seven status crons through the shared discovery", () => {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* Same source-text-grep approach as the drain + step-consolidation +
|
||||
* mean-consolidation guards: assert the repair queue is registered in
|
||||
* `allQueues`, a `boss.work` handler is wired against it, and the
|
||||
* `allQueues`, a `createAndWork` handler is wired against it, and the
|
||||
* boot-discovery enqueue helper fires — without booting pg-boss + Prisma.
|
||||
* An unregistered queue silently never drains (the dead-queue class).
|
||||
*/
|
||||
@@ -36,9 +36,9 @@ describe("reminder-worker — step-consolidation-repair wiring", () => {
|
||||
expect(allQueuesMatch![1]).toMatch(/\bSTEP_CONSOLIDATION_REPAIR_QUEUE\b/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler against the repair queue", () => {
|
||||
it("registers a createAndWork handler against the repair queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}STEP_CONSOLIDATION_REPAIR_QUEUE[\s\S]{0,400}runStepConsolidationRepairForUser/,
|
||||
/createAndWork[\s\S]{0,200}STEP_CONSOLIDATION_REPAIR_QUEUE[\s\S]{0,400}runStepConsolidationRepairForUser/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
* Same source-text-grep approach as the WX-C recovery-score guard: assert
|
||||
* each queue is imported, registered in `allQueues`, scheduled (the two
|
||||
* cron-driven score jobs), and wired to a `boss.work` handler — without
|
||||
* cron-driven score jobs), and wired to a `createAndWork` handler — without
|
||||
* booting pg-boss + Prisma. An unregistered queue silently never drains
|
||||
* (the recurring past bug this guards against).
|
||||
*/
|
||||
@@ -67,9 +67,9 @@ describe("reminder-worker — stress-score wiring", () => {
|
||||
expect(source).toMatch(/\[STRESS_SCORE_QUEUE,\s*STRESS_SCORE_CRON\]/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler that runs the stress-score pass", () => {
|
||||
it("registers a createAndWork handler that runs the stress-score pass", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}STRESS_SCORE_QUEUE[\s\S]{0,400}runStressScore/,
|
||||
/createAndWork[\s\S]{0,200}STRESS_SCORE_QUEUE[\s\S]{0,400}runStressScore/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -90,9 +90,9 @@ describe("reminder-worker — strain-score wiring", () => {
|
||||
expect(source).toMatch(/\[STRAIN_SCORE_QUEUE,\s*STRAIN_SCORE_CRON\]/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler that runs the strain-score pass", () => {
|
||||
it("registers a createAndWork handler that runs the strain-score pass", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}STRAIN_SCORE_QUEUE[\s\S]{0,400}runStrainScore/,
|
||||
/createAndWork[\s\S]{0,200}STRAIN_SCORE_QUEUE[\s\S]{0,400}runStrainScore/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -111,9 +111,9 @@ describe("reminder-worker — dense intra-day retention wiring", () => {
|
||||
expect(allQueuesBlock()).toMatch(/\bDENSE_INTRADAY_RETENTION_QUEUE\b/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler against the dense-intraday-retention queue", () => {
|
||||
it("registers a createAndWork handler against the dense-intraday-retention queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,250}DENSE_INTRADAY_RETENTION_QUEUE[\s\S]{0,500}runDenseIntradayRetentionForUser/,
|
||||
/createAndWork[\s\S]{0,250}DENSE_INTRADAY_RETENTION_QUEUE[\s\S]{0,500}runDenseIntradayRetentionForUser/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -163,9 +163,9 @@ describe("reminder-worker — dense hourly-rebuild wiring", () => {
|
||||
expect(allQueuesBlock()).toMatch(/\bDENSE_INTRADAY_HOURLY_REBUILD_QUEUE\b/);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler against the hourly-rebuild queue", () => {
|
||||
it("registers a createAndWork handler against the hourly-rebuild queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,250}DENSE_INTRADAY_HOURLY_REBUILD_QUEUE[\s\S]{0,500}runDenseIntradayHourlyRebuildForUser/,
|
||||
/createAndWork[\s\S]{0,250}DENSE_INTRADAY_HOURLY_REBUILD_QUEUE[\s\S]{0,500}runDenseIntradayHourlyRebuildForUser/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Same source-text-grep approach as the other queue-wiring guards: assert
|
||||
* the queue is imported, registered in `allQueues`, scheduled, and wired to
|
||||
* a `boss.work` handler — without booting pg-boss + Prisma. An unregistered
|
||||
* a `createAndWork` handler — without booting pg-boss + Prisma. An unregistered
|
||||
* queue silently never drains (the recurring v1.4.37 dead-queue bug), which
|
||||
* would leave the pinned-leaf-rotation alarm dark.
|
||||
*/
|
||||
@@ -50,9 +50,9 @@ describe("reminder-worker — tls-pin-monitor wiring", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler for the queue", () => {
|
||||
it("registers a createAndWork handler for the queue", () => {
|
||||
expect(workerSource).toMatch(
|
||||
/boss\.work[\s\S]{0,200}TLS_PIN_MONITOR_QUEUE[\s\S]{0,200}handleTlsPinMonitor/,
|
||||
/createAndWork[\s\S]{0,200}TLS_PIN_MONITOR_QUEUE[\s\S]{0,200}handleTlsPinMonitor/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -77,25 +77,25 @@ describe("reminder-worker — WHOOP sync queues", () => {
|
||||
/\[WHOOP_CYCLE_SYNC_QUEUE,\s*WHOOP_CYCLE_SYNC_CRON\]/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/\[WHOOP_OAUTH_STATE_CLEANUP_QUEUE,\s*WHOOP_OAUTH_STATE_CLEANUP_CRON\]/,
|
||||
/\[\s*WHOOP_OAUTH_STATE_CLEANUP_QUEUE,\s*WHOOP_OAUTH_STATE_CLEANUP_CRON,\s*cronIsTheRetry,?\s*\]/,
|
||||
);
|
||||
});
|
||||
|
||||
it("registers a boss.work handler for every WHOOP sync queue", () => {
|
||||
it("registers a createAndWork handler for every WHOOP sync queue", () => {
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,160}WHOOP_RECOVERY_SYNC_QUEUE[\s\S]{0,160}handleWhoopRecoverySync/,
|
||||
/createAndWork[\s\S]{0,160}WHOOP_RECOVERY_SYNC_QUEUE[\s\S]{0,160}handleWhoopRecoverySync/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,160}WHOOP_SLEEP_SYNC_QUEUE[\s\S]{0,160}handleWhoopSleepSync/,
|
||||
/createAndWork[\s\S]{0,160}WHOOP_SLEEP_SYNC_QUEUE[\s\S]{0,160}handleWhoopSleepSync/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,160}WHOOP_WORKOUT_SYNC_QUEUE[\s\S]{0,160}handleWhoopWorkoutSync/,
|
||||
/createAndWork[\s\S]{0,160}WHOOP_WORKOUT_SYNC_QUEUE[\s\S]{0,160}handleWhoopWorkoutSync/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,160}WHOOP_CYCLE_SYNC_QUEUE[\s\S]{0,160}handleWhoopCycleSync/,
|
||||
/createAndWork[\s\S]{0,160}WHOOP_CYCLE_SYNC_QUEUE[\s\S]{0,160}handleWhoopCycleSync/,
|
||||
);
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}WHOOP_BACKFILL_QUEUE[\s\S]{0,260}enqueueIntegrationBackfillAdmission/,
|
||||
/createAndWork[\s\S]{0,200}WHOOP_BACKFILL_QUEUE[\s\S]{0,260}enqueueIntegrationBackfillAdmission/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -128,7 +128,18 @@ describe("runWhoopResourceSync dispatch", () => {
|
||||
findManyMock.mockResolvedValue([{ userId: "a" }, { userId: "b" }]);
|
||||
syncUserWorkout.mockRejectedValueOnce(new Error("boom for a"));
|
||||
|
||||
await expect(handleWhoopWorkoutSync([job({})])).resolves.toBeUndefined();
|
||||
// The pass is what the outcome judges: the sweep ran, so the job is
|
||||
// done. The revoked-grant user rides out as `users_failed` rather than
|
||||
// failing the queue and retrying the whole cohort over one account.
|
||||
await expect(handleWhoopWorkoutSync([job({})])).resolves.toEqual({
|
||||
ok: true,
|
||||
did: {
|
||||
total: 2,
|
||||
users_synced: 1,
|
||||
users_failed: 1,
|
||||
measurements_imported: 0,
|
||||
},
|
||||
});
|
||||
// Both users were attempted despite the first throwing.
|
||||
expect(syncUserWorkout).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
@@ -93,7 +93,15 @@ describe("handleWithingsEcgSync", () => {
|
||||
enddate: 1715000060,
|
||||
});
|
||||
|
||||
await expect(handleWithingsEcgSync([queued])).resolves.toBeUndefined();
|
||||
await expect(handleWithingsEcgSync([queued])).resolves.toEqual({
|
||||
ok: true,
|
||||
did: {
|
||||
total: 1,
|
||||
users_synced: 1,
|
||||
users_failed: 0,
|
||||
recordings_imported: 1,
|
||||
},
|
||||
});
|
||||
|
||||
expect(ecgFindMany).not.toHaveBeenCalled();
|
||||
expect(syncUserEcg).toHaveBeenCalledTimes(1);
|
||||
@@ -120,14 +128,24 @@ describe("handleWithingsEcgSync", () => {
|
||||
|
||||
it("one user's failure never starves the rest of the cohort", async () => {
|
||||
ecgFindMany.mockResolvedValue([{ userId: "a" }, { userId: "b" }]);
|
||||
syncUserEcg.mockResolvedValue(0);
|
||||
syncUserEcg.mockRejectedValueOnce(new Error("boom for a"));
|
||||
|
||||
// Per-user catch-and-warn (mirrors the activity / sleep handlers): the
|
||||
// handler resolves, both users attempted, the leaf records its own ledger
|
||||
// failure so honesty is preserved without rejecting the whole batch.
|
||||
// pass ran, so the job is done and the failing user rides out as a
|
||||
// `users_failed` count. The leaf records its own ledger failure, so
|
||||
// honesty is preserved without rejecting the whole batch.
|
||||
await expect(
|
||||
handleWithingsEcgSync([job({} as WithingsEcgSyncPayload)]),
|
||||
).resolves.toBeUndefined();
|
||||
).resolves.toEqual({
|
||||
ok: true,
|
||||
did: {
|
||||
total: 2,
|
||||
users_synced: 1,
|
||||
users_failed: 1,
|
||||
recordings_imported: 0,
|
||||
},
|
||||
});
|
||||
expect(syncUserEcg).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
});
|
||||
@@ -143,7 +161,7 @@ describe("withings ECG queue registration", () => {
|
||||
/\[WITHINGS_ECG_SYNC_QUEUE\]:\s*\{[\s\S]*?policy:\s*"short"/,
|
||||
);
|
||||
expect(registrarSource).toMatch(
|
||||
/boss\.work<WithingsEcgSyncPayload>\([\s\S]*?WITHINGS_ECG_SYNC_QUEUE[\s\S]*?handleWithingsEcgSync/,
|
||||
/createAndWork<WithingsEcgSyncPayload>\(\s*boss,\s*WITHINGS_ECG_SYNC_QUEUE[\s\S]{0,120}handleWithingsEcgSync/,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -50,14 +50,14 @@ describe("reminder-worker — Withings activity + sleep v2 queues", () => {
|
||||
it("registers the activity-sync handler against pg-boss", () => {
|
||||
expect(source).toMatch(/handleWithingsActivitySync\b/);
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}WITHINGS_ACTIVITY_QUEUE[\s\S]{0,200}handleWithingsActivitySync/,
|
||||
/createAndWork[\s\S]{0,200}WITHINGS_ACTIVITY_QUEUE[\s\S]{0,200}handleWithingsActivitySync/,
|
||||
);
|
||||
});
|
||||
|
||||
it("registers the sleep-sync handler against pg-boss", () => {
|
||||
expect(source).toMatch(/handleWithingsSleepSync\b/);
|
||||
expect(source).toMatch(
|
||||
/boss\.work[\s\S]{0,200}WITHINGS_SLEEP_QUEUE[\s\S]{0,200}handleWithingsSleepSync/,
|
||||
/createAndWork[\s\S]{0,200}WITHINGS_SLEEP_QUEUE[\s\S]{0,200}handleWithingsSleepSync/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -78,7 +78,7 @@ describe("reminder-worker — Withings activity + sleep v2 queues", () => {
|
||||
});
|
||||
|
||||
it("schedules the ECG catch-net cron at :41 so watch-only accounts pull strips", () => {
|
||||
// The queue is already in allQueues + boss.work-bound; W-1 adds the missing
|
||||
// The queue is already in allQueues + createAndWork-bound; W-1 adds the missing
|
||||
// schedule entry so a payload-less cron tick walks the connection cohort.
|
||||
expect(source).toMatch(
|
||||
/WITHINGS_ECG_SYNC_CRON\s*=\s*["']41 \* \* \* \*["']/,
|
||||
|
||||
@@ -72,7 +72,16 @@ describe("handleWithingsFallbackSync subscription repair", () => {
|
||||
data: { triggeredAt: "2026-07-21T10:00:00.000Z" },
|
||||
} as Job<WithingsSyncPayload>;
|
||||
|
||||
await handleWithingsFallbackSync([queued]);
|
||||
await expect(handleWithingsFallbackSync([queued])).resolves.toEqual({
|
||||
ok: true,
|
||||
did: {
|
||||
total: 0,
|
||||
users_synced: 0,
|
||||
users_failed: 0,
|
||||
measurements_imported: 0,
|
||||
subscription_repair_failed: false,
|
||||
},
|
||||
});
|
||||
|
||||
expect(retryDueWithingsWebhookSubscriptions).toHaveBeenCalledTimes(1);
|
||||
expect(syncUserMeasurements).not.toHaveBeenCalled();
|
||||
@@ -89,7 +98,19 @@ describe("handleWithingsFallbackSync subscription repair", () => {
|
||||
data: { triggeredAt: "2026-07-21T11:00:00.000Z" },
|
||||
} as Job<WithingsSyncPayload>;
|
||||
|
||||
await expect(handleWithingsFallbackSync([queued])).resolves.toBeUndefined();
|
||||
// The repair is an auxiliary leg, not the pass this queue is named for:
|
||||
// the fallback sync still ran for everyone, so the job is done and the
|
||||
// repair failure rides out as a fact on the outcome.
|
||||
await expect(handleWithingsFallbackSync([queued])).resolves.toEqual({
|
||||
ok: true,
|
||||
did: {
|
||||
total: 2,
|
||||
users_synced: 2,
|
||||
users_failed: 0,
|
||||
measurements_imported: 0,
|
||||
subscription_repair_failed: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(findMany).toHaveBeenCalledWith({ select: { userId: true } });
|
||||
expect(syncUserMeasurements).toHaveBeenNthCalledWith(
|
||||
|
||||
@@ -32,9 +32,9 @@ describe("workout-insight-generate queue wiring", () => {
|
||||
expect(allQueues![1]).toMatch(/\bWORKOUT_INSIGHT_GENERATE_QUEUE\b/);
|
||||
});
|
||||
|
||||
it("binds a boss.work handler that drains it", () => {
|
||||
it("binds a createAndWork handler that drains it", () => {
|
||||
expect(statusRegistrar).toMatch(
|
||||
/boss\.work[\s\S]{0,300}WORKOUT_INSIGHT_GENERATE_QUEUE[\s\S]{0,300}handleWorkoutInsightGenerate/,
|
||||
/createAndWork[\s\S]{0,300}WORKOUT_INSIGHT_GENERATE_QUEUE[\s\S]{0,300}handleWorkoutInsightGenerate/,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -50,8 +50,10 @@ describe("workout-insight-generate queue wiring", () => {
|
||||
it("has no cron schedule — it is dispatched by arrival only", () => {
|
||||
// A cron here would be a standing invitation to regenerate, which is the
|
||||
// saturation surface the design refuses.
|
||||
// Tolerates the optional third schedule element, so a cron sneaking in
|
||||
// with send options still trips this.
|
||||
expect(statusRegistrar).not.toMatch(
|
||||
/\[WORKOUT_INSIGHT_GENERATE_QUEUE,\s*\w+_CRON\]/,
|
||||
/\[\s*WORKOUT_INSIGHT_GENERATE_QUEUE,\s*\w+_CRON\s*[,\]]/,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -107,7 +107,17 @@ describe("makePollCohortHandler — marker-aware recordFailure boundary", () =>
|
||||
recordFailure,
|
||||
});
|
||||
|
||||
await expect(handler([job()])).resolves.toBeUndefined();
|
||||
// The recorder rejection is warned, not propagated: the pass ran, so the
|
||||
// job is done and the failing user is a `users_failed` count.
|
||||
await expect(handler([job()])).resolves.toEqual({
|
||||
ok: true,
|
||||
did: {
|
||||
total: 2,
|
||||
users_synced: 1,
|
||||
users_failed: 1,
|
||||
measurements_imported: 1,
|
||||
},
|
||||
});
|
||||
// The next user still synced despite the recorder throwing.
|
||||
expect(syncUser).toHaveBeenCalledTimes(2);
|
||||
expect(syncUser).toHaveBeenLastCalledWith("next");
|
||||
|
||||
Reference in new issue
Block a user