wallet-core: towards event-based waiting in runIntegrationTestV2

This commit is contained in:
Florian Dold 2023-06-30 23:01:48 +02:00
parent 86e9799ffd
commit 7a18e12a17
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
4 changed files with 124 additions and 24 deletions

View File

@ -184,6 +184,8 @@ export interface InternalWalletState {
merchantOps: MerchantOperations;
refreshOps: RefreshOperations;
isTaskLoopRunning: boolean;
getTransactionState(
ws: InternalWalletState,
tx: GetReadOnlyAccess<typeof WalletStoresV1>,
@ -218,4 +220,10 @@ export interface InternalWalletState {
runSequentialized<T>(tokens: string[], f: () => Promise<T>): Promise<T>;
runUntilDone(req?: { maxRetries?: number }): Promise<void>;
/**
* Ensure that a task loop is currently running.
* Starts one if no task loop is running.
*/
ensureTaskLoopRunning(): void;
}

View File

@ -746,10 +746,10 @@ export async function initiatePeerPullPayment(
undefined,
);
await ws.db
const transitionInfo = await ws.db
.mktx((x) => [x.peerPullPaymentInitiations, x.contractTerms])
.runReadWrite(async (tx) => {
await tx.peerPullPaymentInitiations.put({
const ppi: PeerPullPaymentInitiationRecord = {
amount: req.partialContractTerms.amount,
contractTermsHash: hContractTerms,
exchangeBaseUrl: exchangeBaseUrl,
@ -766,11 +766,17 @@ export async function initiatePeerPullPayment(
contractPub: contractKeyPair.pub,
withdrawalGroupId,
estimatedAmountEffective: wi.withdrawalAmountEffective,
});
}
await tx.peerPullPaymentInitiations.put(ppi);
const oldTxState: TransactionState = {
major: TransactionMajorState.None,
};
const newTxState = computePeerPullCreditTransactionState(ppi);
await tx.contractTerms.put({
contractTermsRaw: contractTerms,
h: hContractTerms,
});
return { oldTxState, newTxState };
});
const transactionId = constructTransactionIdentifier({
@ -781,6 +787,10 @@ export async function initiatePeerPullPayment(
// The pending-incoming balance has changed.
ws.notify({ type: NotificationType.BalanceChange });
notifyTransition(ws, transactionId, transitionInfo);
ws.workAvailable.trigger();
return {
talerUri: stringifyTalerUri({
type: TalerUriAction.PayPull,

View File

@ -27,6 +27,8 @@ import {
NotificationType,
stringToBytes,
TestPayResult,
TransactionMajorState,
TransactionMinorState,
WithdrawTestBalanceRequest,
} from "@gnu-taler/taler-util";
import {
@ -66,6 +68,7 @@ import {
} from "./pay-peer-push-credit.js";
import { initiatePeerPushDebit } from "./pay-peer-push-debit.js";
import { OpenedPromise, openPromise } from "../index.js";
import { getTransactionById } from "./transactions.js";
const logger = new Logger("operations/testing.ts");
@ -459,10 +462,45 @@ async function waitUntilDone(ws: InternalWalletState): Promise<void> {
});
}
async function waitUntilPendingReady(
ws: InternalWalletState,
transactionId: string,
): Promise<void> {
logger.info(`starting waiting for ${transactionId} to be in pending(ready)`);
ws.ensureTaskLoopRunning();
let p: OpenedPromise<void> | undefined = undefined;
ws.addNotificationListener((notif) => {
if (!p) {
return;
}
if (notif.type === NotificationType.TransactionStateTransition) {
p.resolve();
}
});
while (1) {
p = openPromise();
const tx = await getTransactionById(ws, {
transactionId,
});
if (
tx.txState.major == TransactionMajorState.Pending &&
tx.txState.minor === TransactionMinorState.Ready
) {
break;
}
// Wait until transaction state changed
await p.promise;
}
logger.info(`done waiting for ${transactionId} to be in pending(ready)`);
// FIXME: Remove listener!
}
export async function runIntegrationTest2(
ws: InternalWalletState,
args: IntegrationTestV2Args,
): Promise<void> {
// FIXME: Make sure that a task look is running, since we're
// waiting for notifications.
logger.info("running test with arguments", args);
const exchangeInfo = await updateExchangeFromUrl(ws, args.exchangeBaseUrl);
@ -565,6 +603,8 @@ export async function runIntegrationTest2(
},
});
await waitUntilPendingReady(ws, peerPushInit.transactionId);
const peerPushCredit = await preparePeerPushCredit(ws, {
talerUri: peerPushInit.talerUri,
});
@ -586,6 +626,8 @@ export async function runIntegrationTest2(
},
});
await waitUntilPendingReady(ws, peerPullInit.transactionId);
const peerPullInc = await preparePeerPullDebit(ws, {
talerUri: peerPullInit.talerUri,
});
@ -594,6 +636,8 @@ export async function runIntegrationTest2(
peerPullPaymentIncomingId: peerPullInc.peerPullPaymentIncomingId,
});
await ws.runUntilDone();
logger.trace("integration test: all done!");
}

View File

@ -287,9 +287,7 @@ import {
GetReadOnlyAccess,
GetReadWriteAccess,
} from "./util/query.js";
import {
TaskIdentifiers,
} from "./operations/common.js";
import { TaskIdentifiers } from "./operations/common.js";
import { TimerAPI, TimerGroup } from "./util/timer.js";
import {
WALLET_BANK_INTEGRATION_PROTOCOL_VERSION,
@ -404,6 +402,12 @@ async function runTaskLoop(
opts: RetryLoopOpts = {},
): Promise<TaskLoopResult> {
logger.info(`running task loop opts=${j2s(opts)}`);
if (ws.isTaskLoopRunning) {
logger.warn(
"task loop already running, nesting the wallet-core task loop is deprecated and should be avoided",
);
}
ws.isTaskLoopRunning = true;
let retriesExceeded = false;
for (let iteration = 0; !ws.stopped; iteration++) {
const pending = await getPendingOperations(ws);
@ -434,6 +438,14 @@ async function runTaskLoop(
if (opts.stopWhenDone && numGivingLiveness === 0 && iteration !== 0) {
logger.warn(`stopping, as no pending operations have lifeness`);
ws.isTaskLoopRunning = false;
return {
retriesExceeded,
};
}
if (ws.stopped) {
ws.isTaskLoopRunning = false;
return {
retriesExceeded,
};
@ -468,16 +480,24 @@ async function runTaskLoop(
}
await runTaskWithErrorReporting(ws, p.id, async () => {
logger.trace(`running pending ${JSON.stringify(p, undefined, 2)}`);
ws.isTaskLoopRunning = false;
return await callOperationHandler(ws, p);
});
ws.notify({
type: NotificationType.PendingOperationProcessed,
id: p.id,
});
if (ws.stopped) {
ws.isTaskLoopRunning = false;
return {
retriesExceeded,
};
}
}
}
logger.trace("exiting wallet retry loop");
}
logger.trace("exiting wallet task loop");
ws.isTaskLoopRunning = false;
return {
retriesExceeded,
};
@ -1575,7 +1595,9 @@ export async function handleCoreApiRequest(
};
} catch (e: any) {
const err = getErrorDetailFromException(e);
logger.info(`finished wallet core request ${operation} with error: ${j2s(err)}`);
logger.info(
`finished wallet core request ${operation} with error: ${j2s(err)}`,
);
return {
type: "error",
operation,
@ -1737,6 +1759,8 @@ class InternalWalletStateImpl implements InternalWalletState {
*/
private resourceLocks: Set<string> = new Set();
isTaskLoopRunning: boolean = false;
config: Readonly<WalletConfig>;
constructor(
@ -1948,6 +1972,20 @@ class InternalWalletStateImpl implements InternalWalletState {
}
}
}
ensureTaskLoopRunning(): void {
if (this.isTaskLoopRunning) {
return;
}
runTaskLoop(this)
.catch((e) => {
logger.error("error running task loop");
logger.error(`err: ${e}`);
})
.then(() => {
logger.info("done running task loop");
});
}
}
/**