diff options
author | Florian Dold <florian.dold@gmail.com> | 2020-08-20 11:59:06 +0530 |
---|---|---|
committer | Florian Dold <florian.dold@gmail.com> | 2020-08-20 11:59:06 +0530 |
commit | 57000c22141afde7eb7b13ccbd16f06d6b6eb5cd (patch) | |
tree | fc0de2a8a01bdc637325ec735e7a8f68e856532c | |
parent | 7ff93d8ef64d8ae832c2267192ce1f97bf914776 (diff) |
add auto-refund test case, fix bug detected by it
6 files changed, 155 insertions, 6 deletions
diff --git a/packages/taler-integrationtests/src/test-refund-auto.ts b/packages/taler-integrationtests/src/test-refund-auto.ts new file mode 100644 index 000000000..ed80c1c3f --- /dev/null +++ b/packages/taler-integrationtests/src/test-refund-auto.ts @@ -0,0 +1,99 @@ +/* + This file is part of GNU Taler + (C) 2020 Taler Systems S.A. + + GNU Taler is free software; you can redistribute it and/or modify it under the + terms of the GNU General Public License as published by the Free Software + Foundation; either version 3, or (at your option) any later version. + + GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> + */ + +/** + * Imports. + */ +import { runTest, GlobalTestState, MerchantPrivateApi } from "./harness"; +import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers"; +import { CoreApiResponse } from "taler-wallet-core"; + +/** + * Run test for basic, bank-integrated withdrawal. + */ +runTest(async (t: GlobalTestState) => { + // Set up test environment + + const { + wallet, + bank, + exchange, + merchant, + } = await createSimpleTestkudosEnvironment(t); + + // Withdraw digital cash into the wallet. + + await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + + // Set up order. + const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", { + order: { + summary: "Buy me!", + amount: "TESTKUDOS:5", + fulfillment_url: "taler://fulfillment-success/thx", + auto_refund: { + d_ms: 3000, + } + }, + }); + + let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { + orderId: orderResp.order_id, + }); + + t.assertTrue(orderStatus.order_status === "unpaid"); + + // Make wallet pay for the order + + const r1 = await wallet.apiRequest("preparePay", { + talerPayUri: orderStatus.taler_pay_uri, + }); + t.assertTrue(r1.type === "response"); + + const r2 = await wallet.apiRequest("confirmPay", { + // FIXME: should be validated, don't cast! + proposalId: (r1.result as any).proposalId, + }); + t.assertTrue(r2.type === "response"); + + // Check if payment was successful. + + orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, { + orderId: orderResp.order_id, + }); + + t.assertTrue(orderStatus.order_status === "paid"); + + const ref = await MerchantPrivateApi.giveRefund(merchant, { + amount: "TESTKUDOS:5", + instance: "default", + justification: "foo", + orderId: orderResp.order_id, + }); + + console.log(ref); + + // The wallet should now automatically pick up the refund. + await wallet.runUntilDone(); + + const transactions = await wallet.getTransactions() + console.log(JSON.stringify(transactions, undefined, 2)); + + const transactionTypes = transactions.transactions.map((x) => x.type); + t.assertDeepEqual(transactionTypes, ["withdrawal", "payment", "refund"]); + + await t.shutdown(); +}); diff --git a/packages/taler-wallet-core/src/operations/refresh.ts b/packages/taler-wallet-core/src/operations/refresh.ts index 409ae58cc..430675328 100644 --- a/packages/taler-wallet-core/src/operations/refresh.ts +++ b/packages/taler-wallet-core/src/operations/refresh.ts @@ -571,10 +571,19 @@ export async function createRefreshGroup( retryInfo: initRetryInfo(), }; + if (oldCoinPubs.length == 0) { + logger.warn("created refresh group with zero coins"); + refreshGroup.timestampFinished = getTimestampNow(); + } + await tx.put(Stores.refreshGroups, refreshGroup); logger.trace(`created refresh group ${refreshGroupId}`); + processRefreshGroup(ws, refreshGroupId).catch((e) => { + logger.warn(`processing refresh group ${refreshGroupId} failed`); + }); + return { refreshGroupId, }; diff --git a/packages/taler-wallet-core/src/operations/refund.ts b/packages/taler-wallet-core/src/operations/refund.ts index e9324712e..0d6b9ec86 100644 --- a/packages/taler-wallet-core/src/operations/refund.ts +++ b/packages/taler-wallet-core/src/operations/refund.ts @@ -259,7 +259,9 @@ async function acceptRefunds( } const refreshCoinsPubs = Object.values(refreshCoinsMap); - await createRefreshGroup(ws, tx, refreshCoinsPubs, RefreshReason.Refund); + if (refreshCoinsPubs.length > 0) { + await createRefreshGroup(ws, tx, refreshCoinsPubs, RefreshReason.Refund); + } // Are we done with querying yet, or do we need to do another round // after a retry delay? diff --git a/packages/taler-wallet-core/src/operations/transactions.ts b/packages/taler-wallet-core/src/operations/transactions.ts index e17dfac3a..8d0558dbd 100644 --- a/packages/taler-wallet-core/src/operations/transactions.ts +++ b/packages/taler-wallet-core/src/operations/transactions.ts @@ -265,7 +265,7 @@ export async function getTransactions( refundGroupKeys.forEach((groupKey: string) => { const refundTransactionId = makeEventId( - TransactionType.Payment, + TransactionType.Refund, pr.proposalId, groupKey, ); diff --git a/packages/taler-wallet-webextension/src/pages/popup.tsx b/packages/taler-wallet-webextension/src/pages/popup.tsx index e833f5950..d7cdd548e 100644 --- a/packages/taler-wallet-webextension/src/pages/popup.tsx +++ b/packages/taler-wallet-webextension/src/pages/popup.tsx @@ -33,9 +33,11 @@ import { Balance, classifyTalerUri, TalerUriType, + TransactionsResponse, + Transaction, + TransactionType, } from "taler-wallet-core"; - import { abbrev, renderAmount, PageLink } from "../renderHtml"; import * as wxApi from "../wxApi"; @@ -309,9 +311,37 @@ function formatAndCapitalize(text: string): string { return text; } -const HistoryComponent = (props: any): JSX.Element => { - return <span>TBD</span>; -}; +function TransactionItem(props: { tx: Transaction }): JSX.Element { + const tx = props.tx; + return <pre>{JSON.stringify(tx)}</pre> +} + +function WalletHistory(props: any): JSX.Element { + const [transactions, setTransactions] = useState< + TransactionsResponse | undefined + >(); + + useEffect(() => { + const fetchData = async (): Promise<void> => { + const res = await wxApi.getTransactions(); + setTransactions(res); + }; + fetchData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + if (!transactions) { + return <div>Loading ...</div>; + } + + return ( + <div> + {transactions.transactions.map((tx) => ( + <TransactionItem tx={tx} /> + ))} + </div> + ); +} class WalletSettings extends React.Component<any, any> { render(): JSX.Element { @@ -492,6 +522,7 @@ function WalletPopup(): JSX.Element { <WalletBalanceView route="/balance" default /> <WalletSettings route="/settings" /> <WalletDebug route="/debug" /> + <WalletHistory route="/history" /> </Router> </div> </div> diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index dc78686ad..455d0b8ae 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -35,6 +35,7 @@ import { OperationFailedError, GetWithdrawalDetailsForUriRequest, WithdrawUriInfoResponse, + TransactionsResponse, } from "taler-wallet-core"; export interface ExtendedPermissionsResponse { @@ -123,6 +124,13 @@ export function getBalance(): Promise<BalancesResponse> { } /** + * Get balances for all currencies/exchanges. + */ +export function getTransactions(): Promise<TransactionsResponse> { + return callBackend("getTransactions", {}); +} + +/** * Return coins to a bank account. */ export function returnCoins(args: { |