commit c33029b2ecba72420f0500ea25228c59ab6f1d2f
parent 3830cf3476372f3299beaf018be030d3c990aee7
Author: Florian Dold <dold@taler.net>
Date: Wed, 22 Jul 2026 16:23:04 +0200
wallet: test what happens when a crypto worker stops answering
Uses the existing test worker, which already has an operation that never
responds, and records whether a worker was terminated.
Diffstat:
1 file changed, 45 insertions(+), 2 deletions(-)
diff --git a/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.test.ts b/packages/taler-wallet-core/src/crypto/workers/crypto-dispatcher.test.ts
@@ -93,17 +93,23 @@ export class MyCryptoWorker implements CryptoWorker {
});
}
+ terminated = false;
+
/**
* Forcibly terminate the worker thread.
*/
terminate(): void {
- // This is a no-op.
+ this.terminated = true;
}
}
export class MyCryptoWorkerFactory implements CryptoWorkerFactory {
+ readonly started: MyCryptoWorker[] = [];
+
startWorker(): CryptoWorker {
- return new MyCryptoWorker();
+ const w = new MyCryptoWorker();
+ this.started.push(w);
+ return w;
}
getConcurrency(): number {
@@ -127,3 +133,40 @@ test("continues after error", async (t) => {
const resp3 = await cryptoDisp.doRpc("testSuccess", 0, {});
assert.ok((resp3 as any).testResult === 42);
});
+
+test("a worker that stops answering fails the caller", async (t) => {
+ const factory = new MyCryptoWorkerFactory();
+ const cryptoDisp = new CryptoDispatcher(factory, {
+ workerHangTimeoutMs: 1,
+ });
+
+ await assert.rejects(
+ async () => cryptoDisp.doRpc("testTimeout", 0, {}),
+ /timed out/,
+ "the caller must be rejected instead of waiting forever",
+ );
+
+ assert.strictEqual(factory.started.length, 1);
+ assert.strictEqual(
+ factory.started[0].terminated,
+ true,
+ "the worker that did not answer must be terminated",
+ );
+
+ // The worker slot has to be free again, otherwise the dispatcher queues
+ // every later request forever.
+ const resp = await cryptoDisp.doRpc("testSuccess", 0, {});
+ assert.ok((resp as any).testResult === 42);
+});
+
+test("a worker that does answer is not terminated", async (t) => {
+ const factory = new MyCryptoWorkerFactory();
+ // Far longer than the answer takes, so the timeout must not trigger.
+ const cryptoDisp = new CryptoDispatcher(factory, {
+ workerHangTimeoutMs: 60_000,
+ });
+
+ const resp = await cryptoDisp.doRpc("testSuccess", 0, {});
+ assert.ok((resp as any).testResult === 42);
+ assert.strictEqual(factory.started[0].terminated, false);
+});