add bank API tests
This commit is contained in:
parent
4c296c9c5f
commit
786976e5a8
@ -66,9 +66,10 @@ import {
|
||||
TransactionsResponse,
|
||||
codecForTransactionsResponse,
|
||||
WithdrawTestBalanceRequest,
|
||||
AmountString,
|
||||
} from "taler-wallet-core";
|
||||
import { URL } from "url";
|
||||
import axios from "axios";
|
||||
import axios, { AxiosError } from "axios";
|
||||
import {
|
||||
codecForMerchantOrderPrivateStatusResponse,
|
||||
codecForPostOrderResponse,
|
||||
@ -276,6 +277,21 @@ export class GlobalTestState {
|
||||
);
|
||||
}
|
||||
|
||||
async assertThrowsAsync(block: () => Promise<void>): Promise<any> {
|
||||
try {
|
||||
await block();
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
throw Error(
|
||||
`expected exception to be thrown, but block finished without throwing`,
|
||||
);
|
||||
}
|
||||
|
||||
assertAxiosError(e: any): asserts e is AxiosError {
|
||||
return e.isAxiosError;
|
||||
}
|
||||
|
||||
assertTrue(b: boolean): asserts b {
|
||||
if (!b) {
|
||||
throw Error("test assertion failed");
|
||||
@ -412,6 +428,7 @@ export interface BankConfig {
|
||||
httpPort: number;
|
||||
database: string;
|
||||
allowRegistrations: boolean;
|
||||
maxDebt?: string;
|
||||
}
|
||||
|
||||
function setPaths(config: Configuration, home: string) {
|
||||
@ -474,7 +491,136 @@ export interface ExchangeBankAccount {
|
||||
wireGatewayApiBaseUrl: string;
|
||||
}
|
||||
|
||||
export class BankService {
|
||||
export interface BankServiceInterface {
|
||||
readonly baseUrl: string;
|
||||
readonly port: number;
|
||||
}
|
||||
|
||||
export enum CreditDebitIndicator {
|
||||
Credit = "credit",
|
||||
Debit = "debit",
|
||||
}
|
||||
|
||||
export interface BankAccountBalanceResponse {
|
||||
balance_amount: AmountString;
|
||||
credit_debit_indicator: CreditDebitIndicator;
|
||||
}
|
||||
|
||||
export namespace BankAccessApi {
|
||||
export async function getAccountBalance(
|
||||
bank: BankServiceInterface,
|
||||
bankUser: BankUser,
|
||||
): Promise<BankAccountBalanceResponse> {
|
||||
const url = new URL(
|
||||
`accounts/${bankUser.username}/balance`,
|
||||
bank.baseUrl,
|
||||
);
|
||||
const resp = await axios.get(
|
||||
url.href,
|
||||
{
|
||||
auth: bankUser,
|
||||
},
|
||||
);
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function createWithdrawalOperation(
|
||||
bank: BankServiceInterface,
|
||||
bankUser: BankUser,
|
||||
amount: string,
|
||||
): Promise<WithdrawalOperationInfo> {
|
||||
const url = new URL(
|
||||
`accounts/${bankUser.username}/withdrawals`,
|
||||
bank.baseUrl,
|
||||
);
|
||||
const resp = await axios.post(
|
||||
url.href,
|
||||
{
|
||||
amount,
|
||||
},
|
||||
{
|
||||
auth: bankUser,
|
||||
},
|
||||
);
|
||||
return codecForWithdrawalOperationInfo().decode(resp.data);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace BankApi {
|
||||
export async function registerAccount(
|
||||
bank: BankServiceInterface,
|
||||
username: string,
|
||||
password: string,
|
||||
): Promise<BankUser> {
|
||||
const url = new URL("testing/register", bank.baseUrl);
|
||||
await axios.post(url.href, {
|
||||
username,
|
||||
password,
|
||||
});
|
||||
return {
|
||||
password,
|
||||
username,
|
||||
accountPaytoUri: `payto://x-taler-bank/localhost/${username}`,
|
||||
};
|
||||
}
|
||||
|
||||
export async function createRandomBankUser(
|
||||
bank: BankServiceInterface,
|
||||
): Promise<BankUser> {
|
||||
const username = "user-" + encodeCrock(getRandomBytes(10));
|
||||
const password = "pw-" + encodeCrock(getRandomBytes(10));
|
||||
return await registerAccount(bank, username, password);
|
||||
}
|
||||
|
||||
export async function adminAddIncoming(
|
||||
bank: BankServiceInterface,
|
||||
params: {
|
||||
exchangeBankAccount: ExchangeBankAccount;
|
||||
amount: string;
|
||||
reservePub: string;
|
||||
debitAccountPayto: string;
|
||||
},
|
||||
) {
|
||||
const url = new URL(
|
||||
`taler-wire-gateway/${params.exchangeBankAccount.accountName}/admin/add-incoming`,
|
||||
bank.baseUrl,
|
||||
);
|
||||
await axios.post(
|
||||
url.href,
|
||||
{
|
||||
amount: params.amount,
|
||||
reserve_pub: params.reservePub,
|
||||
debit_account: params.debitAccountPayto,
|
||||
},
|
||||
{
|
||||
auth: {
|
||||
username: params.exchangeBankAccount.accountName,
|
||||
password: params.exchangeBankAccount.accountPassword,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export async function confirmWithdrawalOperation(
|
||||
bank: BankServiceInterface,
|
||||
bankUser: BankUser,
|
||||
wopi: WithdrawalOperationInfo,
|
||||
): Promise<void> {
|
||||
const url = new URL(
|
||||
`accounts/${bankUser.username}/withdrawals/${wopi.withdrawal_id}/confirm`,
|
||||
bank.baseUrl,
|
||||
);
|
||||
await axios.post(
|
||||
url.href,
|
||||
{},
|
||||
{
|
||||
auth: bankUser,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class BankService implements BankServiceInterface {
|
||||
proc: ProcessWrapper | undefined;
|
||||
|
||||
static fromExistingConfig(gc: GlobalTestState): BankService {
|
||||
@ -502,6 +648,7 @@ export class BankService {
|
||||
config.setString("bank", "database", bc.database);
|
||||
config.setString("bank", "http_port", `${bc.httpPort}`);
|
||||
config.setString("bank", "max_debt_bank", `${bc.currency}:999999`);
|
||||
config.setString("bank", "max_debt", bc.maxDebt ?? `${bc.currency}:100`);
|
||||
config.setString(
|
||||
"bank",
|
||||
"allow_registrations",
|
||||
@ -583,79 +730,6 @@ export class BankService {
|
||||
const url = `http://localhost:${this.bankConfig.httpPort}/config`;
|
||||
await pingProc(this.proc, url, "bank");
|
||||
}
|
||||
|
||||
async createAccount(username: string, password: string): Promise<void> {
|
||||
const url = `http://localhost:${this.bankConfig.httpPort}/testing/register`;
|
||||
await axios.post(url, {
|
||||
username,
|
||||
password,
|
||||
});
|
||||
}
|
||||
|
||||
async createRandomBankUser(): Promise<BankUser> {
|
||||
const username = "user-" + encodeCrock(getRandomBytes(10));
|
||||
const bankUser: BankUser = {
|
||||
username,
|
||||
password: "pw-" + encodeCrock(getRandomBytes(10)),
|
||||
accountPaytoUri: `payto://x-taler-bank/localhost/${username}`,
|
||||
};
|
||||
await this.createAccount(bankUser.username, bankUser.password);
|
||||
return bankUser;
|
||||
}
|
||||
|
||||
async createWithdrawalOperation(
|
||||
bankUser: BankUser,
|
||||
amount: string,
|
||||
): Promise<WithdrawalOperationInfo> {
|
||||
const url = `http://localhost:${this.bankConfig.httpPort}/accounts/${bankUser.username}/withdrawals`;
|
||||
const resp = await axios.post(
|
||||
url,
|
||||
{
|
||||
amount,
|
||||
},
|
||||
{
|
||||
auth: bankUser,
|
||||
},
|
||||
);
|
||||
return codecForWithdrawalOperationInfo().decode(resp.data);
|
||||
}
|
||||
|
||||
async adminAddIncoming(params: {
|
||||
exchangeBankAccount: ExchangeBankAccount;
|
||||
amount: string;
|
||||
reservePub: string;
|
||||
debitAccountPayto: string;
|
||||
}) {
|
||||
const url = `http://localhost:${this.bankConfig.httpPort}/taler-wire-gateway/${params.exchangeBankAccount.accountName}/admin/add-incoming`;
|
||||
await axios.post(
|
||||
url,
|
||||
{
|
||||
amount: params.amount,
|
||||
reserve_pub: params.reservePub,
|
||||
debit_account: params.debitAccountPayto,
|
||||
},
|
||||
{
|
||||
auth: {
|
||||
username: params.exchangeBankAccount.accountName,
|
||||
password: params.exchangeBankAccount.accountPassword,
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
async confirmWithdrawalOperation(
|
||||
bankUser: BankUser,
|
||||
wopi: WithdrawalOperationInfo,
|
||||
): Promise<void> {
|
||||
const url = `http://localhost:${this.bankConfig.httpPort}/accounts/${bankUser.username}/withdrawals/${wopi.withdrawal_id}/confirm`;
|
||||
await axios.post(
|
||||
url,
|
||||
{},
|
||||
{
|
||||
auth: bankUser,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface BankUser {
|
||||
@ -945,11 +1019,12 @@ export namespace MerchantPrivateApi {
|
||||
export async function giveRefund(
|
||||
merchantService: MerchantServiceInterface,
|
||||
r: {
|
||||
instance: string;
|
||||
orderId: string;
|
||||
amount: string;
|
||||
justification: string;
|
||||
}): Promise<{ talerRefundUri: string }> {
|
||||
instance: string;
|
||||
orderId: string;
|
||||
amount: string;
|
||||
justification: string;
|
||||
},
|
||||
): Promise<{ talerRefundUri: string }> {
|
||||
const reqUrl = new URL(
|
||||
`private/orders/${r.orderId}/refund`,
|
||||
merchantService.makeInstanceBaseUrl(r.instance),
|
||||
|
@ -34,6 +34,8 @@ import {
|
||||
defaultCoinConfig,
|
||||
ExchangeBankAccount,
|
||||
MerchantServiceInterface,
|
||||
BankApi,
|
||||
BankAccessApi,
|
||||
} from "./harness";
|
||||
import { AmountString } from "taler-wallet-core/lib/types/talerTypes";
|
||||
import { FaultInjectedMerchantService } from "./faultInjection";
|
||||
@ -230,8 +232,8 @@ export async function withdrawViaBank(
|
||||
): Promise<void> {
|
||||
const { wallet, bank, exchange, amount } = p;
|
||||
|
||||
const user = await bank.createRandomBankUser();
|
||||
const wop = await bank.createWithdrawalOperation(user, amount);
|
||||
const user = await BankApi.createRandomBankUser(bank);
|
||||
const wop = await BankAccessApi.createWithdrawalOperation(bank, user, amount);
|
||||
|
||||
// Hand it to the wallet
|
||||
|
||||
@ -244,7 +246,7 @@ export async function withdrawViaBank(
|
||||
|
||||
// Confirm it
|
||||
|
||||
await bank.confirmWithdrawalOperation(user, wop);
|
||||
await BankApi.confirmWithdrawalOperation(bank, user, wop);
|
||||
|
||||
// Withdraw
|
||||
|
||||
@ -260,3 +262,4 @@ export async function withdrawViaBank(
|
||||
const balApiResp = await wallet.apiRequest("getBalances", {});
|
||||
t.assertTrue(balApiResp.type === "response");
|
||||
}
|
||||
|
||||
|
122
packages/taler-integrationtests/src/test-bank-api.ts
Normal file
122
packages/taler-integrationtests/src/test-bank-api.ts
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
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, WalletCli, defaultCoinConfig, ExchangeService, setupDb, BankService, MerchantService, BankApi, BankUser, BankAccessApi, CreditDebitIndicator } from "./harness";
|
||||
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
|
||||
import { createEddsaKeyPair, encodeCrock } from "taler-wallet-core";
|
||||
|
||||
/**
|
||||
* Run test for basic, bank-integrated withdrawal.
|
||||
*/
|
||||
runTest(async (t: GlobalTestState) => {
|
||||
// Set up test environment
|
||||
|
||||
const db = await setupDb(t);
|
||||
|
||||
const bank = await BankService.create(t, {
|
||||
allowRegistrations: true,
|
||||
currency: "TESTKUDOS",
|
||||
database: db.connStr,
|
||||
httpPort: 8082,
|
||||
});
|
||||
|
||||
const exchange = ExchangeService.create(t, {
|
||||
name: "testexchange-1",
|
||||
currency: "TESTKUDOS",
|
||||
httpPort: 8081,
|
||||
database: db.connStr,
|
||||
});
|
||||
|
||||
const merchant = await MerchantService.create(t, {
|
||||
name: "testmerchant-1",
|
||||
currency: "TESTKUDOS",
|
||||
httpPort: 8083,
|
||||
database: db.connStr,
|
||||
});
|
||||
|
||||
const exchangeBankAccount = await bank.createExchangeAccount(
|
||||
"MyExchange",
|
||||
"x",
|
||||
);
|
||||
exchange.addBankAccount("1", exchangeBankAccount);
|
||||
|
||||
bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri);
|
||||
|
||||
await bank.start();
|
||||
|
||||
await bank.pingUntilAvailable();
|
||||
|
||||
exchange.addOfferedCoins(defaultCoinConfig);
|
||||
|
||||
await exchange.start();
|
||||
await exchange.pingUntilAvailable();
|
||||
|
||||
merchant.addExchange(exchange);
|
||||
|
||||
await merchant.start();
|
||||
await merchant.pingUntilAvailable();
|
||||
|
||||
await merchant.addInstance({
|
||||
id: "minst1",
|
||||
name: "minst1",
|
||||
paytoUris: ["payto://x-taler-bank/minst1"],
|
||||
});
|
||||
|
||||
await merchant.addInstance({
|
||||
id: "default",
|
||||
name: "Default Instance",
|
||||
paytoUris: [`payto://x-taler-bank/merchant-default`],
|
||||
});
|
||||
|
||||
console.log("setup done!");
|
||||
|
||||
const wallet = new WalletCli(t);
|
||||
|
||||
const bankUser = await BankApi.registerAccount(bank, "user1", "pw1");
|
||||
|
||||
// Make sure that registering twice results in a 409 Conflict
|
||||
{
|
||||
const e = await t.assertThrowsAsync(async () => {
|
||||
await BankApi.registerAccount(bank, "user1", "pw1");
|
||||
});
|
||||
t.assertAxiosError(e);
|
||||
t.assertTrue(e.response?.status === 409);
|
||||
}
|
||||
|
||||
let bal = await BankAccessApi.getAccountBalance(bank, bankUser);
|
||||
|
||||
console.log(bal);
|
||||
|
||||
// Check that we got the sign-up bonus.
|
||||
t.assertAmountEquals(bal.balance_amount, "TESTKUDOS:100");
|
||||
t.assertTrue(bal.credit_debit_indicator === CreditDebitIndicator.Credit);
|
||||
|
||||
const res = createEddsaKeyPair();
|
||||
|
||||
await BankApi.adminAddIncoming(bank, {
|
||||
amount: "TESTKUDOS:115",
|
||||
debitAccountPayto: bankUser.accountPaytoUri,
|
||||
exchangeBankAccount: exchangeBankAccount,
|
||||
reservePub: encodeCrock(res.eddsaPub),
|
||||
});
|
||||
|
||||
bal = await BankAccessApi.getAccountBalance(bank, bankUser);
|
||||
t.assertAmountEquals(bal.balance_amount, "TESTKUDOS:15");
|
||||
t.assertTrue(bal.credit_debit_indicator === CreditDebitIndicator.Debit);
|
||||
});
|
@ -26,6 +26,8 @@ import {
|
||||
ExchangeService,
|
||||
MerchantService,
|
||||
defaultCoinConfig,
|
||||
BankApi,
|
||||
BankAccessApi,
|
||||
} from "./harness";
|
||||
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
|
||||
import {
|
||||
@ -231,8 +233,8 @@ runTest(async (t: GlobalTestState) => {
|
||||
|
||||
// Create withdrawal operation
|
||||
|
||||
const user = await bank.createRandomBankUser();
|
||||
const wop = await bank.createWithdrawalOperation(user, "TESTKUDOS:10");
|
||||
const user = await BankApi.createRandomBankUser(bank);
|
||||
const wop = await BankAccessApi.createWithdrawalOperation(bank, user, "TESTKUDOS:10");
|
||||
|
||||
// Hand it to the wallet
|
||||
|
||||
|
@ -31,6 +31,8 @@ import {
|
||||
WalletCli,
|
||||
defaultCoinConfig,
|
||||
MerchantPrivateApi,
|
||||
BankApi,
|
||||
BankAccessApi,
|
||||
} from "./harness";
|
||||
import {
|
||||
FaultInjectedExchangeService,
|
||||
@ -114,8 +116,8 @@ runTest(async (t: GlobalTestState) => {
|
||||
|
||||
// Create withdrawal operation
|
||||
|
||||
const user = await bank.createRandomBankUser();
|
||||
const wop = await bank.createWithdrawalOperation(user, "TESTKUDOS:20");
|
||||
const user = await BankApi.createRandomBankUser(bank);
|
||||
const wop = await BankAccessApi.createWithdrawalOperation(bank, user, "TESTKUDOS:20");
|
||||
|
||||
// Hand it to the wallet
|
||||
|
||||
@ -128,7 +130,7 @@ runTest(async (t: GlobalTestState) => {
|
||||
|
||||
// Confirm it
|
||||
|
||||
await bank.confirmWithdrawalOperation(user, wop);
|
||||
await BankApi.confirmWithdrawalOperation(bank, user, wop);
|
||||
|
||||
// Withdraw
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { runTest, GlobalTestState } from "./harness";
|
||||
import { runTest, GlobalTestState, BankApi, BankAccessApi } from "./harness";
|
||||
import { createSimpleTestkudosEnvironment } from "./helpers";
|
||||
import { codecForBalancesResponse } from "taler-wallet-core";
|
||||
|
||||
@ -31,8 +31,8 @@ runTest(async (t: GlobalTestState) => {
|
||||
|
||||
// Create a withdrawal operation
|
||||
|
||||
const user = await bank.createRandomBankUser();
|
||||
const wop = await bank.createWithdrawalOperation(user, "TESTKUDOS:10");
|
||||
const user = await BankApi.createRandomBankUser(bank);
|
||||
const wop = await BankAccessApi.createWithdrawalOperation(bank, user, "TESTKUDOS:10");
|
||||
|
||||
// Hand it to the wallet
|
||||
|
||||
@ -45,7 +45,7 @@ runTest(async (t: GlobalTestState) => {
|
||||
|
||||
// Confirm it
|
||||
|
||||
await bank.confirmWithdrawalOperation(user, wop);
|
||||
await BankApi.confirmWithdrawalOperation(bank, user, wop);
|
||||
|
||||
// Withdraw
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
/**
|
||||
* Imports.
|
||||
*/
|
||||
import { runTest, GlobalTestState } from "./harness";
|
||||
import { runTest, GlobalTestState, BankApi } from "./harness";
|
||||
import { createSimpleTestkudosEnvironment } from "./helpers";
|
||||
import { CoreApiResponse } from "taler-wallet-core/lib/walletCoreApiHandler";
|
||||
import { codecForBalancesResponse } from "taler-wallet-core";
|
||||
@ -37,7 +37,7 @@ runTest(async (t: GlobalTestState) => {
|
||||
|
||||
// Create a withdrawal operation
|
||||
|
||||
const user = await bank.createRandomBankUser();
|
||||
const user = await BankApi.createRandomBankUser(bank);
|
||||
|
||||
let wresp: CoreApiResponse;
|
||||
|
||||
@ -56,7 +56,7 @@ runTest(async (t: GlobalTestState) => {
|
||||
|
||||
const reservePub: string = (wresp.result as any).reservePub;
|
||||
|
||||
await bank.adminAddIncoming({
|
||||
await BankApi.adminAddIncoming(bank, {
|
||||
exchangeBankAccount,
|
||||
amount: "TESTKUDOS:10",
|
||||
debitAccountPayto: user.accountPaytoUri,
|
||||
|
Loading…
Reference in New Issue
Block a user