implement the big LibEuFin integration test
This commit is contained in:
parent
94431fc6d2
commit
9aa9742d0e
@ -76,8 +76,8 @@ import {
|
|||||||
codecForPrepareTipResult,
|
codecForPrepareTipResult,
|
||||||
AcceptTipRequest,
|
AcceptTipRequest,
|
||||||
AbortPayWithRefundRequest,
|
AbortPayWithRefundRequest,
|
||||||
handleWorkerError,
|
|
||||||
openPromise,
|
openPromise,
|
||||||
|
parsePaytoUri,
|
||||||
} from "taler-wallet-core";
|
} from "taler-wallet-core";
|
||||||
import { URL } from "url";
|
import { URL } from "url";
|
||||||
import axios, { AxiosError } from "axios";
|
import axios, { AxiosError } from "axios";
|
||||||
@ -352,6 +352,10 @@ export class GlobalTestState {
|
|||||||
if (this.inShutdown) {
|
if (this.inShutdown) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (shouldLingerInTest()) {
|
||||||
|
console.log("refusing to shut down, lingering was requested");
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.inShutdown = true;
|
this.inShutdown = true;
|
||||||
console.log("shutting down");
|
console.log("shutting down");
|
||||||
for (const s of this.servers) {
|
for (const s of this.servers) {
|
||||||
@ -368,6 +372,10 @@ export class GlobalTestState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function shouldLingerInTest(): boolean {
|
||||||
|
return !!process.env["TALER_TEST_LINGER"];
|
||||||
|
}
|
||||||
|
|
||||||
export interface TalerConfigSection {
|
export interface TalerConfigSection {
|
||||||
options: Record<string, string | undefined>;
|
options: Record<string, string | undefined>;
|
||||||
}
|
}
|
||||||
@ -427,7 +435,11 @@ function setCoin(config: Configuration, c: CoinConfig) {
|
|||||||
config.setString(s, "rsa_keysize", `${c.rsaKeySize}`);
|
config.setString(s, "rsa_keysize", `${c.rsaKeySize}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function pingProc(
|
/**
|
||||||
|
* Send an HTTP request until it succeeds or the
|
||||||
|
* process dies.
|
||||||
|
*/
|
||||||
|
export async function pingProc(
|
||||||
proc: ProcessWrapper | undefined,
|
proc: ProcessWrapper | undefined,
|
||||||
url: string,
|
url: string,
|
||||||
serviceName: string,
|
serviceName: string,
|
||||||
@ -814,6 +826,15 @@ export class ExchangeService implements ExchangeServiceInterface {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async runTransferOnce() {
|
||||||
|
await runCommand(
|
||||||
|
this.globalState,
|
||||||
|
`exchange-${this.name}-transfer-once`,
|
||||||
|
"taler-exchange-transfer",
|
||||||
|
[...this.timetravelArgArr, "-c", this.configFilename, "-t"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
changeConfig(f: (config: Configuration) => void) {
|
changeConfig(f: (config: Configuration) => void) {
|
||||||
const config = Configuration.load(this.configFilename);
|
const config = Configuration.load(this.configFilename);
|
||||||
f(config);
|
f(config);
|
||||||
@ -1006,11 +1027,18 @@ export class ExchangeService implements ExchangeServiceInterface {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const accounts: string[] = [];
|
const accounts: string[] = [];
|
||||||
|
const accountTargetTypes: Set<string> = new Set();
|
||||||
|
|
||||||
const config = Configuration.load(this.configFilename);
|
const config = Configuration.load(this.configFilename);
|
||||||
for (const sectionName of config.getSectionNames()) {
|
for (const sectionName of config.getSectionNames()) {
|
||||||
if (sectionName.startsWith("exchange-account")) {
|
if (sectionName.startsWith("exchange-account")) {
|
||||||
accounts.push(config.getString(sectionName, "payto_uri").required());
|
const paytoUri = config.getString(sectionName, "payto_uri").required();
|
||||||
|
const p = parsePaytoUri(paytoUri);
|
||||||
|
if (!p) {
|
||||||
|
throw Error(`invalid payto uri in exchange config: ${paytoUri}`);
|
||||||
|
}
|
||||||
|
accountTargetTypes.add(p?.targetType);
|
||||||
|
accounts.push(paytoUri);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1032,22 +1060,24 @@ export class ExchangeService implements ExchangeServiceInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const year = new Date().getFullYear();
|
const year = new Date().getFullYear();
|
||||||
for (let i = year; i < year + 5; i++) {
|
for (const accTargetType of accountTargetTypes.values()) {
|
||||||
await runCommand(
|
for (let i = year; i < year + 5; i++) {
|
||||||
this.globalState,
|
await runCommand(
|
||||||
"exchange-offline",
|
this.globalState,
|
||||||
"taler-exchange-offline",
|
"exchange-offline",
|
||||||
[
|
"taler-exchange-offline",
|
||||||
"-c",
|
[
|
||||||
this.configFilename,
|
"-c",
|
||||||
"wire-fee",
|
this.configFilename,
|
||||||
`${i}`,
|
"wire-fee",
|
||||||
"x-taler-bank",
|
`${i}`,
|
||||||
`${this.exchangeConfig.currency}:0.01`,
|
accTargetType,
|
||||||
`${this.exchangeConfig.currency}:0.01`,
|
`${this.exchangeConfig.currency}:0.01`,
|
||||||
"upload",
|
`${this.exchangeConfig.currency}:0.01`,
|
||||||
],
|
"upload",
|
||||||
);
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1451,10 +1481,10 @@ export async function runTestWithState(
|
|||||||
let status: TestStatus;
|
let status: TestStatus;
|
||||||
|
|
||||||
const handleSignal = (s: string) => {
|
const handleSignal = (s: string) => {
|
||||||
gc.shutdownSync();
|
|
||||||
console.warn(
|
console.warn(
|
||||||
`**** received fatal proces event, terminating test ${testName}`,
|
`**** received fatal proces event, terminating test ${testName}`,
|
||||||
);
|
);
|
||||||
|
gc.shutdownSync();
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
442
packages/taler-wallet-cli/src/integrationtests/libeufin.ts
Normal file
442
packages/taler-wallet-cli/src/integrationtests/libeufin.ts
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
/*
|
||||||
|
This file is part of GNU Taler
|
||||||
|
(C) 2021 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 axios from "axios";
|
||||||
|
import { URL } from "taler-wallet-core";
|
||||||
|
import {
|
||||||
|
GlobalTestState,
|
||||||
|
pingProc,
|
||||||
|
ProcessWrapper,
|
||||||
|
runCommand,
|
||||||
|
} from "./harness";
|
||||||
|
|
||||||
|
export interface LibeufinSandboxServiceInterface {
|
||||||
|
baseUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LibeufinNexusServiceInterface {
|
||||||
|
baseUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LibeufinSandboxConfig {
|
||||||
|
httpPort: number;
|
||||||
|
databaseJdbcUri: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LibeufinNexusConfig {
|
||||||
|
httpPort: number;
|
||||||
|
databaseJdbcUri: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LibeufinSandboxService implements LibeufinSandboxServiceInterface {
|
||||||
|
static async create(
|
||||||
|
gc: GlobalTestState,
|
||||||
|
sandboxConfig: LibeufinSandboxConfig,
|
||||||
|
): Promise<LibeufinSandboxService> {
|
||||||
|
return new LibeufinSandboxService(gc, sandboxConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
sandboxProc: ProcessWrapper | undefined;
|
||||||
|
globalTestState: GlobalTestState;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
gc: GlobalTestState,
|
||||||
|
private sandboxConfig: LibeufinSandboxConfig,
|
||||||
|
) {
|
||||||
|
this.globalTestState = gc;
|
||||||
|
}
|
||||||
|
|
||||||
|
get baseUrl(): string {
|
||||||
|
return `http://localhost:${this.sandboxConfig.httpPort}/`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(): Promise<void> {
|
||||||
|
this.sandboxProc = this.globalTestState.spawnService(
|
||||||
|
"libeufin-sandbox",
|
||||||
|
[
|
||||||
|
"serve",
|
||||||
|
"--port",
|
||||||
|
`${this.sandboxConfig.httpPort}`,
|
||||||
|
"--db-conn-string",
|
||||||
|
this.sandboxConfig.databaseJdbcUri,
|
||||||
|
],
|
||||||
|
"libeufin-sandbox",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async pingUntilAvailable(): Promise<void> {
|
||||||
|
const url = `${this.baseUrl}config`;
|
||||||
|
await pingProc(this.sandboxProc, url, "libeufin-sandbox");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class LibeufinNexusService {
|
||||||
|
static async create(
|
||||||
|
gc: GlobalTestState,
|
||||||
|
nexusConfig: LibeufinNexusConfig,
|
||||||
|
): Promise<LibeufinNexusService> {
|
||||||
|
return new LibeufinNexusService(gc, nexusConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
nexusProc: ProcessWrapper | undefined;
|
||||||
|
globalTestState: GlobalTestState;
|
||||||
|
|
||||||
|
constructor(gc: GlobalTestState, private nexusConfig: LibeufinNexusConfig) {
|
||||||
|
this.globalTestState = gc;
|
||||||
|
}
|
||||||
|
|
||||||
|
get baseUrl(): string {
|
||||||
|
return `http://localhost:${this.nexusConfig.httpPort}/`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async start(): Promise<void> {
|
||||||
|
await runCommand(
|
||||||
|
this.globalTestState,
|
||||||
|
"libeufin-nexus-superuser",
|
||||||
|
"libeufin-nexus",
|
||||||
|
[
|
||||||
|
"superuser",
|
||||||
|
"admin",
|
||||||
|
"--password",
|
||||||
|
"test",
|
||||||
|
"--db-conn-string",
|
||||||
|
this.nexusConfig.databaseJdbcUri,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
this.nexusProc = this.globalTestState.spawnService(
|
||||||
|
"libeufin-nexus",
|
||||||
|
[
|
||||||
|
"serve",
|
||||||
|
"--port",
|
||||||
|
`${this.nexusConfig.httpPort}`,
|
||||||
|
"--db-conn-string",
|
||||||
|
this.nexusConfig.databaseJdbcUri,
|
||||||
|
],
|
||||||
|
"libeufin-nexus",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async pingUntilAvailable(): Promise<void> {
|
||||||
|
const url = `${this.baseUrl}config`;
|
||||||
|
await pingProc(this.nexusProc, url, "libeufin-nexus");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateEbicsSubscriberRequest {
|
||||||
|
hostID: string;
|
||||||
|
userID: string;
|
||||||
|
partnerID: string;
|
||||||
|
systemID?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CreateEbicsBankAccountRequest {
|
||||||
|
subscriber: {
|
||||||
|
hostID: string;
|
||||||
|
partnerID: string;
|
||||||
|
userID: string;
|
||||||
|
systemID?: string;
|
||||||
|
};
|
||||||
|
// IBAN
|
||||||
|
iban: string;
|
||||||
|
// BIC
|
||||||
|
bic: string;
|
||||||
|
// human name
|
||||||
|
name: string;
|
||||||
|
currency: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SimulateIncomingTransactionRequest {
|
||||||
|
debtorIban: string;
|
||||||
|
debtorBic: string;
|
||||||
|
debtorName: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subject / unstructured remittance info.
|
||||||
|
*/
|
||||||
|
subject: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decimal amount without currency.
|
||||||
|
*/
|
||||||
|
amount: string;
|
||||||
|
currency: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace LibeufinSandboxApi {
|
||||||
|
export async function createEbicsHost(
|
||||||
|
libeufinSandboxService: LibeufinSandboxServiceInterface,
|
||||||
|
hostID: string,
|
||||||
|
) {
|
||||||
|
const baseUrl = libeufinSandboxService.baseUrl;
|
||||||
|
let url = new URL("admin/ebics/hosts", baseUrl);
|
||||||
|
await axios.post(url.href, {
|
||||||
|
hostID,
|
||||||
|
ebicsVersion: "2.5",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createEbicsSubscriber(
|
||||||
|
libeufinSandboxService: LibeufinSandboxServiceInterface,
|
||||||
|
req: CreateEbicsSubscriberRequest,
|
||||||
|
) {
|
||||||
|
const baseUrl = libeufinSandboxService.baseUrl;
|
||||||
|
let url = new URL("admin/ebics/subscribers", baseUrl);
|
||||||
|
await axios.post(url.href, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createEbicsBankAccount(
|
||||||
|
libeufinSandboxService: LibeufinSandboxServiceInterface,
|
||||||
|
req: CreateEbicsBankAccountRequest,
|
||||||
|
) {
|
||||||
|
const baseUrl = libeufinSandboxService.baseUrl;
|
||||||
|
let url = new URL("admin/ebics/bank-accounts", baseUrl);
|
||||||
|
await axios.post(url.href, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function simulateIncomingTransaction(
|
||||||
|
libeufinSandboxService: LibeufinSandboxServiceInterface,
|
||||||
|
accountLabel: string,
|
||||||
|
req: SimulateIncomingTransactionRequest,
|
||||||
|
) {
|
||||||
|
const baseUrl = libeufinSandboxService.baseUrl;
|
||||||
|
let url = new URL(
|
||||||
|
`admin/bank-accounts/${accountLabel}/simulate-incoming-transaction`,
|
||||||
|
baseUrl,
|
||||||
|
);
|
||||||
|
await axios.post(url.href, req);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAccountTransactions(
|
||||||
|
libeufinSandboxService: LibeufinSandboxServiceInterface,
|
||||||
|
accountLabel: string,
|
||||||
|
): Promise<SandboxAccountTransactions> {
|
||||||
|
const baseUrl = libeufinSandboxService.baseUrl;
|
||||||
|
let url = new URL(
|
||||||
|
`admin/bank-accounts/${accountLabel}/transactions`,
|
||||||
|
baseUrl,
|
||||||
|
);
|
||||||
|
const res = await axios.get(url.href);
|
||||||
|
return res.data as SandboxAccountTransactions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SandboxAccountTransactions {
|
||||||
|
payments: {
|
||||||
|
accountLabel: string;
|
||||||
|
creditorIban: string;
|
||||||
|
creditorBic?: string;
|
||||||
|
creditorName: string;
|
||||||
|
debtorIban: string;
|
||||||
|
debtorBic: string;
|
||||||
|
debtorName: string;
|
||||||
|
amount: string;
|
||||||
|
currency: string;
|
||||||
|
subject: string;
|
||||||
|
date: string;
|
||||||
|
creditDebitIndicator: "debit" | "credit";
|
||||||
|
accountServicerReference: string;
|
||||||
|
}[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateEbicsBankConnectionRequest {
|
||||||
|
name: string;
|
||||||
|
ebicsURL: string;
|
||||||
|
hostID: string;
|
||||||
|
userID: string;
|
||||||
|
partnerID: string;
|
||||||
|
systemID?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CreateTalerWireGatewayFacadeRequest {
|
||||||
|
name: string;
|
||||||
|
connectionName: string;
|
||||||
|
accountName: string;
|
||||||
|
currency: string;
|
||||||
|
reserveTransferLevel: "report" | "statement" | "notification";
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace LibeufinNexusApi {
|
||||||
|
export async function createEbicsBankConnection(
|
||||||
|
libeufinNexusService: LibeufinNexusServiceInterface,
|
||||||
|
req: CreateEbicsBankConnectionRequest,
|
||||||
|
): Promise<void> {
|
||||||
|
const baseUrl = libeufinNexusService.baseUrl;
|
||||||
|
let url = new URL("bank-connections", baseUrl);
|
||||||
|
await axios.post(
|
||||||
|
url.href,
|
||||||
|
{
|
||||||
|
source: "new",
|
||||||
|
type: "ebics",
|
||||||
|
name: req.name,
|
||||||
|
data: {
|
||||||
|
ebicsURL: req.ebicsURL,
|
||||||
|
hostID: req.hostID,
|
||||||
|
userID: req.userID,
|
||||||
|
partnerID: req.partnerID,
|
||||||
|
systemID: req.systemID,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
username: "admin",
|
||||||
|
password: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAccounts(
|
||||||
|
libeufinNexusService: LibeufinNexusServiceInterface,
|
||||||
|
connectionName: string,
|
||||||
|
): Promise<void> {
|
||||||
|
const baseUrl = libeufinNexusService.baseUrl;
|
||||||
|
let url = new URL(
|
||||||
|
`bank-connections/${connectionName}/fetch-accounts`,
|
||||||
|
baseUrl,
|
||||||
|
);
|
||||||
|
await axios.post(
|
||||||
|
url.href,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
username: "admin",
|
||||||
|
password: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function importConnectionAccount(
|
||||||
|
libeufinNexusService: LibeufinNexusServiceInterface,
|
||||||
|
connectionName: string,
|
||||||
|
offeredAccountId: string,
|
||||||
|
nexusBankAccountId: string,
|
||||||
|
): Promise<void> {
|
||||||
|
const baseUrl = libeufinNexusService.baseUrl;
|
||||||
|
let url = new URL(
|
||||||
|
`bank-connections/${connectionName}/import-account`,
|
||||||
|
baseUrl,
|
||||||
|
);
|
||||||
|
await axios.post(
|
||||||
|
url.href,
|
||||||
|
{
|
||||||
|
offeredAccountId,
|
||||||
|
nexusBankAccountId,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
username: "admin",
|
||||||
|
password: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function connectBankConnection(
|
||||||
|
libeufinNexusService: LibeufinNexusServiceInterface,
|
||||||
|
connectionName: string,
|
||||||
|
) {
|
||||||
|
const baseUrl = libeufinNexusService.baseUrl;
|
||||||
|
let url = new URL(`bank-connections/${connectionName}/connect`, baseUrl);
|
||||||
|
await axios.post(
|
||||||
|
url.href,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
username: "admin",
|
||||||
|
password: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchAllTransactions(
|
||||||
|
libeufinNexusService: LibeufinNexusService,
|
||||||
|
accountName: string,
|
||||||
|
): Promise<void> {
|
||||||
|
const baseUrl = libeufinNexusService.baseUrl;
|
||||||
|
let url = new URL(
|
||||||
|
`/bank-accounts/${accountName}/fetch-transactions`,
|
||||||
|
baseUrl,
|
||||||
|
);
|
||||||
|
await axios.post(
|
||||||
|
url.href,
|
||||||
|
{
|
||||||
|
rangeType: "all",
|
||||||
|
level: "report",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
username: "admin",
|
||||||
|
password: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function createTwgFacade(
|
||||||
|
libeufinNexusService: LibeufinNexusServiceInterface,
|
||||||
|
req: CreateTalerWireGatewayFacadeRequest,
|
||||||
|
) {
|
||||||
|
const baseUrl = libeufinNexusService.baseUrl;
|
||||||
|
let url = new URL("facades", baseUrl);
|
||||||
|
await axios.post(
|
||||||
|
url.href,
|
||||||
|
{
|
||||||
|
name: req.name,
|
||||||
|
type: "taler-wire-gateway",
|
||||||
|
config: {
|
||||||
|
bankAccount: req.accountName,
|
||||||
|
bankConnection: req.connectionName,
|
||||||
|
currency: req.currency,
|
||||||
|
reserveTransferLevel: req.reserveTransferLevel,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
username: "admin",
|
||||||
|
password: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function submitAllPaymentInitiations(
|
||||||
|
libeufinNexusService: LibeufinNexusServiceInterface,
|
||||||
|
accountId: string,
|
||||||
|
) {
|
||||||
|
const baseUrl = libeufinNexusService.baseUrl;
|
||||||
|
let url = new URL(
|
||||||
|
`/bank-accounts/${accountId}/submit-all-payment-initiations`,
|
||||||
|
baseUrl,
|
||||||
|
);
|
||||||
|
await axios.post(
|
||||||
|
url.href,
|
||||||
|
{},
|
||||||
|
{
|
||||||
|
auth: {
|
||||||
|
username: "admin",
|
||||||
|
password: "test",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,293 @@
|
|||||||
|
/*
|
||||||
|
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 { CoreApiResponse } from "taler-wallet-core";
|
||||||
|
import { CoinConfig, defaultCoinConfig } from "./denomStructures";
|
||||||
|
import {
|
||||||
|
BankService,
|
||||||
|
DbInfo,
|
||||||
|
delayMs,
|
||||||
|
ExchangeBankAccount,
|
||||||
|
ExchangeService,
|
||||||
|
GlobalTestState,
|
||||||
|
MerchantService,
|
||||||
|
setupDb,
|
||||||
|
WalletCli,
|
||||||
|
} from "./harness";
|
||||||
|
import { makeTestPayment } from "./helpers";
|
||||||
|
import {
|
||||||
|
LibeufinNexusApi,
|
||||||
|
LibeufinNexusService,
|
||||||
|
LibeufinSandboxApi,
|
||||||
|
LibeufinSandboxService,
|
||||||
|
} from "./libeufin";
|
||||||
|
|
||||||
|
const exchangeIban = "DE71500105179674997361";
|
||||||
|
const customerIban = "DE84500105176881385584";
|
||||||
|
const customerBic = "BELADEBEXXX";
|
||||||
|
const merchantIban = "DE42500105171245624648";
|
||||||
|
|
||||||
|
export interface LibeufinTestEnvironment {
|
||||||
|
commonDb: DbInfo;
|
||||||
|
exchange: ExchangeService;
|
||||||
|
exchangeBankAccount: ExchangeBankAccount;
|
||||||
|
merchant: MerchantService;
|
||||||
|
wallet: WalletCli;
|
||||||
|
libeufinSandbox: LibeufinSandboxService;
|
||||||
|
libeufinNexus: LibeufinNexusService;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a Taler environment with LibEuFin and an EBICS account.
|
||||||
|
*/
|
||||||
|
export async function createLibeufinTestEnvironment(
|
||||||
|
t: GlobalTestState,
|
||||||
|
coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("EUR")),
|
||||||
|
): Promise<LibeufinTestEnvironment> {
|
||||||
|
const db = await setupDb(t);
|
||||||
|
|
||||||
|
const libeufinSandbox = await LibeufinSandboxService.create(t, {
|
||||||
|
httpPort: 5010,
|
||||||
|
databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`,
|
||||||
|
});
|
||||||
|
|
||||||
|
await libeufinSandbox.start();
|
||||||
|
await libeufinSandbox.pingUntilAvailable();
|
||||||
|
|
||||||
|
const libeufinNexus = await LibeufinNexusService.create(t, {
|
||||||
|
httpPort: 5011,
|
||||||
|
databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`,
|
||||||
|
});
|
||||||
|
|
||||||
|
await libeufinNexus.start();
|
||||||
|
await libeufinNexus.pingUntilAvailable();
|
||||||
|
|
||||||
|
await LibeufinSandboxApi.createEbicsHost(libeufinSandbox, "host01");
|
||||||
|
// Subscriber and bank Account for the exchange
|
||||||
|
await LibeufinSandboxApi.createEbicsSubscriber(libeufinSandbox, {
|
||||||
|
hostID: "host01",
|
||||||
|
partnerID: "partner01",
|
||||||
|
userID: "user01",
|
||||||
|
});
|
||||||
|
await LibeufinSandboxApi.createEbicsBankAccount(libeufinSandbox, {
|
||||||
|
bic: "DEUTDEBB101",
|
||||||
|
iban: exchangeIban,
|
||||||
|
label: "exchangeacct",
|
||||||
|
name: "Taler Exchange",
|
||||||
|
subscriber: {
|
||||||
|
hostID: "host01",
|
||||||
|
partnerID: "partner01",
|
||||||
|
userID: "user01",
|
||||||
|
},
|
||||||
|
currency: "EUR",
|
||||||
|
});
|
||||||
|
// Subscriber and bank Account for the merchant
|
||||||
|
// (Merchant doesn't need EBICS access, but sandbox right now only supports EBICS
|
||||||
|
// accounts.)
|
||||||
|
await LibeufinSandboxApi.createEbicsSubscriber(libeufinSandbox, {
|
||||||
|
hostID: "host01",
|
||||||
|
partnerID: "partner02",
|
||||||
|
userID: "user02",
|
||||||
|
});
|
||||||
|
await LibeufinSandboxApi.createEbicsBankAccount(libeufinSandbox, {
|
||||||
|
bic: "COBADEFXXX",
|
||||||
|
iban: merchantIban,
|
||||||
|
label: "merchantacct",
|
||||||
|
name: "Merchant",
|
||||||
|
subscriber: {
|
||||||
|
hostID: "host01",
|
||||||
|
partnerID: "partner02",
|
||||||
|
userID: "user02",
|
||||||
|
},
|
||||||
|
currency: "EUR",
|
||||||
|
});
|
||||||
|
|
||||||
|
await LibeufinNexusApi.createEbicsBankConnection(libeufinNexus, {
|
||||||
|
name: "myconn",
|
||||||
|
ebicsURL: "http://localhost:5010/ebicsweb",
|
||||||
|
hostID: "host01",
|
||||||
|
partnerID: "partner01",
|
||||||
|
userID: "user01",
|
||||||
|
});
|
||||||
|
await LibeufinNexusApi.connectBankConnection(libeufinNexus, "myconn");
|
||||||
|
await LibeufinNexusApi.fetchAccounts(libeufinNexus, "myconn");
|
||||||
|
await LibeufinNexusApi.importConnectionAccount(
|
||||||
|
libeufinNexus,
|
||||||
|
"myconn",
|
||||||
|
"exchangeacct",
|
||||||
|
"myacct",
|
||||||
|
);
|
||||||
|
|
||||||
|
await LibeufinNexusApi.createTwgFacade(libeufinNexus, {
|
||||||
|
name: "twg1",
|
||||||
|
accountName: "myacct",
|
||||||
|
connectionName: "myconn",
|
||||||
|
currency: "EUR",
|
||||||
|
reserveTransferLevel: "report",
|
||||||
|
});
|
||||||
|
|
||||||
|
const exchange = ExchangeService.create(t, {
|
||||||
|
name: "testexchange-1",
|
||||||
|
currency: "EUR",
|
||||||
|
httpPort: 8081,
|
||||||
|
database: db.connStr,
|
||||||
|
});
|
||||||
|
|
||||||
|
const merchant = await MerchantService.create(t, {
|
||||||
|
name: "testmerchant-1",
|
||||||
|
currency: "EUR",
|
||||||
|
httpPort: 8083,
|
||||||
|
database: db.connStr,
|
||||||
|
});
|
||||||
|
|
||||||
|
const exchangeBankAccount: ExchangeBankAccount = {
|
||||||
|
accountName: "twg-user",
|
||||||
|
accountPassword: "123",
|
||||||
|
accountPaytoUri: `payto://iban/${exchangeIban}?receiver-name=Exchange`,
|
||||||
|
wireGatewayApiBaseUrl:
|
||||||
|
"http://localhost:5011/facades/twg1/taler-wire-gateway/",
|
||||||
|
};
|
||||||
|
|
||||||
|
exchange.addBankAccount("1", exchangeBankAccount);
|
||||||
|
|
||||||
|
exchange.addCoinConfigList(coinConfig);
|
||||||
|
|
||||||
|
await exchange.start();
|
||||||
|
await exchange.pingUntilAvailable();
|
||||||
|
|
||||||
|
merchant.addExchange(exchange);
|
||||||
|
|
||||||
|
await merchant.start();
|
||||||
|
await merchant.pingUntilAvailable();
|
||||||
|
|
||||||
|
await merchant.addInstance({
|
||||||
|
id: "default",
|
||||||
|
name: "Default Instance",
|
||||||
|
paytoUris: [`payto://iban/${merchantIban}?receiver-name=Merchant`],
|
||||||
|
defaultWireTransferDelay: { d_ms: 0 },
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("setup done!");
|
||||||
|
|
||||||
|
const wallet = new WalletCli(t);
|
||||||
|
|
||||||
|
return {
|
||||||
|
commonDb: db,
|
||||||
|
exchange,
|
||||||
|
merchant,
|
||||||
|
wallet,
|
||||||
|
exchangeBankAccount,
|
||||||
|
libeufinNexus,
|
||||||
|
libeufinSandbox,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run basic test with LibEuFin.
|
||||||
|
*/
|
||||||
|
export async function runLibeufinBasicTest(t: GlobalTestState) {
|
||||||
|
// Set up test environment
|
||||||
|
|
||||||
|
const {
|
||||||
|
wallet,
|
||||||
|
exchange,
|
||||||
|
merchant,
|
||||||
|
libeufinSandbox,
|
||||||
|
libeufinNexus,
|
||||||
|
} = await createLibeufinTestEnvironment(t);
|
||||||
|
|
||||||
|
let wresp: CoreApiResponse;
|
||||||
|
|
||||||
|
// FIXME: add nicer api in the harness wallet for this.
|
||||||
|
wresp = await wallet.apiRequest("addExchange", {
|
||||||
|
exchangeBaseUrl: exchange.baseUrl,
|
||||||
|
});
|
||||||
|
|
||||||
|
t.assertTrue(wresp.type === "response");
|
||||||
|
|
||||||
|
// FIXME: add nicer api in the harness wallet for this.
|
||||||
|
wresp = await wallet.apiRequest("acceptManualWithdrawal", {
|
||||||
|
exchangeBaseUrl: exchange.baseUrl,
|
||||||
|
amount: "EUR:10",
|
||||||
|
});
|
||||||
|
|
||||||
|
t.assertTrue(wresp.type === "response");
|
||||||
|
|
||||||
|
const reservePub: string = (wresp.result as any).reservePub;
|
||||||
|
|
||||||
|
await LibeufinSandboxApi.simulateIncomingTransaction(
|
||||||
|
libeufinSandbox,
|
||||||
|
"exchangeacct",
|
||||||
|
{
|
||||||
|
amount: "15.00",
|
||||||
|
currency: "EUR",
|
||||||
|
debtorBic: customerBic,
|
||||||
|
debtorIban: customerIban,
|
||||||
|
debtorName: "Jane Customer",
|
||||||
|
subject: `Taler Top-up ${reservePub}`,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
await LibeufinNexusApi.fetchAllTransactions(libeufinNexus, "myacct");
|
||||||
|
|
||||||
|
await exchange.runWirewatchOnce();
|
||||||
|
|
||||||
|
await wallet.runUntilDone();
|
||||||
|
|
||||||
|
const bal = await wallet.getBalances();
|
||||||
|
console.log("balances", JSON.stringify(bal, undefined, 2));
|
||||||
|
t.assertAmountEquals(bal.balances[0].available, "EUR:14.7");
|
||||||
|
|
||||||
|
const order = {
|
||||||
|
summary: "Buy me!",
|
||||||
|
amount: "EUR:5",
|
||||||
|
fulfillment_url: "taler://fulfillment-success/thx",
|
||||||
|
};
|
||||||
|
|
||||||
|
await makeTestPayment(t, { wallet, merchant, order });
|
||||||
|
|
||||||
|
await exchange.runAggregatorOnce();
|
||||||
|
await exchange.runTransferOnce();
|
||||||
|
|
||||||
|
await LibeufinNexusApi.submitAllPaymentInitiations(libeufinNexus, "myacct");
|
||||||
|
|
||||||
|
const exchangeTransactions = await LibeufinSandboxApi.getAccountTransactions(
|
||||||
|
libeufinSandbox,
|
||||||
|
"exchangeacct",
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"exchange transactions:",
|
||||||
|
JSON.stringify(exchangeTransactions, undefined, 2),
|
||||||
|
);
|
||||||
|
|
||||||
|
t.assertDeepEqual(
|
||||||
|
exchangeTransactions.payments[0].creditDebitIndicator,
|
||||||
|
"credit",
|
||||||
|
);
|
||||||
|
t.assertDeepEqual(
|
||||||
|
exchangeTransactions.payments[1].creditDebitIndicator,
|
||||||
|
"debit",
|
||||||
|
);
|
||||||
|
t.assertDeepEqual(exchangeTransactions.payments[1].debtorIban, exchangeIban);
|
||||||
|
t.assertDeepEqual(
|
||||||
|
exchangeTransactions.payments[1].creditorIban,
|
||||||
|
merchantIban,
|
||||||
|
);
|
||||||
|
}
|
@ -14,7 +14,7 @@
|
|||||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { GlobalTestState, runTestWithState, TestRunResult } from "./harness";
|
import { GlobalTestState, runTestWithState, shouldLingerInTest, TestRunResult } from "./harness";
|
||||||
import { runPaymentTest } from "./test-payment";
|
import { runPaymentTest } from "./test-payment";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
@ -48,6 +48,7 @@ import { runWithdrawalAbortBankTest } from "./test-withdrawal-abort-bank";
|
|||||||
import { runWithdrawalBankIntegratedTest } from "./test-withdrawal-bank-integrated";
|
import { runWithdrawalBankIntegratedTest } from "./test-withdrawal-bank-integrated";
|
||||||
import M from "minimatch";
|
import M from "minimatch";
|
||||||
import { runMerchantExchangeConfusionTest } from "./test-merchant-exchange-confusion";
|
import { runMerchantExchangeConfusionTest } from "./test-merchant-exchange-confusion";
|
||||||
|
import { runLibeufinBasicTest } from "./test-libeufin-basic";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test runner.
|
* Test runner.
|
||||||
@ -65,6 +66,8 @@ const allTests: TestMainFunction[] = [
|
|||||||
runClaimLoopTest,
|
runClaimLoopTest,
|
||||||
runExchangeManagementTest,
|
runExchangeManagementTest,
|
||||||
runFeeRegressionTest,
|
runFeeRegressionTest,
|
||||||
|
runLibeufinBasicTest,
|
||||||
|
runMerchantExchangeConfusionTest,
|
||||||
runMerchantLongpollingTest,
|
runMerchantLongpollingTest,
|
||||||
runMerchantRefundApiTest,
|
runMerchantRefundApiTest,
|
||||||
runPayAbortTest,
|
runPayAbortTest,
|
||||||
@ -81,14 +84,13 @@ const allTests: TestMainFunction[] = [
|
|||||||
runRefundIncrementalTest,
|
runRefundIncrementalTest,
|
||||||
runRefundTest,
|
runRefundTest,
|
||||||
runRevocationTest,
|
runRevocationTest,
|
||||||
|
runTestWithdrawalManualTest,
|
||||||
runTimetravelAutorefreshTest,
|
runTimetravelAutorefreshTest,
|
||||||
runTimetravelWithdrawTest,
|
runTimetravelWithdrawTest,
|
||||||
runTippingTest,
|
runTippingTest,
|
||||||
runWallettestingTest,
|
runWallettestingTest,
|
||||||
runTestWithdrawalManualTest,
|
|
||||||
runWithdrawalAbortBankTest,
|
runWithdrawalAbortBankTest,
|
||||||
runWithdrawalBankIntegratedTest,
|
runWithdrawalBankIntegratedTest,
|
||||||
runMerchantExchangeConfusionTest,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export interface TestRunSpec {
|
export interface TestRunSpec {
|
||||||
@ -301,6 +303,10 @@ if (runTestInstrStr && process.argv.includes("__TWCLI_TESTWORKER")) {
|
|||||||
runTest()
|
runTest()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
console.log(`test ${testName} finished in worker`);
|
console.log(`test ${testName} finished in worker`);
|
||||||
|
if (shouldLingerInTest()) {
|
||||||
|
console.log("lingering ...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
})
|
})
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
|
Loading…
Reference in New Issue
Block a user