commit 6200fcdd87c1539dea55568bdeb59ab87d57b329
parent 61157df2531fed7db753bbce30e2a5faaef9fb77
Author: Florian Dold <dold@taler.net>
Date: Tue, 21 Jul 2026 02:18:05 +0200
util: test that the observable HTTP client releases cancellation callbacks
Diffstat:
1 file changed, 54 insertions(+), 0 deletions(-)
diff --git a/packages/taler-util/src/observability.test.ts b/packages/taler-util/src/observability.test.ts
@@ -0,0 +1,54 @@
+/*
+ This file is part of GNU Taler
+ (C) 2024 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 { CancellationToken } from "./CancellationToken.js";
+import { HttpRequestLibrary, HttpResponse } from "./http-common.js";
+import { ObservableHttpClientLibrary } from "./observability.js";
+
+function stubResponse(url: string): HttpResponse {
+ return {
+ requestUrl: url,
+ requestMethod: "GET",
+ status: 200,
+ headers: new Headers(),
+ json: async () => ({}),
+ text: async () => "",
+ bytes: async () => new Uint8Array(),
+ };
+}
+
+test("ObservableHttpClientLibrary releases the caller's cancellation callback", async (t) => {
+ const inner: HttpRequestLibrary = {
+ fetch: async (url) => stubResponse(url),
+ };
+ const client = new ObservableHttpClientLibrary(inner, { observe: () => {} });
+ const source = CancellationToken.create();
+
+ // The Set of registered callbacks is exposed for leak detection.
+ const callbacks = (): Set<unknown> => (source.token as any)._callbacks;
+
+ for (let i = 0; i < 50; i++) {
+ await client.fetch("http://example.com/", {
+ cancellationToken: source.token,
+ });
+ }
+
+ // A completed request must unregister the callback it added to the caller's
+ // long-lived token, not leave one behind per request.
+ assert.strictEqual(callbacks().size, 0);
+});