commit bd595927c041e93cd0fb881304abf4fcc0539624
parent 51920edbf7702dfc52a4c55a6471972d50221ece
Author: Florian Dold <florian@dold.me>
Date: Sat, 14 Feb 2026 00:56:46 +0100
test for refresh redenomination, flight recorder
Diffstat:
6 files changed, 70 insertions(+), 7 deletions(-)
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -218,6 +218,7 @@ import { runWithdrawalManualTest } from "./test-withdrawal-manual.js";
import { runWithdrawalPrepareTest } from "./test-withdrawal-prepare.js";
import { runMerchantBankBadWireTargetTest } from "./test-merchant-bank-bad-wire-target.js";
import { runWebMerchantLoginTest } from "./web/test-merchant-login.js";
+import { runWalletRefreshRedenominateTest } from "./test-wallet-refresh-redenominate.js";
/**
* Test runner.
@@ -419,6 +420,7 @@ const allTests: TestMainFunction[] = [
runMerchantBankBadWireTargetTest,
runWalletBbanTest,
runCurrencyScopeSeparationTest,
+ runWalletRefreshRedenominateTest,
];
export interface TestRunSpec {
diff --git a/packages/taler-util/src/types-taler-wallet.ts b/packages/taler-util/src/types-taler-wallet.ts
@@ -4077,6 +4077,10 @@ export interface TestingGetDiagnosticsResponse {
}[];
}
+export interface TestingGetFlightRecordsResponse {
+ flightRecords: FlightRecordEntry[];
+}
+
export const codecForTestingGetDenomStatsRequest =
(): Codec<TestingGetDenomStatsRequest> =>
buildCodecForObject<TestingGetDenomStatsRequest>()
@@ -4701,3 +4705,13 @@ export const codecForGetDonauStatementsRequest =
buildCodecForObject<GetDonauStatementsRequest>()
.property("donauBaseUrl", codecOptional(codecForString()))
.build("GetDonauStatementsRequest");
+
+export interface FlightRecordEntry {
+ timestamp: TalerPreciseTimestamp;
+ target: string;
+ event: FlightRecordEvent;
+}
+
+export enum FlightRecordEvent {
+ MeltGone = "melt-gone",
+}
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
@@ -1510,7 +1510,7 @@ advancedCli
.action(async (args) => {
await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
const diagResp = await wallet.client.call(
- WalletApiOperation.TestingGetDiagnostics,
+ WalletApiOperation.GetDiagnostics,
{},
);
console.log(j2s(diagResp));
@@ -1520,12 +1520,12 @@ advancedCli
advancedCli
.subcommand("performanceStats", "performance-stats", {
help: "Print performance stats.",
-})
+ })
.maybeOption("limit", ["--limit"], clk.INT, {
help: "Limit each table to n entries.",
})
.action(async (args) => {
- await withWallet(args, { lazyTaskLoop: true}, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
const statsResp = await wallet.client.call(
WalletApiOperation.TestingGetPerformanceStats,
{ limit: args.performanceStats.limit },
diff --git a/packages/taler-wallet-core/src/refresh.ts b/packages/taler-wallet-core/src/refresh.ts
@@ -43,6 +43,7 @@ import {
ExchangeMeltRequestV2,
ExchangeRefreshRevealRequestV2,
ExchangeRefundRequest,
+ FlightRecordEvent,
fnutil,
ForceRefreshRequest,
ForceRefreshResponse,
@@ -848,6 +849,13 @@ async function handleRefreshMeltGone(
// FIXME: Validate signature, possibly even store.
+ logger.warn(`handling melt Gone response from exchange`);
+
+ ctx.wex.ws.addFdr({
+ event: FlightRecordEvent.MeltGone,
+ target: ctx.transactionId,
+ });
+
await ctx.wex.db.runReadWriteTx(
{
storeNames: [
diff --git a/packages/taler-wallet-core/src/wallet-api-types.ts b/packages/taler-wallet-core/src/wallet-api-types.ts
@@ -180,6 +180,7 @@ import {
TestingGetDenomStatsRequest,
TestingGetDenomStatsResponse,
TestingGetDiagnosticsResponse,
+ TestingGetFlightRecordsResponse,
TestingGetReserveHistoryRequest,
TestingPlanMigrateExchangeBaseUrlRequest,
TestingSetTimetravelRequest,
@@ -368,9 +369,12 @@ export enum WalletApiOperation {
TestingWaitExchangeWalletKyc = "testingWaitWalletKyc",
TestingPlanMigrateExchangeBaseUrl = "testingPlanMigrateExchangeBaseUrl",
TestingRunFixup = "testingRunFixup",
- TestingGetDiagnostics = "getDiagnostics",
+ TestingGetFlightRecords = "testingGetFlightRecords",
TestingGetPerformanceStats = "testingGetPerformanceStats",
+ // Diagnostics
+ GetDiagnostics = "getDiagnostics",
+
// Hints
HintNetworkAvailability = "hintNetworkAvailability",
@@ -1633,11 +1637,17 @@ export type TestingRunFixupOp = {
};
export type TestingGetDiagnosticsOp = {
- op: WalletApiOperation.TestingGetDiagnostics;
+ op: WalletApiOperation.GetDiagnostics;
request: EmptyObject;
response: TestingGetDiagnosticsResponse;
};
+export type TestingGetFlightRecordsOp = {
+ op: WalletApiOperation.TestingGetFlightRecords;
+ request: EmptyObject;
+ response: TestingGetFlightRecordsResponse;
+};
+
/**
* Set a coin as (un-)suspended.
* Suspended coins won't be used for payments.
@@ -1803,8 +1813,9 @@ export type WalletOperations = {
[WalletApiOperation.SendTalerUriMailboxMessage]: SendTalerUriMailboxMessageOp;
[WalletApiOperation.ConvertIbanAccountFieldToPayto]: ConvertIbanAccountFieldToPaytoOp;
[WalletApiOperation.ConvertIbanPaytoToAccountField]: ConvertIbanPaytoToAccountFieldOp;
- [WalletApiOperation.TestingGetDiagnostics]: TestingGetDiagnosticsOp;
+ [WalletApiOperation.GetDiagnostics]: TestingGetDiagnosticsOp;
[WalletApiOperation.TestingGetPerformanceStats]: GetPerformanceStatsOp;
+ [WalletApiOperation.TestingGetFlightRecords]: TestingGetFlightRecordsOp;
};
export type WalletCoreRequestType<
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
@@ -72,6 +72,8 @@ import {
ExportDbToFileRequest,
ExportDbToFileResponse,
FailTransactionRequest,
+ FlightRecordEntry,
+ FlightRecordEvent,
ForgetBankAccountRequest,
GetActiveTasksResponse,
GetBankAccountByIdRequest,
@@ -134,11 +136,13 @@ import {
TalerErrorCode,
TalerExchangeHttpClient,
TalerMerchantInstanceHttpClient,
+ TalerPreciseTimestamp,
TalerProtocolTimestamp,
TalerUriAction,
TestingGetDenomStatsRequest,
TestingGetDenomStatsResponse,
TestingGetDiagnosticsResponse,
+ TestingGetFlightRecordsResponse,
TestingGetReserveHistoryRequest,
TestingSetTimetravelRequest,
TimerAPI,
@@ -2037,6 +2041,13 @@ export async function handleConvertIbanPaytoToAccountField(
};
}
+export async function handleGetFlightRecords(
+ wex: WalletExecutionContext,
+ _req: EmptyObject,
+): Promise<TestingGetFlightRecordsResponse> {
+ return { flightRecords: wex.ws.flightRecords };
+}
+
export async function handleGetDiagnostics(
wex: WalletExecutionContext,
req: EmptyObject,
@@ -2090,7 +2101,11 @@ interface HandlerWithValidator<Tag extends WalletApiOperation> {
}
const handlers: { [T in WalletApiOperation]: HandlerWithValidator<T> } = {
- [WalletApiOperation.TestingGetDiagnostics]: {
+ [WalletApiOperation.TestingGetFlightRecords]: {
+ codec: codecForEmptyObject(),
+ handler: handleGetFlightRecords,
+ },
+ [WalletApiOperation.GetDiagnostics]: {
codec: codecForEmptyObject(),
handler: handleGetDiagnostics,
},
@@ -3276,6 +3291,19 @@ export class InternalWalletState {
}
}
+ flightRecords: FlightRecordEntry[] = [];
+
+ addFdr(arg: { target: string; event: FlightRecordEvent }) {
+ this.flightRecords.push({
+ timestamp: TalerPreciseTimestamp.now(),
+ event: arg.event,
+ target: arg.target,
+ });
+ if (this.flightRecords.length > 100) {
+ this.flightRecords.shift();
+ }
+ }
+
createDbAccessHandle(
cancellationToken: CancellationToken,
): DbAccess<typeof WalletStoresV1> {