taler-typescript-core

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

commit 9d4254f9708c83bc4771ff7ac0690295a0fa760f
parent 09a3d09ee6c718006176e78901d6c3291f105ebc
Author: Florian Dold <dold@taler.net>
Date:   Thu,  3 Sep 2026 15:32:33 +0200

db: test that a dead helper and an aborted transaction fail

Diffstat:
Mpackages/idb-bridge/src/node-helper-sqlite3-impl.test.ts | 30++++++++++++++++++++++++++++++
Mpackages/taler-wallet-core/src/db/indexeddb/database.test.ts | 29++++++++++++++++++++++++++++-
2 files changed, 58 insertions(+), 1 deletion(-)

diff --git a/packages/idb-bridge/src/node-helper-sqlite3-impl.test.ts b/packages/idb-bridge/src/node-helper-sqlite3-impl.test.ts @@ -112,3 +112,33 @@ test("sqlite3 helper", async (t) => { await assert.rejects(db.prepare("SELECT 1"), /database is closed/); await impl.shutdown(); }); + +// The helper serves one connection at a time and exits on a second open; +// callers must see that as an error rather than wait forever. +test("sqlite3 helper exit rejects pending and later requests", async (t) => { + const impl = await createNodeHelperSqlite3Impl(); + t.after(async () => await impl.shutdown().catch(() => {})); + const db = await impl.open(":memory:"); + const withTimeout = <T>(p: Promise<T>): Promise<T | "timeout"> => + Promise.race([ + p, + new Promise<"timeout">((resolve) => + setTimeout(() => resolve("timeout"), 5000).unref(), + ), + ]); + await assert.rejects( + withTimeout(impl.open(":memory:")).then((r) => { + if (r === "timeout") throw Error("request hung"); + return r; + }), + (err: unknown) => { + assert(err instanceof Error); + assert.doesNotMatch(err.message, /hung/); + return true; + }, + ); + await assert.rejects(withTimeout(db.exec("SELECT 1")).then((r) => { + if (r === "timeout") throw Error("request hung"); + return r; + }), /helper/); +}); diff --git a/packages/taler-wallet-core/src/db/indexeddb/database.test.ts b/packages/taler-wallet-core/src/db/indexeddb/database.test.ts @@ -14,7 +14,11 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { BridgeIDBFactory, createSqliteBackend } from "@gnu-taler/idb-bridge"; +import { + BridgeIDBFactory, + createSqliteBackend, + IDBDatabase, +} from "@gnu-taler/idb-bridge"; import { createNodeHelperSqlite3Impl } from "@gnu-taler/idb-bridge/node-helper-sqlite3-impl"; import assert from "node:assert"; import { test } from "node:test"; @@ -23,6 +27,7 @@ import { abortTalerDatabaseReplacement, beginTalerDatabaseReplacement, openTalerDatabase, + promiseFromTransaction, publishTalerDatabaseReplacement, } from "./database.js"; import { exportSingleDb } from "./dump.js"; @@ -271,3 +276,25 @@ test("export closes its database connection", async () => { ); }); }); + +test("an aborted transaction without pending requests settles its promise", async () => { + const handle = await makeIdbRunner(); + try { + const db = (handle as any).idbHandle as IDBDatabase; + const tx = db.transaction(["config"], "readwrite"); + const prom = promiseFromTransaction(tx); + tx.abort(); + const outcome = await Promise.race([ + prom.then( + () => "resolved", + () => "rejected", + ), + new Promise<string>((resolve) => + setTimeout(() => resolve("hung"), 3000).unref(), + ), + ]); + assert.strictEqual(outcome, "rejected"); + } finally { + await handle.close(); + } +});