add auto-refund test case, fix bug detected by it
This commit is contained in:
parent
7ff93d8ef6
commit
57000c2214
99
packages/taler-integrationtests/src/test-refund-auto.ts
Normal file
99
packages/taler-integrationtests/src/test-refund-auto.ts
Normal file
@ -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();
|
||||
});
|
@ -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,
|
||||
};
|
||||
|
@ -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?
|
||||
|
@ -265,7 +265,7 @@ export async function getTransactions(
|
||||
|
||||
refundGroupKeys.forEach((groupKey: string) => {
|
||||
const refundTransactionId = makeEventId(
|
||||
TransactionType.Payment,
|
||||
TransactionType.Refund,
|
||||
pr.proposalId,
|
||||
groupKey,
|
||||
);
|
||||
|
@ -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>
|
||||
|
@ -35,6 +35,7 @@ import {
|
||||
OperationFailedError,
|
||||
GetWithdrawalDetailsForUriRequest,
|
||||
WithdrawUriInfoResponse,
|
||||
TransactionsResponse,
|
||||
} from "taler-wallet-core";
|
||||
|
||||
export interface ExtendedPermissionsResponse {
|
||||
@ -122,6 +123,13 @@ export function getBalance(): Promise<BalancesResponse> {
|
||||
return callBackend("getBalances", {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get balances for all currencies/exchanges.
|
||||
*/
|
||||
export function getTransactions(): Promise<TransactionsResponse> {
|
||||
return callBackend("getTransactions", {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return coins to a bank account.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user