From 7d3cfa85dca1a109f58bbfc8ee9cfd42d39558c8 Mon Sep 17 00:00:00 2001 From: Narahari Raghava <70995755+NarahariRaghava@users.noreply.github.com> Date: Sat, 4 Jul 2026 21:02:30 -0500 Subject: [PATCH] fix(infra): roll session warning durations across unit boundaries (#100096) * fix(infra): formatDuration rolls over near-boundary values to the next unit The raw float was checked against the boundary before rounding, so values like 59.5s, 59m30s, and 23h59m30s rounded up to "60 seconds", "60 minutes", and "24 hours" in session maintenance warning messages instead of "1 minute", "1 hour", and "1 day". Round first, then check the unit boundary. Closes #99978 * fix(infra): fix formatDuration progressive rollover in session maintenance warning Round seconds first, then promote to the next unit only when the rounded lower unit reaches its overflow threshold (60s->min, 60m->hr, 24h->day). The previous approach compared raw milliseconds against the unit boundary before rounding, causing half-unit values like 30s, 30m, and 12h to promote one unit too early. Closes #99978 * test(infra): expose formatDuration via testing export for direct verification * ci: retrigger checks * ci: retrigger checks * test(infra): keep duration formatter private --------- Co-authored-by: Vincent Koc --- src/infra/session-maintenance-warning.test.ts | 33 +++++++++++++++++++ src/infra/session-maintenance-warning.ts | 22 ++++++------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/infra/session-maintenance-warning.test.ts b/src/infra/session-maintenance-warning.test.ts index 3cf0ee72eeae..7fd1a454d367 100644 --- a/src/infra/session-maintenance-warning.test.ts +++ b/src/infra/session-maintenance-warning.test.ts @@ -199,4 +199,37 @@ describe("deliverSessionMaintenanceWarning", () => { { sessionKey: params.sessionKey }, ]); }); + + it.each([ + [59_500, "60 seconds", "1 minute"], + [3_570_000, "60 minutes", "1 hour"], + [86_370_000, "24 hours", "1 day"], + ])( + "formatDuration rolls over %dms to next unit instead of %s", + async (pruneAfterMs, _buggyOutput, expected) => { + mocks.deliverOutboundPayloads.mockRejectedValueOnce(new Error("force system event")); + const params = createParams({ + warning: { pruneAfterMs, wouldPrune: true, wouldCap: false, maxEntries: 100 } as never, + }); + + await deliverSessionMaintenanceWarning(params); + + expect(firstSystemEventCall()?.[0]).toContain(`older than ${expected}`); + }, + ); + + it.each([ + [30_000, "30 seconds"], + [1_800_000, "30 minutes"], + [43_200_000, "12 hours"], + ])("formatDuration keeps %dms in its own unit as %s", async (pruneAfterMs, expected) => { + mocks.deliverOutboundPayloads.mockRejectedValueOnce(new Error("force system event")); + const params = createParams({ + warning: { pruneAfterMs, wouldPrune: true, wouldCap: false, maxEntries: 100 } as never, + }); + + await deliverSessionMaintenanceWarning(params); + + expect(firstSystemEventCall()?.[0]).toContain(`older than ${expected}`); + }); }); diff --git a/src/infra/session-maintenance-warning.ts b/src/infra/session-maintenance-warning.ts index 5871de1311c3..9248de3d5f1c 100644 --- a/src/infra/session-maintenance-warning.ts +++ b/src/infra/session-maintenance-warning.ts @@ -54,20 +54,20 @@ function buildWarningContext(params: WarningParams): string { } function formatDuration(ms: number): string { - if (ms >= 86_400_000) { - const days = Math.round(ms / 86_400_000); - return `${days} day${days === 1 ? "" : "s"}`; + const secs = Math.round(ms / 1000); + if (secs < 60) { + return `${secs} second${secs === 1 ? "" : "s"}`; } - if (ms >= 3_600_000) { - const hours = Math.round(ms / 3_600_000); - return `${hours} hour${hours === 1 ? "" : "s"}`; - } - if (ms >= 60_000) { - const mins = Math.round(ms / 60_000); + const mins = Math.round(secs / 60); + if (mins < 60) { return `${mins} minute${mins === 1 ? "" : "s"}`; } - const secs = Math.round(ms / 1000); - return `${secs} second${secs === 1 ? "" : "s"}`; + const hours = Math.round(mins / 60); + if (hours < 24) { + return `${hours} hour${hours === 1 ? "" : "s"}`; + } + const days = Math.round(hours / 24); + return `${days} day${days === 1 ? "" : "s"}`; } function buildWarningText(warning: SessionMaintenanceWarning): string {