make integration tests part of taler-wallet-cli

This commit is contained in:
Florian Dold 2021-01-12 20:04:16 +01:00
parent 6772c54793
commit a5681579fb
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
43 changed files with 341 additions and 488 deletions

View File

@ -1,25 +0,0 @@
{
"name": "taler-integrationtests",
"version": "0.0.1",
"description": "Integration tests and fault injection for GNU Taler components",
"main": "index.js",
"scripts": {
"compile": "tsc -b",
"pretty": "prettier --write src"
},
"author": "Florian Dold <dold@taler.net>",
"license": "AGPL-3.0-or-later",
"devDependencies": {
"esm": "^3.2.25",
"nyc": "^15.1.0",
"prettier": "^2.1.2",
"source-map-support": "^0.5.19",
"ts-node": "^9.0.0",
"typescript": "^4.0.5"
},
"dependencies": {
"axios": "^0.21.0",
"taler-wallet-core": "workspace:*",
"tslib": "^2.0.3"
}
}

View File

@ -1,26 +0,0 @@
#!/usr/bin/env bash
# Simple test runner for the wallet integration tests.
#
# Usage: $0 TESTGLOB
#
# The TESTGLOB can be used to select which test cases to execute
set -eu
if [ "$#" -ne 1 ]; then
echo "Usage: $0 SCENARIO"
exit 1
fi
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
cd $DIR
./node_modules/.bin/tsc -b
export ESM_OPTIONS='{"sourceMap": true}'
file=lib/scenario-$1.js
exec node -r source-map-support/register -r esm $file

View File

@ -1,128 +0,0 @@
/*
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 {
GlobalTestState,
BankService,
ExchangeService,
MerchantService,
WalletCli,
runTestWithState,
MerchantPrivateApi,
} from "./harness";
import { withdrawViaBank } from "./helpers";
import fs from "fs";
let existingTestDir =
process.env["TALER_TEST_OLD_DIR"] ?? "/tmp/taler-integrationtest-current";
if (!fs.existsSync(existingTestDir)) {
throw Error("old test dir not found");
}
existingTestDir = fs.realpathSync(existingTestDir);
const prevT = new GlobalTestState({
testDir: existingTestDir,
});
async function withdrawAndPay(
t: GlobalTestState,
wallet: WalletCli,
bank: BankService,
exchange: ExchangeService,
merchant: MerchantService,
): Promise<void> {
await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:100" });
// Set up order.
const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
order: {
summary: "Buy me!",
amount: "TESTKUDOS:80",
fulfillment_url: "taler://fulfillment-success/thx",
},
});
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");
}
/**
* Run test.
*/
runTestWithState(prevT, async (t: GlobalTestState) => {
// Set up test environment
const bank = BankService.fromExistingConfig(t);
const exchange = ExchangeService.fromExistingConfig(t, "testexchange-1");
const merchant = MerchantService.fromExistingConfig(t, "testmerchant-1");
await bank.start();
await exchange.start();
await merchant.start();
await Promise.all([
bank.pingUntilAvailable(),
merchant.pingUntilAvailable(),
exchange.pingUntilAvailable(),
]);
const wallet = new WalletCli(t);
// Withdraw digital cash into the wallet.
const repetitions = Number.parseInt(process.env["TALER_TEST_REPEAT"] ?? "1");
for (let rep = 0; rep < repetitions; rep++) {
console.log("repetition", rep);
try {
wallet.deleteDatabase();
await withdrawAndPay(t, wallet, bank, exchange, merchant);
} catch (e) {
console.log("ignoring exception", e);
}
}
await t.shutdown();
});

View File

@ -1,77 +0,0 @@
#!/usr/bin/env bash
# Simple test runner for the wallet integration tests.
#
# Usage: $0 TESTGLOB
#
# The TESTGLOB can be used to select which test cases to execute
set -eu
exit_int() {
echo "Interrupted..."
exit 2
}
trap "exit_int" INT
if [ "$#" -ne 1 ]; then
echo "Usage: $0 TESTGLOB"
exit 1
fi
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
cd $DIR
./node_modules/.bin/tsc -b
export ESM_OPTIONS='{"sourceMap": true}'
shopt -s extglob
num_exec=0
num_fail=0
num_succ=0
files_failed=''
# Glob tests
for file in lib/$1?(.js); do
case "$file" in
*/test-*.js)
echo "executing test $file"
ret=0
node -r source-map-support/register -r esm $file || ret=$?
num_exec=$((num_exec+1))
case $ret in
0)
num_succ=$((num_succ+1))
;;
*)
num_fail=$((num_fail+1))
files_failed=$files_failed:$file
;;
esac
;;
*)
continue
;;
esac
done
echo "-----------------------------------"
echo "Tests finished"
echo "$num_succ/$num_exec tests succeeded"
if [[ $num_fail != 0 ]]; then
echo "These tests failed:"
echo $files_failed | tr : \\n | sed '/^$/d'
fi
echo "-----------------------------------"
if [[ $num_fail = 0 ]]; then
exit 0
else
exit 1
fi

View File

