From 5056da6548d5880211abd3e1cdacd92134e40dab Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 1 Sep 2020 18:00:46 +0530 Subject: test error handling --- packages/taler-integrationtests/src/harness.ts | 90 +++++++++++++++++++--- packages/taler-integrationtests/src/helpers.ts | 22 +++++- .../src/test-exchange-management.ts | 4 +- .../src/test-payment-claim.ts | 2 +- .../taler-integrationtests/src/test-timetravel.ts | 42 ++++++++-- .../src/test-withdrawal-abort-bank.ts | 2 +- 6 files changed, 141 insertions(+), 21 deletions(-) (limited to 'packages/taler-integrationtests/src') diff --git a/packages/taler-integrationtests/src/harness.ts b/packages/taler-integrationtests/src/harness.ts index fd96c3165..93999c871 100644 --- a/packages/taler-integrationtests/src/harness.ts +++ b/packages/taler-integrationtests/src/harness.ts @@ -133,6 +133,62 @@ export async function sh( }); } +function shellescape(args: string[]) { + const ret = args.map((s) => { + if (/[^A-Za-z0-9_\/:=-]/.test(s)) { + s = "'" + s.replace(/'/g, "'\\''") + "'"; + s = s.replace(/^(?:'')+/g, "").replace(/\\'''/g, "\\'"); + } + return s; + }); + return ret.join(" "); +} + +/** + * Run a shell command, return stdout. + * + * Log stderr to a log file. + */ +export async function runCommand( + t: GlobalTestState, + logName: string, + command: string, + args: string[], +): Promise { + console.log("runing command", shellescape([command, ...args])); + return new Promise((resolve, reject) => { + const stdoutChunks: Buffer[] = []; + const proc = spawn(command, args, { + stdio: ["inherit", "pipe", "pipe"], + shell: false, + }); + proc.stdout.on("data", (x) => { + if (x instanceof Buffer) { + stdoutChunks.push(x); + } else { + throw Error("unexpected data chunk type"); + } + }); + const stderrLogFileName = path.join(t.testDir, `${logName}-stderr.log`); + const stderrLog = fs.createWriteStream(stderrLogFileName, { + flags: "a", + }); + proc.stderr.pipe(stderrLog); + proc.on("exit", (code, signal) => { + console.log(`child process exited (${code} / ${signal})`); + if (code != 0) { + reject(Error(`Unexpected exit code ${code} for '${command}'`)); + return; + } + const b = Buffer.concat(stdoutChunks).toString("utf-8"); + resolve(b); + }); + proc.on("error", () => { + reject(Error("Child process had error")); + }); + }); +} + export class ProcessWrapper { private waitPromise: Promise; constructor(public proc: ChildProcess) { @@ -298,7 +354,7 @@ export class GlobalTestState { } } - assertDeepEqual(actual: any, expected: any): asserts actual is any { + assertDeepEqual(actual: any, expected: T): asserts actual is T { deepStrictEqual(actual, expected); } @@ -349,7 +405,9 @@ export class GlobalTestState { args: string[], logName: string, ): ProcessWrapper { - console.log(`spawning process ${command} with arguments ${args})`); + console.log( + `spawning process (${logName}): ${shellescape([command, ...args])}`, + ); const proc = spawn(command, args, { stdio: ["inherit", "pipe", "pipe"], }); @@ -1028,8 +1086,8 @@ export class ExchangeService implements ExchangeServiceInterface { await sh( this.globalState, "exchange-wire", - `taler-exchange-wire ${this.timetravelArg} -c "${this.configFilename}"` - ) + `taler-exchange-wire ${this.timetravelArg} -c "${this.configFilename}"`, + ); this.exchangeWirewatchProc = this.globalState.spawnService( "taler-exchange-wirewatch", @@ -1403,6 +1461,14 @@ export class WalletCli { fs.unlinkSync(this.dbfile); } + private get timetravelArgArr(): string[] { + const tta = this.timetravelArg; + if (tta) { + return [tta]; + } + return []; + } + async apiRequest( request: string, payload: unknown, @@ -1420,13 +1486,19 @@ export class WalletCli { return JSON.parse(resp) as CoreApiResponse; } - async runUntilDone(): Promise { - await sh( + async runUntilDone(args: { maxRetries?: number } = {}): Promise { + await runCommand( this.globalTestState, `wallet-${this.name}`, - `taler-wallet-cli ${this.timetravelArg ?? ""} --no-throttle --wallet-db ${ - this.dbfile - } run-until-done`, + "taler-wallet-cli", + [ + "--no-throttle", + ...this.timetravelArgArr, + "--wallet-db", + this.dbfile, + "run-until-done", + ...(args.maxRetries ? ["--max-retries", `${args.maxRetries}`] : []), + ], ); } diff --git a/packages/taler-integrationtests/src/helpers.ts b/packages/taler-integrationtests/src/helpers.ts index 61b015190..515ae54bd 100644 --- a/packages/taler-integrationtests/src/helpers.ts +++ b/packages/taler-integrationtests/src/helpers.ts @@ -221,7 +221,7 @@ export async function createFaultInjectedMerchantTestkudosEnvironment( /** * Withdraw balance. */ -export async function withdrawViaBank( +export async function startWithdrawViaBank( t: GlobalTestState, p: { wallet: WalletCli; @@ -255,6 +255,26 @@ export async function withdrawViaBank( talerWithdrawUri: wop.taler_withdraw_uri, }); t.assertTrue(r2.type === "response"); +} + + +/** + * Withdraw balance. + */ +export async function withdrawViaBank( + t: GlobalTestState, + p: { + wallet: WalletCli; + bank: BankService; + exchange: ExchangeService; + amount: AmountString; + }, +): Promise { + + const { wallet } = p; + + await startWithdrawViaBank(t, p); + await wallet.runUntilDone(); // Check balance diff --git a/packages/taler-integrationtests/src/test-exchange-management.ts b/packages/taler-integrationtests/src/test-exchange-management.ts index 4ca86b341..7da260978 100644 --- a/packages/taler-integrationtests/src/test-exchange-management.ts +++ b/packages/taler-integrationtests/src/test-exchange-management.ts @@ -177,7 +177,7 @@ runTest(async (t: GlobalTestState) => { // Response is malformed, since it didn't even contain a version code // in a format the wallet can understand. t.assertTrue( - err1.operationError.talerErrorCode === + err1.operationError.code === TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE, ); @@ -214,7 +214,7 @@ runTest(async (t: GlobalTestState) => { }); t.assertTrue( - err2.operationError.talerErrorCode === + err2.operationError.code === TalerErrorCode.WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE, ); diff --git a/packages/taler-integrationtests/src/test-payment-claim.ts b/packages/taler-integrationtests/src/test-payment-claim.ts index c93057efd..3755394fd 100644 --- a/packages/taler-integrationtests/src/test-payment-claim.ts +++ b/packages/taler-integrationtests/src/test-payment-claim.ts @@ -102,7 +102,7 @@ runTest(async (t: GlobalTestState) => { }); t.assertTrue( - err.operationError.talerErrorCode === + err.operationError.code === TalerErrorCode.WALLET_ORDER_ALREADY_CLAIMED, ); diff --git a/packages/taler-integrationtests/src/test-timetravel.ts b/packages/taler-integrationtests/src/test-timetravel.ts index acc770d5d..086606b90 100644 --- a/packages/taler-integrationtests/src/test-timetravel.ts +++ b/packages/taler-integrationtests/src/test-timetravel.ts @@ -17,9 +17,18 @@ /** * Imports. */ -import { runTest, GlobalTestState, MerchantPrivateApi, WalletCli } from "./harness"; -import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers"; -import { PreparePayResultType, durationMin, Duration } from "taler-wallet-core"; +import { + runTest, + GlobalTestState, + MerchantPrivateApi, + WalletCli, +} from "./harness"; +import { + createSimpleTestkudosEnvironment, + withdrawViaBank, + startWithdrawViaBank, +} from "./helpers"; +import { PreparePayResultType, durationMin, Duration, TransactionType } from "taler-wallet-core"; /** * Basic time travel test. @@ -36,7 +45,7 @@ runTest(async (t: GlobalTestState) => { // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" }); // Travel 400 days into the future, // as the deposit expiration is two years @@ -56,9 +65,28 @@ runTest(async (t: GlobalTestState) => { await merchant.pingUntilAvailable(); // This should fail, as the wallet didn't time travel yet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + await startWithdrawViaBank(t, { + wallet, + bank, + exchange, + amount: "TESTKUDOS:20", + }); + + // Check that transactions are correct for the failed withdrawal + { + await wallet.runUntilDone({ maxRetries: 5 }); + const transactions = await wallet.getTransactions(); + console.log(transactions); + const types = transactions.transactions.map((x) => x.type); + t.assertDeepEqual(types, ["withdrawal", "withdrawal"]); + const wtrans = transactions.transactions[0]; + t.assertTrue(wtrans.type === TransactionType.Withdrawal); + t.assertTrue(wtrans.pending); + } + + // Now we also let the wallet time travel - const bal = await wallet.getBalances(); + wallet.setTimetravel(timetravelDuration); - console.log(bal); + await wallet.runUntilDone({ maxRetries: 5 }); }); diff --git a/packages/taler-integrationtests/src/test-withdrawal-abort-bank.ts b/packages/taler-integrationtests/src/test-withdrawal-abort-bank.ts index 3c1e62924..dd848b93d 100644 --- a/packages/taler-integrationtests/src/test-withdrawal-abort-bank.ts +++ b/packages/taler-integrationtests/src/test-withdrawal-abort-bank.ts @@ -59,7 +59,7 @@ runTest(async (t: GlobalTestState) => { }); t.assertTrue(r2.type === "error"); t.assertTrue( - r2.error.talerErrorCode === + r2.error.code === TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK, ); -- cgit v1.2.3