Skip to content

Commit 7d3cfa8

Browse files
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 <[email protected]>
1 parent 48d12dc commit 7d3cfa8

2 files changed

Lines changed: 44 additions & 11 deletions

File tree

src/infra/session-maintenance-warning.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,4 +199,37 @@ describe("deliverSessionMaintenanceWarning", () => {
199199
{ sessionKey: params.sessionKey },
200200
]);
201201
});
202+
203+
it.each([
204+
[59_500, "60 seconds", "1 minute"],
205+
[3_570_000, "60 minutes", "1 hour"],
206+
[86_370_000, "24 hours", "1 day"],
207+
])(
208+
"formatDuration rolls over %dms to next unit instead of %s",
209+
async (pruneAfterMs, _buggyOutput, expected) => {
210+
mocks.deliverOutboundPayloads.mockRejectedValueOnce(new Error("force system event"));
211+
const params = createParams({
212+
warning: { pruneAfterMs, wouldPrune: true, wouldCap: false, maxEntries: 100 } as never,
213+
});
214+
215+
await deliverSessionMaintenanceWarning(params);
216+
217+
expect(firstSystemEventCall()?.[0]).toContain(`older than ${expected}`);
218+
},
219+
);
220+
221+
it.each([
222+
[30_000, "30 seconds"],
223+
[1_800_000, "30 minutes"],
224+
[43_200_000, "12 hours"],
225+
])("formatDuration keeps %dms in its own unit as %s", async (pruneAfterMs, expected) => {
226+
mocks.deliverOutboundPayloads.mockRejectedValueOnce(new Error("force system event"));
227+
const params = createParams({
228+
warning: { pruneAfterMs, wouldPrune: true, wouldCap: false, maxEntries: 100 } as never,
229+
});
230+
231+
await deliverSessionMaintenanceWarning(params);
232+
233+
expect(firstSystemEventCall()?.[0]).toContain(`older than ${expected}`);
234+
});
202235
});

src/infra/session-maintenance-warning.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,20 @@ function buildWarningContext(params: WarningParams): string {
5454
}
5555

5656
function formatDuration(ms: number): string {
57-
if (ms >= 86_400_000) {
58-
const days = Math.round(ms / 86_400_000);
59-
return `${days} day${days === 1 ? "" : "s"}`;
60-
}
61-
if (ms >= 3_600_000) {
62-
const hours = Math.round(ms / 3_600_000);
63-
return `${hours} hour${hours === 1 ? "" : "s"}`;
57+
const secs = Math.round(ms / 1000);
58+
if (secs < 60) {
59+
return `${secs} second${secs === 1 ? "" : "s"}`;
6460
}
65-
if (ms >= 60_000) {
66-
const mins = Math.round(ms / 60_000);
61+
const mins = Math.round(secs / 60);
62+
if (mins < 60) {
6763
return `${mins} minute${mins === 1 ? "" : "s"}`;
6864
}
69-
const secs = Math.round(ms / 1000);
70-
return `${secs} second${secs === 1 ? "" : "s"}`;
65+
const hours = Math.round(mins / 60);
66+
if (hours < 24) {
67+
return `${hours} hour${hours === 1 ? "" : "s"}`;
68+
}
69+
const days = Math.round(hours / 24);
70+
return `${days} day${days === 1 ? "" : "s"}`;
7171
}
7272

7373
function buildWarningText(warning: SessionMaintenanceWarning): string {

0 commit comments

Comments
 (0)