From 044b7236572089b98a9f230499bb4cd9ad0342a3 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Tue, 1 Sep 2020 20:37:50 +0530 Subject: correct refund amounts and better testing --- packages/taler-integrationtests/src/harness.ts | 38 ++++++++++- .../taler-integrationtests/src/merchantApiTypes.ts | 4 +- .../src/test-refund-incremental.ts | 76 ++++++++++++++++++---- .../src/operations/transactions.ts | 17 +++-- packages/taler-wallet-core/src/util/amounts.ts | 14 +++- 5 files changed, 127 insertions(+), 22 deletions(-) diff --git a/packages/taler-integrationtests/src/harness.ts b/packages/taler-integrationtests/src/harness.ts index 93999c871..0b41923a4 100644 --- a/packages/taler-integrationtests/src/harness.ts +++ b/packages/taler-integrationtests/src/harness.ts @@ -42,7 +42,6 @@ import { CoreApiResponse, PreparePayResult, PreparePayRequest, - codecForPreparePayResultPaymentPossible, codecForPreparePayResult, OperationFailedError, AddExchangeRequest, @@ -67,6 +66,8 @@ import { codecForTransactionsResponse, WithdrawTestBalanceRequest, AmountString, + ApplyRefundRequest, + codecForApplyRefundResponse, } from "taler-wallet-core"; import { URL } from "url"; import axios, { AxiosError } from "axios"; @@ -77,6 +78,7 @@ import { PostOrderResponse, MerchantOrderPrivateStatusResponse, } from "./merchantApiTypes"; +import { ApplyRefundResponse } from "taler-wallet-core"; const exec = util.promisify(require("child_process").exec); @@ -384,6 +386,32 @@ export class GlobalTestState { } } + assertAmountLeq( + amtExpected: string | AmountJson, + amtActual: string | AmountJson, + ): void { + let ja1: AmountJson; + let ja2: AmountJson; + if (typeof amtExpected === "string") { + ja1 = Amounts.parseOrThrow(amtExpected); + } else { + ja1 = amtExpected; + } + if (typeof amtActual === "string") { + ja2 = Amounts.parseOrThrow(amtActual); + } else { + ja2 = amtActual; + } + + if (Amounts.cmp(ja1, ja2) > 0) { + throw Error( + `test assertion failed: expected ${Amounts.stringify( + ja1, + )} to be less or equal (leq) than ${Amounts.stringify(ja2)}`, + ); + } + } + private shutdownSync(): void { for (const s of this.servers) { s.close(); @@ -1512,6 +1540,14 @@ export class WalletCli { ); } + async applyRefund(req: ApplyRefundRequest): Promise { + const resp = await this.apiRequest("applyRefund", req); + if (resp.type === "response") { + return codecForApplyRefundResponse().decode(resp.result); + } + throw new OperationFailedError(resp.error); + } + async preparePay(req: PreparePayRequest): Promise { const resp = await this.apiRequest("preparePay", req); if (resp.type === "response") { diff --git a/packages/taler-integrationtests/src/merchantApiTypes.ts b/packages/taler-integrationtests/src/merchantApiTypes.ts index d08c354a1..550c5e90c 100644 --- a/packages/taler-integrationtests/src/merchantApiTypes.ts +++ b/packages/taler-integrationtests/src/merchantApiTypes.ts @@ -83,8 +83,8 @@ export const codecForCheckPaymentPaidResponse = (): Codec< > => buildCodecForObject() .property("order_status", codecForConstString("paid")) - .property("refunded", codecForBoolean) - .property("wired", codecForBoolean) + .property("refunded", codecForBoolean()) + .property("wired", codecForBoolean()) .property("deposit_total", codecForAmountString()) .property("exchange_ec", codecForNumber()) .property("exchange_hc", codecForNumber()) diff --git a/packages/taler-integrationtests/src/test-refund-incremental.ts b/packages/taler-integrationtests/src/test-refund-incremental.ts index e823b40a1..3439f7047 100644 --- a/packages/taler-integrationtests/src/test-refund-incremental.ts +++ b/packages/taler-integrationtests/src/test-refund-incremental.ts @@ -24,6 +24,7 @@ import { MerchantPrivateApi, } from "./harness"; import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers"; +import { TransactionType, Amounts } from "taler-wallet-core"; /** * Run test for basic, bank-integrated withdrawal. @@ -47,7 +48,7 @@ runTest(async (t: GlobalTestState) => { const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { order: { summary: "Buy me!", - amount: "TESTKUDOS:5", + amount: "TESTKUDOS:10", fulfillment_url: "taler://fulfillment-success/thx", }, }); @@ -88,9 +89,21 @@ runTest(async (t: GlobalTestState) => { console.log("first refund increase response", ref); + { + let wr = await wallet.applyRefund({ + talerRefundUri: ref.talerRefundUri, + }); + console.log(wr); + const txs = await wallet.getTransactions(); + console.log( + "transactions after applying first refund:", + JSON.stringify(txs, undefined, 2), + ); + } + // Wait at least a second, because otherwise the increased // refund will be grouped with the previous one. - await delayMs(1.2); + await delayMs(1200); ref = await MerchantPrivateApi.giveRefund(merchant, { amount: "TESTKUDOS:5", @@ -101,10 +114,25 @@ runTest(async (t: GlobalTestState) => { console.log("second refund increase response", ref); - let r = await wallet.apiRequest("applyRefund", { - talerRefundUri: ref.talerRefundUri, + // Wait at least a second, because otherwise the increased + // refund will be grouped with the previous one. + await delayMs(1200); + + ref = await MerchantPrivateApi.giveRefund(merchant, { + amount: "TESTKUDOS:10", + instance: "default", + justification: "bar", + orderId: orderResp.order_id, }); - console.log(r); + + console.log("third refund increase response", ref); + + { + let wr = await wallet.applyRefund({ + talerRefundUri: ref.talerRefundUri, + }); + console.log(wr); + } orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { orderId: orderResp.order_id, @@ -112,17 +140,43 @@ runTest(async (t: GlobalTestState) => { t.assertTrue(orderStatus.order_status === "paid"); - t.assertAmountEquals(orderStatus.refund_amount, "TESTKUDOS:5"); + t.assertAmountEquals(orderStatus.refund_amount, "TESTKUDOS:10"); console.log(JSON.stringify(orderStatus, undefined, 2)); await wallet.runUntilDone(); - r = await wallet.apiRequest("getBalances", {}); - console.log(JSON.stringify(r, undefined, 2)); - - r = await wallet.apiRequest("getTransactions", {}); - console.log(JSON.stringify(r, undefined, 2)); + const bal = await wallet.getBalances(); + console.log(JSON.stringify(bal, undefined, 2)); + + { + const txs = await wallet.getTransactions(); + console.log(JSON.stringify(txs, undefined, 2)); + + const txTypes = txs.transactions.map((x) => x.type); + t.assertDeepEqual(txTypes, [ + "withdrawal", + "payment", + "refund", + "refund", + "refund", + ]); + + for (const tx of txs.transactions) { + if (tx.type !== TransactionType.Refund) { + continue; + } + t.assertAmountLeq(tx.amountEffective, tx.amountRaw); + } + + const raw = Amounts.sum( + txs.transactions + .filter((x) => x.type === TransactionType.Refund) + .map((x) => x.amountRaw), + ).amount; + + t.assertAmountEquals(raw, "TESTKUDOS:10"); + } await t.shutdown(); }); diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index 2515415d2..da75f6e53 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -281,22 +281,27 @@ export async function getTransactions( groupKey, ); let r0: WalletRefundItem | undefined; - let amountEffective = Amounts.getZero( + let amountRaw = Amounts.getZero( pr.contractData.amount.currency, ); - let amountRaw = Amounts.getZero(pr.contractData.amount.currency); + let amountEffective = Amounts.getZero(pr.contractData.amount.currency); for (const rk of Object.keys(pr.refunds)) { const refund = pr.refunds[rk]; + const myGroupKey = `${refund.executionTime.t_ms}`; + if (myGroupKey !== groupKey) { + continue; + } if (!r0) { r0 = refund; } + if (refund.type === RefundState.Applied) { - amountEffective = Amounts.add( - amountEffective, - refund.refundAmount, - ).amount; amountRaw = Amounts.add( amountRaw, + refund.refundAmount, + ).amount; + amountEffective = Amounts.add( + amountEffective, Amounts.sub( refund.refundAmount, refund.refundFee, diff --git a/packages/taler-wallet-core/src/util/amounts.ts b/packages/taler-wallet-core/src/util/amounts.ts index 2a8c47905..2f912cff4 100644 --- a/packages/taler-wallet-core/src/util/amounts.ts +++ b/packages/taler-wallet-core/src/util/amounts.ts @@ -101,11 +101,21 @@ export function getZero(currency: string): AmountJson { }; } -export function sum(amounts: AmountJson[]): Result { +export type AmountLike = AmountString | AmountJson; + +export function jsonifyAmount(amt: AmountLike): AmountJson { + if (typeof amt === "string") { + return parseOrThrow(amt); + } + return amt; +} + +export function sum(amounts: AmountLike[]): Result { if (amounts.length <= 0) { throw Error("can't sum zero amounts"); } - return add(amounts[0], ...amounts.slice(1)); + const jsonAmounts = amounts.map((x) => jsonifyAmount(x)); + return add(jsonAmounts[0], ...jsonAmounts.slice(1)); } /** -- cgit v1.2.3