taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit f6fa69844bc790b6dbec56f88848254870b1326e
parent 0005c5b5ba41d13dbd3e331366956f9939816464
Author: Florian Dold <dold@taler.net>
Date:   Mon, 20 Jul 2026 20:43:16 +0200

util: test duration parsing and timer lifetime

Diffstat:
Mpackages/taler-util/src/time.test.ts | 10++++++++++
Apackages/taler-util/src/timer.test.ts | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 80 insertions(+), 0 deletions(-)

diff --git a/packages/taler-util/src/time.test.ts b/packages/taler-util/src/time.test.ts @@ -38,3 +38,13 @@ test("duration parsing", (t) => { Duration.fromPrettyString("5 5 s"); }); }); + +test("duration parsing rejects a trailing number with no unit", (t) => { + // A number that is never flushed into the accumulator was silently + // discarded, so these all yielded a *smaller* duration with no error. + assert.throws(() => Duration.fromPrettyString("5")); + assert.throws(() => Duration.fromPrettyString("1h 30")); + assert.throws(() => Duration.fromPrettyString("1h30")); + assert.throws(() => Duration.fromPrettyString("")); + assert.throws(() => Duration.fromPrettyString(" ")); +}); diff --git a/packages/taler-util/src/timer.test.ts b/packages/taler-util/src/timer.test.ts @@ -0,0 +1,70 @@ +/* + This file is part of GNU Taler + (C) 2026 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +import { test } from "node:test"; +import assert from "node:assert"; +import { TimerGroup, SetTimeoutTimerAPI } from "./timer.js"; +import { Duration } from "./time.js"; +import { CancellationToken } from "./CancellationToken.js"; + +/** + * Number of timers the group is currently tracking. A one-shot timer that has + * already fired must not be tracked any more, otherwise the map grows for the + * lifetime of the group. + */ +function trackedTimers(tg: TimerGroup): number { + return Object.keys((tg as any).timerMap).length; +} + +test("TimerGroup releases one-shot timers once they have fired", async (t) => { + const tg = new TimerGroup(new SetTimeoutTimerAPI()); + for (let i = 0; i < 50; i++) { + tg.after(1, () => {}); + } + await new Promise((r) => setTimeout(r, 80)); + assert.strictEqual(trackedTimers(tg), 0); +}); + +test("TimerGroup releases timers created by resolveAfter", async (t) => { + const tg = new TimerGroup(new SetTimeoutTimerAPI()); + await Promise.all( + Array.from({ length: 50 }, () => + tg.resolveAfter(Duration.fromMilliseconds(1)), + ), + ); + assert.strictEqual(trackedTimers(tg), 0); +}); + +test("TimerGroup keeps interval timers registered", async (t) => { + const tg = new TimerGroup(new SetTimeoutTimerAPI()); + const h = tg.every(1, () => {}); + await new Promise((r) => setTimeout(r, 40)); + assert.strictEqual(trackedTimers(tg), 1); + h.clear(); + assert.strictEqual(trackedTimers(tg), 0); +}); + +test("resolveAfter does not schedule when already cancelled", async (t) => { + // onCancelled fires synchronously for an already-cancelled token, at which + // point there is no timer handle to clear -- so the code must not go on to + // schedule one for a promise that has already settled. + const src = CancellationToken.create(); + src.cancel(); + const tg = new TimerGroup(new SetTimeoutTimerAPI()); + const res = await tg.resolveAfter(Duration.fromMilliseconds(3000), src.token); + assert.strictEqual(res, false); + assert.strictEqual(trackedTimers(tg), 0); +});