commit ec559f2064a4fa8a9b2b4473d9db58ff27a54a39
parent f182fcceb124d47ff86e13801137085d0092182e
Author: Florian Dold <dold@taler.net>
Date: Thu, 20 Aug 2026 19:06:44 +0200
wallet-core: retain shared currency metadata
Diffstat:
2 files changed, 72 insertions(+), 4 deletions(-)
diff --git a/packages/taler-wallet-core/src/requests.test.ts b/packages/taler-wallet-core/src/requests.test.ts
@@ -47,6 +47,7 @@ import {
handleGetDefaultExchanges,
handleHintApplicationResumed,
handleListWithdrawalExchangeCandidates,
+ handleRemoveGlobalCurrencyExchange,
setCoinSuspended,
} from "./requests.js";
import { WalletApiOperation } from "./wallet-api-types.js";
@@ -171,6 +172,70 @@ test("application-resumed hint reports DB failures independently", async () => {
);
});
+test("removing one global exchange retains shared currency metadata", async () => {
+ const exchanges = [
+ {
+ id: 1,
+ currency: "TESTKUDOS",
+ exchangeBaseUrl: "https://one.example/",
+ exchangeMasterPub: "master-one",
+ },
+ {
+ id: 2,
+ currency: "TESTKUDOS",
+ exchangeBaseUrl: "https://two.example/",
+ exchangeMasterPub: "master-two",
+ },
+ ];
+ let currencyInfoDeletes = 0;
+ const tx = {
+ async getGlobalCurrencyExchange(
+ currency: string,
+ exchangeBaseUrl: string,
+ exchangeMasterPub: string,
+ ) {
+ return exchanges.find(
+ (x) =>
+ x.currency === currency &&
+ x.exchangeBaseUrl === exchangeBaseUrl &&
+ x.exchangeMasterPub === exchangeMasterPub,
+ );
+ },
+ async deleteGlobalCurrencyExchange(id: number): Promise<void> {
+ const index = exchanges.findIndex((x) => x.id === id);
+ if (index >= 0) exchanges.splice(index, 1);
+ },
+ async listGlobalCurrencyExchanges() {
+ return exchanges;
+ },
+ async deleteCurrencyInfo(): Promise<void> {
+ currencyInfoDeletes++;
+ },
+ } as unknown as WalletDbTransaction;
+ const wex = {
+ async runWalletDbTx<T>(
+ f: (tx: WalletDbTransaction) => Promise<T>,
+ ): Promise<T> {
+ return f(tx);
+ },
+ ws: { exchangeCache: { clear() {} } },
+ } as WalletExecutionContext;
+
+ await handleRemoveGlobalCurrencyExchange(wex, {
+ currency: "TESTKUDOS",
+ exchangeBaseUrl: "https://one.example/",
+ exchangeMasterPub: "master-one",
+ });
+ assert.strictEqual(currencyInfoDeletes, 0);
+
+ await handleRemoveGlobalCurrencyExchange(wex, {
+ currency: "TESTKUDOS",
+ exchangeBaseUrl: "https://two.example/",
+ exchangeMasterPub: "master-two",
+ });
+ assert.strictEqual(currencyInfoDeletes, 1);
+});
+
function makeCoinSuspensionContext(
status: CoinStatus,
freshCoinCount: number,
diff --git a/packages/taler-wallet-core/src/requests.ts b/packages/taler-wallet-core/src/requests.ts
@@ -1745,11 +1745,14 @@ export async function handleRemoveGlobalCurrencyExchange(
!!existingRec.id,
`no global exchange for ${req.currency}`,
);
- await tx.deleteCurrencyInfo({
- type: ScopeType.Global,
- currency: req.currency,
- });
await tx.deleteGlobalCurrencyExchange(existingRec.id);
+ const remainingExchanges = await tx.listGlobalCurrencyExchanges();
+ if (!remainingExchanges.some((x) => x.currency === req.currency)) {
+ await tx.deleteCurrencyInfo({
+ type: ScopeType.Global,
+ currency: req.currency,
+ });
+ }
});
return {};
}