@ -1,32 +0,0 @@
{
"compileOnSave": true,
"compilerOptions": {
"composite": true,
"declaration": true,
"declarationMap": false,
"target": "ES6",
"module": "ESNext",
"moduleResolution": "node",
"sourceMap": true,
"lib": ["es6"],
"types": ["node"],
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"strict": true,
"strictPropertyInitialization": false,
"outDir": "lib",
"noImplicitAny": true,
"noImplicitThis": true,
"incremental": true,
"esModuleInterop": true,
"importHelpers": true,
"rootDir": "./src",
"typeRoots": ["./node_modules/@types"]
},
"references": [
{
"path": "../taler-wallet-core"
}
],
"include": ["src/**/*"]
}

View File

@ -43,7 +43,9 @@
"typescript": "^4.0.5"
},
"dependencies": {
"@types/minimatch": "^3.0.3",
"axios": "^0.21.0",
"minimatch": "^3.0.4",
"source-map-support": "^0.5.19",
"taler-wallet-core": "workspace:*",
"tslib": "^2.0.3"

View File

@ -41,6 +41,7 @@ import {
} from "taler-wallet-core";
import * as clk from "./clk";
import { deepStrictEqual } from "assert";
import { getTestInfo, runTests } from "./integrationtests/testrunner";
// This module also serves as the entry point for the crypto
// thread worker, and thus must expose these two handlers.
@ -749,6 +750,25 @@ const testCli = walletCli.subcommand("testingArgs", "testing", {
help: "Subcommands for testing GNU Taler deployments.",
});
testCli
.subcommand("listIntegrationtests", "list-integrationtests")
.action(async (args) => {
for (const t of getTestInfo()) {
console.log(t.name);
}
});
testCli
.subcommand("runIntegrationtests", "run-integrationtests")
.maybeArgument("pattern", clk.STRING, {
help: "Glob pattern to select which tests to run",
})
.action(async (args) => {
await runTests({
include_pattern: args.runIntegrationtests.pattern,
});
});
testCli.subcommand("vectors", "vectors").action(async (args) => {
printTestVectors();
});

View File

@ -77,6 +77,7 @@ import {
codecForPrepareTipResult,
AcceptTipRequest,
AbortPayWithRefundRequest,
handleWorkerError,
} from "taler-wallet-core";
import { URL } from "url";
import axios, { AxiosError } from "axios";
@ -93,6 +94,7 @@ import {
import { ApplyRefundResponse } from "taler-wallet-core";
import { PendingOperationsResponse } from "taler-wallet-core";
import { CoinConfig } from "./denomStructures";
import { after } from "taler-wallet-core/src/util/timer";
const exec = util.promisify(require("child_process").exec);
@ -236,11 +238,6 @@ export class GlobalTestState {
this.testDir = params.testDir;
this.procs = [];
this.servers = [];
process.on("SIGINT", () => this.shutdownSync());
process.on("SIGTERM", () => this.shutdownSync());
process.on("unhandledRejection", () => this.shutdownSync());
process.on("uncaughtException", () => this.shutdownSync());
}
async assertThrowsOperationErrorAsync(
@ -307,7 +304,7 @@ export class GlobalTestState {
}
}
private shutdownSync(): void {
shutdownSync(): void {
for (const s of this.servers) {
s.close();
s.removeAllListeners();
@ -315,12 +312,8 @@ export class GlobalTestState {
for (const p of this.procs) {
if (p.proc.exitCode == null) {
p.proc.kill("SIGTERM");
} else {
}
}
console.log("*** test harness interrupted");
console.log("*** test state can be found under", this.testDir);
process.exit(1);
}
spawnService(
@ -362,11 +355,6 @@ export class GlobalTestState {
}
this.inShutdown = true;
console.log("shutting down");
if (shouldLingerAlways()) {
console.log("*** test finished, but requested to linger");
console.log("*** test state can be found under", this.testDir);
return;
}
for (const s of this.servers) {
s.close();
s.removeAllListeners();
@ -1438,76 +1426,57 @@ export interface MerchantInstanceConfig {
defaultPayDelay?: Duration;
}
type TestStatus = "pass" | "fail" | "skip";
export interface TestRunResult {
/**
* Check if the test should hang around after it failed.
* Name of the test.
*/
function shouldLinger(): boolean {
return (
process.env["TALER_TEST_LINGER"] == "1" ||
process.env["TALER_TEST_LINGER_ALWAYS"] == "1"
);
}
name: string;
/**
* Check if the test should hang around even after it finished
* successfully.
* How long did the test run?
*/
function shouldLingerAlways(): boolean {
return process.env["TALER_TEST_LINGER_ALWAYS"] == "1";
timeSec: number;
status: TestStatus;
}
function updateCurrentSymlink(testDir: string): void {
const currLink = path.join(os.tmpdir(), "taler-integrationtest-current");
try {
fs.unlinkSync(currLink);
} catch (e) {
// Ignore
}
try {
fs.symlinkSync(testDir, currLink);
} catch (e) {
console.log(e);
// Ignore
}
}
export function runTestWithState(
export async function runTestWithState(
gc: GlobalTestState,
testMain: (t: GlobalTestState) => Promise<void>,
) {
const main = async () => {
let ret = 0;
try {
updateCurrentSymlink(gc.testDir);
console.log("running test in directory", gc.testDir);
await testMain(gc);
} catch (e) {
console.error("FATAL: test failed with exception", e);
ret = 1;
} finally {
if (gc) {
if (shouldLinger()) {
console.log("test logs and config can be found under", gc.testDir);
console.log("keeping test environment running");
} else {
await gc.shutdown();
console.log("test logs and config can be found under", gc.testDir);
process.exit(ret);
}
}
}
testName: string,
): Promise<TestRunResult> {
const startMs = new Date().getTime();
const handleSignal = () => {
gc.shutdownSync();
console.warn("**** received fatal signal, shutting down test harness");
process.exit(1);
};
main();
}
process.on("SIGINT", handleSignal);
process.on("SIGTERM", handleSignal);
process.on("unhandledRejection", handleSignal);
process.on("uncaughtException", handleSignal);
export function runTest(
testMain: (gc: GlobalTestState) => Promise<void>,
): void {
const gc = new GlobalTestState({
testDir: fs.mkdtempSync(path.join(os.tmpdir(), "taler-integrationtest-")),
});
runTestWithState(gc, testMain);
let status: TestStatus;
try {
console.log("running test in directory", gc.testDir);
await testMain(gc);
status = "pass";
} catch (e) {
console.error("FATAL: test failed with exception", e);
status = "fail";
} finally {
await gc.shutdown();
}
const afterMs = new Date().getTime();
return {
name: testName,
timeSec: (afterMs - startMs) / 1000,
status,
};
}
function shellWrap(s: string) {

View File

@ -17,13 +17,13 @@
/**
* Imports.
*/
import { runTest, GlobalTestState, MerchantPrivateApi } from "./harness";
import { GlobalTestState, MerchantPrivateApi } from "./harness";
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runPromptPaymentScenario(t: GlobalTestState) {
// Set up test environment
const {
@ -57,4 +57,4 @@ runTest(async (t: GlobalTestState) => {
// Wait "forever"
await new Promise(() => {});
});
}

View File

@ -18,7 +18,6 @@
* Imports.
*/
import {
runTest,
GlobalTestState,
WalletCli,
ExchangeService,
@ -35,7 +34,7 @@ import { defaultCoinConfig } from "./denomStructures";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runBankApiTest(t: GlobalTestState) {
// Set up test environment
const db = await setupDb(t);
@ -134,4 +133,4 @@ runTest(async (t: GlobalTestState) => {
t.assertTrue(
balResp.balance.credit_debit_indicator === CreditDebitIndicator.Debit,
);
});
}

View File

@ -18,10 +18,8 @@
* Imports.
*/
import {
runTest,
GlobalTestState,
MerchantPrivateApi,
WalletCli,
} from "./harness";
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
import { URL } from "url";
@ -32,7 +30,7 @@ import { URL } from "url";
* FIXME: Is this test still necessary? We initially wrote if to confirm/document
* assumptions about how the merchant should work.
*/
runTest(async (t: GlobalTestState) => {
export async function runClaimLoopTest(t: GlobalTestState) {
// Set up test environment
const {
@ -80,4 +78,4 @@ runTest(async (t: GlobalTestState) => {
t.assertTrue(orderStatusAfter.order_status === "claimed");
await t.shutdown();
});
}

View File

@ -18,7 +18,6 @@
* Imports.
*/
import {
runTest,
GlobalTestState,
WalletCli,
setupDb,
@ -43,7 +42,7 @@ import { defaultCoinConfig } from "./denomStructures";
/**
* Test if the wallet handles outdated exchange versions correct.y
*/
runTest(async (t: GlobalTestState) => {
export async function runExchangeManagementTest(t: GlobalTestState) {
// Set up test environment
const db = await setupDb(t);
@ -247,4 +246,4 @@ runTest(async (t: GlobalTestState) => {
// Make sure the faulty exchange isn't used for the suggestion.
t.assertTrue(wd.possibleExchanges.length === 0);
});
}

View File

@ -17,9 +17,7 @@
/**
* Imports.
*/
import { defaultCoinConfig } from "./denomStructures";
import {
runTest,
GlobalTestState,
BankService,
ExchangeService,
@ -166,7 +164,7 @@ export async function createMyTestkudosEnvironment(
/**
* Run test for basic, bank-integrated withdrawal and payment.
*/
runTest(async (t: GlobalTestState) => {
export async function runFeeRegressionTest(t: GlobalTestState) {
// Set up test environment
const {
@ -203,4 +201,4 @@ runTest(async (t: GlobalTestState) => {
const txs = await wallet.getTransactions();
t.assertAmountEquals(txs.transactions[1].amountEffective, "TESTKUDOS:1.30");
console.log(txs);
});
}

View File

@ -17,7 +17,7 @@
/**
* Imports.
*/
import { runTest, GlobalTestState, MerchantPrivateApi } from "./harness";
import { GlobalTestState, MerchantPrivateApi } from "./harness";
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
import {
PreparePayResultType,
@ -30,7 +30,7 @@ import axios from "axios";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runMerchantLongpollingTest(t: GlobalTestState) {
// Set up test environment
const {
@ -129,4 +129,4 @@ runTest(async (t: GlobalTestState) => {
});
t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
});
}

View File

@ -18,10 +18,8 @@
* Imports.
*/
import {
runTest,
GlobalTestState,
MerchantPrivateApi,
MerchantService,
BankServiceInterface,
MerchantServiceInterface,
WalletCli,
@ -256,7 +254,7 @@ async function testRefundApiWithFulfillmentMessage(
/**
* Test case for the refund API of the merchant backend.
*/
runTest(async (t: GlobalTestState) => {
export async function runMerchantRefundApiTest(t: GlobalTestState) {
// Set up test environment
const {
@ -283,4 +281,4 @@ runTest(async (t: GlobalTestState) => {
exchange,
merchant,
});
});
}

View File

@ -23,7 +23,6 @@
* Imports.
*/
import {
runTest,
GlobalTestState,
MerchantService,
ExchangeService,
@ -31,8 +30,6 @@ import {
BankService,
WalletCli,
MerchantPrivateApi,
BankApi,
BankAccessApi,
} from "./harness";
import {
FaultInjectedExchangeService,
@ -46,7 +43,7 @@ import { withdrawViaBank, makeTestPayment } from "./helpers";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runPayAbortTest(t: GlobalTestState) {
// Set up test environment
const db = await setupDb(t);
@ -199,4 +196,4 @@ runTest(async (t: GlobalTestState) => {
const txTypes = txr.transactions.map((x) => x.type);
t.assertDeepEqual(txTypes, ["withdrawal", "payment", "refund"]);
});
}

View File

@ -17,7 +17,7 @@
/**
* Imports.
*/
import { runTest, GlobalTestState, MerchantPrivateApi } from "./harness";
import { GlobalTestState, MerchantPrivateApi } from "./harness";
import {
withdrawViaBank,
createFaultInjectedMerchantTestkudosEnvironment,
@ -39,7 +39,7 @@ import { FaultInjectionRequestContext } from "./faultInjection";
* since we can't initiate payment via a "claimed" private order status
* response.
*/
runTest(async (t: GlobalTestState) => {
export async function runPayPaidTest(t: GlobalTestState) {
// Set up test environment
const {
@ -203,4 +203,4 @@ runTest(async (t: GlobalTestState) => {
// Make sure the wallet is actually doing the replay properly.
t.assertTrue(numPaidRequested == 1);
t.assertTrue(numPayRequested == 0);
});
}

View File

@ -17,19 +17,14 @@
/**
* Imports.
*/
import {
runTest,
GlobalTestState,
MerchantPrivateApi,
WalletCli,
} from "./harness";
import { GlobalTestState, MerchantPrivateApi, WalletCli } from "./harness";
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
import { PreparePayResultType, TalerErrorCode } from "taler-wallet-core";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runPaymentClaimTest(t: GlobalTestState) {
// Set up test environment
const {
@ -106,4 +101,4 @@ runTest(async (t: GlobalTestState) => {
);
await t.shutdown();
});
}

View File

@ -22,7 +22,6 @@
* Imports.
*/
import {
runTest,
GlobalTestState,
MerchantService,
ExchangeService,
@ -44,7 +43,7 @@ import { defaultCoinConfig } from "./denomStructures";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runPaymentFaultTest(t: GlobalTestState) {
// Set up test environment
const db = await setupDb(t);
@ -207,4 +206,4 @@ runTest(async (t: GlobalTestState) => {
});
t.assertTrue(orderStatus.order_status === "paid");
});
}

View File

@ -17,7 +17,7 @@
/**
* Imports.
*/
import { runTest, GlobalTestState, MerchantPrivateApi } from "./harness";
import { GlobalTestState, MerchantPrivateApi } from "./harness";
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
import { PreparePayResultType } from "taler-wallet-core";
@ -25,7 +25,7 @@ import { PreparePayResultType } from "taler-wallet-core";
* Test the wallet-core payment API, especially that repeated operations
* return the expected result.
*/
runTest(async (t: GlobalTestState) => {
export async function runPaymentIdempotencyTest(t: GlobalTestState) {
// Set up test environment
const {
@ -100,4 +100,4 @@ runTest(async (t: GlobalTestState) => {
t.assertTrue(preparePayResultAfter.paid === true);
await t.shutdown();
});
}

View File

@ -18,7 +18,6 @@
* Imports.
*/
import {
runTest,
GlobalTestState,
setupDb,
BankService,
@ -109,7 +108,7 @@ async function setupTest(
*
* This test uses a very sub-optimal denomination structure.
*/
runTest(async (t: GlobalTestState) => {
export async function runPaymentMultipleTest(t: GlobalTestState) {
// Set up test environment
const { merchant, bank, exchange } = await setupTest(t);
@ -158,4 +157,4 @@ runTest(async (t: GlobalTestState) => {
t.assertTrue(orderStatus.order_status === "paid");
await t.shutdown();
});
}

View File

@ -17,7 +17,7 @@
/**
* Imports.
*/
import { runTest, GlobalTestState, MerchantPrivateApi } from "./harness";
import { GlobalTestState, MerchantPrivateApi } from "./harness";
import {
withdrawViaBank,
createFaultInjectedMerchantTestkudosEnvironment,
@ -41,7 +41,7 @@ import {
* Run test for a payment where the merchant has a transient
* failure in /pay
*/
runTest(async (t: GlobalTestState) => {
export async function runPaymentTransientTest(t: GlobalTestState) {
// Set up test environment
const {
@ -169,4 +169,4 @@ runTest(async (t: GlobalTestState) => {
`expected status 202 (after paying), but got ${publicOrderStatusResp.status}`,
);
}
});
}

View File

@ -17,7 +17,7 @@
/**
* Imports.
*/
import { runTest, GlobalTestState } from "./harness";
import { GlobalTestState } from "./harness";
import {
createSimpleTestkudosEnvironment,
withdrawViaBank,
@ -27,7 +27,7 @@ import {
/**
* Run test for basic, bank-integrated withdrawal and payment.
*/
runTest(async (t: GlobalTestState) => {
export async function runPaymentTest(t: GlobalTestState) {
// Set up test environment
const {
@ -50,4 +50,4 @@ runTest(async (t: GlobalTestState) => {
await makeTestPayment(t, { wallet, merchant, order });
await wallet.runUntilDone();
});
}

View File

@ -17,7 +17,7 @@
/**
* Imports.
*/
import { runTest, GlobalTestState, MerchantPrivateApi } from "./harness";
import { GlobalTestState, MerchantPrivateApi } from "./harness";
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
import {
PreparePayResultType,
@ -29,7 +29,7 @@ import axios from "axios";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runPaywallFlowTest(t: GlobalTestState) {
// Set up test environment
const {
@ -230,4 +230,4 @@ runTest(async (t: GlobalTestState) => {
console.log(publicOrderStatusResp.data);
t.assertTrue(pubUnpaidStatus.already_paid_order_id === firstOrderId);
});
}

View File

@ -17,14 +17,14 @@
/**
* Imports.
*/
import { runTest, GlobalTestState, MerchantPrivateApi } from "./harness";
import { GlobalTestState, MerchantPrivateApi } from "./harness";
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
import { CoreApiResponse, durationFromSpec } from "taler-wallet-core";
import { durationFromSpec } from "taler-wallet-core";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runRefundAutoTest(t: GlobalTestState) {
// Set up test environment
const {
@ -97,4 +97,4 @@ runTest(async (t: GlobalTestState) => {
t.assertDeepEqual(transactionTypes, ["withdrawal", "payment", "refund"]);
await t.shutdown();
});
}

View File

@ -17,7 +17,7 @@
/**
* Imports.
*/
import { runTest, GlobalTestState, MerchantPrivateApi } from "./harness";
import { GlobalTestState, MerchantPrivateApi } from "./harness";
import {
createSimpleTestkudosEnvironment,
withdrawViaBank,
@ -33,7 +33,7 @@ import {
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runRefundGoneTest(t: GlobalTestState) {
// Set up test environment
const {
@ -124,4 +124,4 @@ runTest(async (t: GlobalTestState) => {
console.log(JSON.stringify(r, undefined, 2));
await t.shutdown();
});
}

View File

@ -17,19 +17,14 @@
/**
* Imports.
*/
import {
runTest,
GlobalTestState,
delayMs,
MerchantPrivateApi,
} from "./harness";
import { GlobalTestState, delayMs, MerchantPrivateApi } from "./harness";
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
import { TransactionType, Amounts, durationFromSpec } from "taler-wallet-core";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runRefundIncrementalTest(t: GlobalTestState) {
// Set up test environment
const {
@ -188,4 +183,4 @@ runTest(async (t: GlobalTestState) => {
}
await t.shutdown();
});
}

View File

@ -18,13 +18,13 @@
* Imports.
*/
import { durationFromSpec } from "taler-wallet-core";
import { runTest, GlobalTestState, MerchantPrivateApi } from "./harness";
import { GlobalTestState, MerchantPrivateApi } from "./harness";
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runRefundTest(t: GlobalTestState) {
// Set up test environment
const {
@ -100,4 +100,4 @@ runTest(async (t: GlobalTestState) => {
console.log(JSON.stringify(r, undefined, 2));
await t.shutdown();
});
}

View File

@ -18,9 +18,7 @@
* Imports.
*/
import {
runTest,
GlobalTestState,
MerchantPrivateApi,
ExchangeService,
MerchantService,
WalletCli,
@ -63,7 +61,7 @@ async function revokeAllWalletCoins(req: {
/**
* Basic time travel test.
*/
runTest(async (t: GlobalTestState) => {
export async function runRevocationTest(t: GlobalTestState) {
// Set up test environment
const {
@ -119,4 +117,4 @@ runTest(async (t: GlobalTestState) => {
}
await makeTestPayment(t, { wallet, merchant, order });
});
}

View File

@ -31,7 +31,6 @@ import {
GlobalTestState,
MerchantPrivateApi,
MerchantService,
runTest,
setupDb,
WalletCli,
} from "./harness";
@ -67,7 +66,7 @@ async function applyTimeTravel(
/**
* Basic time travel test.
*/
runTest(async (t: GlobalTestState) => {
export async function runTimetravelAutorefreshTest(t: GlobalTestState) {
// Set up test environment
const db = await setupDb(t);
@ -201,4 +200,4 @@ runTest(async (t: GlobalTestState) => {
});
t.assertTrue(cpr.type === ConfirmPayResultType.Done);
});
}

View File

@ -17,7 +17,7 @@
/**
* Imports.
*/
import { runTest, GlobalTestState } from "./harness";
import { GlobalTestState } from "./harness";
import {
createSimpleTestkudosEnvironment,
withdrawViaBank,
@ -28,7 +28,7 @@ import { Duration, TransactionType } from "taler-wallet-core";
/**
* Basic time travel test.
*/
runTest(async (t: GlobalTestState) => {
export async function runTimetravelWithdrawTest(t: GlobalTestState){
// Set up test environment
const {
@ -87,4 +87,4 @@ runTest(async (t: GlobalTestState) => {
// This doesn't work yet, see https://bugs.taler.net/n/6585
// await wallet.runUntilDone({ maxRetries: 5 });
});
}

View File

@ -18,7 +18,6 @@
* Imports.
*/
import {
runTest,
GlobalTestState,
MerchantPrivateApi,
BankApi,
@ -28,7 +27,7 @@ import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runTippingTest(t: GlobalTestState) {
// Set up test environment
const {
@ -125,4 +124,4 @@ runTest(async (t: GlobalTestState) => {
// Check twice so make sure tip handling is idempotent
await doTip();
await doTip();
});
}

View File

@ -22,13 +22,13 @@
/**
* Imports.
*/
import { runTest, GlobalTestState } from "./harness";
import { GlobalTestState } from "./harness";
import { createSimpleTestkudosEnvironment, withdrawViaBank } from "./helpers";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runWallettestingTest(t: GlobalTestState) {
const {
wallet,
bank,
@ -84,4 +84,4 @@ runTest(async (t: GlobalTestState) => {
t.assertDeepEqual(txTypes, ["withdrawal", "payment"]);
await t.shutdown();
});
}

View File

@ -17,14 +17,14 @@
/**
* Imports.
*/
import { runTest, GlobalTestState, BankApi, BankAccessApi } from "./harness";
import { GlobalTestState, BankApi, BankAccessApi } from "./harness";
import { createSimpleTestkudosEnvironment } from "./helpers";
import { codecForBalancesResponse, TalerErrorCode } from "taler-wallet-core";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runWithdrawalAbortBankTest(t: GlobalTestState) {
// Set up test environment
const { wallet, bank, exchange } = await createSimpleTestkudosEnvironment(t);
@ -64,4 +64,4 @@ runTest(async (t: GlobalTestState) => {
);
await t.shutdown();
});
}

View File

@ -17,14 +17,14 @@
/**
* Imports.
*/
import { runTest, GlobalTestState, BankApi, BankAccessApi } from "./harness";
import { GlobalTestState, BankApi, BankAccessApi } from "./harness";
import { createSimpleTestkudosEnvironment } from "./helpers";
import { codecForBalancesResponse } from "taler-wallet-core";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
// Set up test environment
const { wallet, bank, exchange } = await createSimpleTestkudosEnvironment(t);
@ -68,4 +68,4 @@ runTest(async (t: GlobalTestState) => {
t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available);
await t.shutdown();
});
}

View File

@ -17,7 +17,7 @@
/**
* Imports.
*/
import { runTest, GlobalTestState, BankApi } from "./harness";
import { GlobalTestState, BankApi } from "./harness";
import { createSimpleTestkudosEnvironment } from "./helpers";
import { CoreApiResponse } from "taler-wallet-core";
import { codecForBalancesResponse } from "taler-wallet-core";
@ -25,7 +25,7 @@ import { codecForBalancesResponse } from "taler-wallet-core";
/**
* Run test for basic, bank-integrated withdrawal.
*/
runTest(async (t: GlobalTestState) => {
export async function runTestWithdrawalManualTest(t: GlobalTestState) {
// Set up test environment
const {
@ -75,4 +75,4 @@ runTest(async (t: GlobalTestState) => {
t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available);
await t.shutdown();
});
}

View File

@ -0,0 +1,176 @@
/*
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/>
*/
import { GlobalTestState, runTestWithState, TestRunResult } from "./harness";
import { runPaymentTest } from "./test-payment";
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
import { runBankApiTest } from "./test-bank-api";
import { runClaimLoopTest } from "./test-claim-loop";
import { runExchangeManagementTest } from "./test-exchange-management";
import { runFeeRegressionTest } from "./test-fee-regression";
import { runMerchantLongpollingTest } from "./test-merchant-longpolling";
import { runMerchantRefundApiTest } from "./test-merchant-refund-api";
import { runPayAbortTest } from "./test-pay-abort";
import { runPayPaidTest } from "./test-pay-paid";
import { runPaymentClaimTest } from "./test-payment-claim";
import { runPaymentFaultTest } from "./test-payment-fault";
import { runPaymentIdempotencyTest } from "./test-payment-idempotency";
import { runPaymentMultipleTest } from "./test-payment-multiple";
import { runPaymentTransientTest } from "./test-payment-transient";
import { runPaywallFlowTest } from "./test-paywall-flow";
import { runRefundAutoTest } from "./test-refund-auto";
import { runRefundGoneTest } from "./test-refund-gone";
import { runRefundIncrementalTest } from "./test-refund-incremental";
import { runRefundTest } from "./test-refund";
import { runRevocationTest } from "./test-revocation";
import { runTimetravelAutorefreshTest } from "./test-timetravel-autorefresh";
import { runTimetravelWithdrawTest } from "./test-timetravel-withdraw";
import { runTippingTest } from "./test-tipping";
import { runWallettestingTest } from "./test-wallettesting";
import { runTestWithdrawalManualTest } from "./test-withdrawal-manual";
import { runWithdrawalAbortBankTest } from "./test-withdrawal-abort-bank";
import { runWithdrawalBankIntegratedTest } from "./test-withdrawal-bank-integrated";
import M from "minimatch";
/**
* Test runner.
*/
/**
* Spec for one test.
*/
interface TestMainFunction {
(t: GlobalTestState): Promise<void>;
}
const allTests: TestMainFunction[] = [
runBankApiTest,
runClaimLoopTest,
runExchangeManagementTest,
runFeeRegressionTest,
runMerchantLongpollingTest,
runMerchantRefundApiTest,
runPayAbortTest,
runPayPaidTest,
runPaymentClaimTest,
runPaymentFaultTest,
runPaymentIdempotencyTest,
runPaymentMultipleTest,
runPaymentTransientTest,
runPaywallFlowTest,
runRefundAutoTest,
runRefundGoneTest,
runRefundIncrementalTest,
runRefundTest,
runRevocationTest,
runTimetravelAutorefreshTest,
runTimetravelWithdrawTest,
runTippingTest,
runWallettestingTest,
runWithdrawalAbortBankTest,
runWithdrawalBankIntegratedTest,
runWallettestingTest,
runPaymentTest,
];
export interface TestRunSpec {
include_pattern?: string;
}
export interface TestInfo {
name: string;
}
function updateCurrentSymlink(testDir: string): void {
const currLink = path.join(os.tmpdir(), "taler-integrationtests-current");
try {
fs.unlinkSync(currLink);
} catch (e) {
// Ignore
}
try {
fs.symlinkSync(testDir, currLink);
} catch (e) {
console.log(e);
// Ignore
}
}
export function getTestName(tf: TestMainFunction): string {
const res = tf.name.match(/run([a-zA-Z0-9]*)Test/);
if (!res) {
throw Error("invalid test name, must be 'run${NAME}Test'");
}
return res[1]
.replace(/[a-z0-9][A-Z]/, (x) => {
return x[0] + "-" + x[1];
})
.toLowerCase();
}
export async function runTests(spec: TestRunSpec) {
const testRootDir = fs.mkdtempSync(
path.join(os.tmpdir(), "taler-integrationtests-"),
);
updateCurrentSymlink(testRootDir);
console.log("testsuite root directory: ", testRootDir);
let numTotal = 0;
let numFail = 0;
let numSkip = 0;
let numPass = 0;
const testResults: TestRunResult[] = [];
for (const [n, testCase] of allTests.entries()) {
const testName = getTestName(testCase);
if (spec.include_pattern && !M(testName, spec.include_pattern)) {
continue;
}
const testDir = path.join(testRootDir, testName);
fs.mkdirSync(testDir);
console.log(`running test ${testName}`);
const gc = new GlobalTestState({
testDir,
});
const result = await runTestWithState(gc, testCase, testName);
testResults.push(result);
console.log(result);
numTotal++;
if (result.status === "fail") {
numFail++;
} else if (result.status === "skip") {
numSkip++;
} else if (result.status === "pass") {
numPass++;
}
}
const resultsFile = path.join(testRootDir, "results.json");
fs.writeFileSync(
path.join(testRootDir, "results.json"),
JSON.stringify({ testResults }, undefined, 2),
);
console.log(`See ${resultsFile} for details`);
console.log(`Passed: ${numPass}/${numTotal}`);
}
export function getTestInfo(): TestInfo[] {
return allTests.map((x) => ({
name: getTestName(x),
}));
}

View File

@ -34,7 +34,7 @@ importers:
packages/taler-integrationtests:
dependencies:
axios: 0.21.0
taler-wallet-core: 'link:../taler-wallet-core'
taler-wallet-core: link:../taler-wallet-core
tslib: 2.0.3
devDependencies:
esm: 3.2.25
@ -49,13 +49,13 @@ importers:
nyc: ^15.1.0
prettier: ^2.1.2
source-map-support: ^0.5.19
taler-wallet-core: 'workspace:*'
taler-wallet-core: workspace:*
ts-node: ^9.0.0
tslib: ^2.0.3
typescript: ^4.0.5
packages/taler-wallet-android:
dependencies:
taler-wallet-core: 'link:../taler-wallet-core'
taler-wallet-core: link:../taler-wallet-core
tslib: 2.0.3
devDependencies:
'@rollup/plugin-commonjs': 16.0.0_rollup@2.33.2
@ -80,14 +80,16 @@ importers:
rollup: ^2.33.2
rollup-plugin-sourcemaps: ^0.6.3
rollup-plugin-terser: ^7.0.2
taler-wallet-core: 'workspace:*'
taler-wallet-core: workspace:*
tslib: ^2.0.3
typescript: ^4.0.5
packages/taler-wallet-cli:
dependencies:
'@types/minimatch': 3.0.3
axios: 0.21.0
minimatch: 3.0.4
source-map-support: 0.5.19
taler-wallet-core: 'link:../taler-wallet-core'
taler-wallet-core: link:../taler-wallet-core
tslib: 2.0.3
devDependencies:
'@rollup/plugin-commonjs': 16.0.0_rollup@2.33.2
@ -107,15 +109,17 @@ importers:
'@rollup/plugin-json': ^4.1.0
'@rollup/plugin-node-resolve': ^10.0.0
'@rollup/plugin-replace': ^2.3.4
'@types/minimatch': ^3.0.3
'@types/node': ^14.14.7
axios: ^0.21.0
minimatch: ^3.0.4
prettier: ^2.1.2
rimraf: ^3.0.2
rollup: ^2.33.2
rollup-plugin-sourcemaps: ^0.6.3
rollup-plugin-terser: ^7.0.2
source-map-support: ^0.5.19
taler-wallet-core: 'workspace:*'
taler-wallet-core: workspace:*
tslib: ^2.0.3
typedoc: ^0.19.2
typescript: ^4.0.5
@ -125,7 +129,7 @@ importers:
axios: 0.21.0
big-integer: 1.6.48
fflate: 0.3.10
idb-bridge: 'link:../idb-bridge'
idb-bridge: link:../idb-bridge
source-map-support: 0.5.19
tslib: 2.0.3
devDependencies:
@ -144,7 +148,7 @@ importers:
jed: 1.1.1
nyc: 15.1.0
po2json: 0.4.5
pogen: 'link:../pogen'
pogen: link:../pogen
prettier: 2.1.2
rimraf: 3.0.2
rollup: 2.33.2
@ -169,11 +173,11 @@ importers:
eslint-plugin-react-hooks: ^4.2.0
esm: ^3.2.25
fflate: ^0.3.10
idb-bridge: 'workspace:*'
idb-bridge: workspace:*
jed: ^1.1.1
nyc: ^15.1.0
po2json: ^0.4.5
pogen: 'workspace:*'
pogen: workspace:*
prettier: ^2.1.2
rimraf: ^3.0.2
rollup: ^2.33.2
@ -186,7 +190,7 @@ importers:
packages/taler-wallet-webextension:
dependencies:
moment: 2.29.1
taler-wallet-core: 'link:../taler-wallet-core'
taler-wallet-core: link:../taler-wallet-core
tslib: 2.0.3
devDependencies:
'@rollup/plugin-commonjs': 16.0.0_rollup@2.33.2
@ -232,7 +236,7 @@ importers:
rollup-plugin-ignore: ^1.0.9
rollup-plugin-sourcemaps: ^0.6.3
rollup-plugin-terser: ^7.0.2
taler-wallet-core: 'workspace:*'
taler-wallet-core: workspace:*
tslib: ^2.0.3
typescript: ^4.0.5
lockfileVersion: 5.2
@ -684,6 +688,10 @@ packages:
dev: true
resolution:
integrity: sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
/@types/minimatch/3.0.3:
dev: false
resolution:
integrity: sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
/@types/node/10.17.13:
dev: true
resolution:
@ -1222,7 +1230,6 @@ packages:
resolution:
integrity: sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA==
/balanced-match/1.0.0:
dev: true
resolution:
integrity: sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
/big-integer/1.6.48:
@ -1264,7 +1271,6 @@ packages:
dependencies:
balanced-match: 1.0.0
concat-map: 0.0.1
dev: true
resolution:
integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
/braces/3.0.2:
@ -1525,7 +1531,6 @@ packages:
resolution:
integrity: sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
/concat-map/0.0.1:
dev: true
resolution:
integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
/concordance/5.0.1:
@ -3431,7 +3436,6 @@ packages:
/minimatch/3.0.4:
dependencies:
brace-expansion: 1.1.11
dev: true
resolution:
integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
/minimist/1.2.5:

View File

@ -1,7 +1,7 @@
{
"compileOnSave": true,
"compilerOptions": {
"composite": true,
"composite": true
},
"references": [
{
@ -10,9 +10,6 @@
{
"path": "packages/taler-wallet-core/"
},
{
"path": "packages/taler-integrationtests"
},
{
"path": "packages/taler-wallet-cli"
}