summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-cli
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-cli')
-rw-r--r--packages/taler-wallet-cli/Makefile51
-rw-r--r--packages/taler-wallet-cli/README.md9
-rw-r--r--packages/taler-wallet-cli/assets/.gitkeep0
-rwxr-xr-xpackages/taler-wallet-cli/bin/taler-wallet-cli7
-rwxr-xr-xpackages/taler-wallet-cli/bin/taler-wallet-cli-local.mjs8
-rwxr-xr-xpackages/taler-wallet-cli/bin/taler-wallet-cli.mjs19
-rwxr-xr-xpackages/taler-wallet-cli/build-node.mjs75
-rwxr-xr-xpackages/taler-wallet-cli/build-qtart.mjs77
-rw-r--r--packages/taler-wallet-cli/debian/README8
-rw-r--r--packages/taler-wallet-cli/debian/changelog129
-rw-r--r--packages/taler-wallet-cli/debian/control16
-rw-r--r--packages/taler-wallet-cli/debian/copyright699
-rwxr-xr-xpackages/taler-wallet-cli/debian/rules19
-rw-r--r--packages/taler-wallet-cli/package.json36
-rw-r--r--packages/taler-wallet-cli/rollup.config.js32
-rw-r--r--packages/taler-wallet-cli/src/assets.ts50
-rw-r--r--packages/taler-wallet-cli/src/bench1.ts105
-rw-r--r--packages/taler-wallet-cli/src/clk.ts614
-rw-r--r--packages/taler-wallet-cli/src/env1.ts68
-rw-r--r--packages/taler-wallet-cli/src/harness/denomStructures.ts151
-rw-r--r--packages/taler-wallet-cli/src/harness/faultInjection.ts256
-rw-r--r--packages/taler-wallet-cli/src/harness/harness.ts1779
-rw-r--r--packages/taler-wallet-cli/src/harness/helpers.ts406
-rw-r--r--packages/taler-wallet-cli/src/harness/libeufin.ts1676
-rw-r--r--packages/taler-wallet-cli/src/harness/merchantApiTypes.ts318
-rw-r--r--packages/taler-wallet-cli/src/harness/sync.ts118
-rw-r--r--packages/taler-wallet-cli/src/import-meta-url.js2
-rw-r--r--packages/taler-wallet-cli/src/index.ts1801
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/scenario-prompt-payment.ts60
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts136
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-claim-loop.ts79
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts131
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-deposit.ts71
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-exchange-management.ts272
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts221
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-fee-regression.ts200
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankaccount.ts110
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankconnection.ts63
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade-bad-request.ts71
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade.ts70
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-permissions.ts64
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-camt.ts64
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-transactions.ts72
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-scheduling.ts107
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-users.ts63
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-bad-gateway.ts74
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts314
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-c5x.ts138
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-facade-anastasis.ts169
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-keyrotation.ts79
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-nexus-balance.ts114
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund-multiple-users.ts104
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund.ts101
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-sandbox-wire-transfer-cli.ts72
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-libeufin-tutorial.ts128
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-merchant-exchange-confusion.ts244
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-delete.ts127
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-urls.ts178
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-merchant-instances.ts182
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-merchant-longpolling.ts161
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-merchant-refund-api.ts296
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-merchant-spec-public-orders.ts620
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-pay-abort.ts156
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-pay-paid.ts225
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-payment-claim.ts109
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-payment-fault.ts217
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-payment-forgettable.ts81
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-payment-idempotency.ts114
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-payment-multiple.ts162
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-payment-on-demo.ts99
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-payment-transient.ts187
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-payment-zero.ts72
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-payment.ts55
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-paywall-flow.ts252
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-refund-auto.ts104
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-refund-gone.ts127
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-refund-incremental.ts196
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-refund.ts105
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-revocation.ts212
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-timetravel-autorefresh.ts215
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-timetravel-withdraw.ts96
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-tipping.ts130
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-basic.ts151
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-doublespend.ts168
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-wallettesting.ts226
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-withdrawal-abort-bank.ts73
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts70
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts96
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts72
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/testrunner.ts482
-rw-r--r--packages/taler-wallet-cli/src/lint.ts534
-rw-r--r--packages/taler-wallet-cli/tsconfig.json10
92 files changed, 2395 insertions, 15885 deletions
diff --git a/packages/taler-wallet-cli/Makefile b/packages/taler-wallet-cli/Makefile
new file mode 100644
index 000000000..c8529c768
--- /dev/null
+++ b/packages/taler-wallet-cli/Makefile
@@ -0,0 +1,51 @@
+# This Makefile has been placed in the public domain.
+
+ifeq ($(TOPLEVEL), yes)
+ $(info top-level build)
+ -include ../../.config.mk
+ override DESTDIR := $(TOP_DESTDIR)
+else
+ $(info package-level build)
+ -include ../../.config.mk
+ -include .config.mk
+endif
+
+$(info prefix is $(prefix))
+
+all:
+ @echo use 'make install' to build and install taler-wallet-cli
+
+ifndef prefix
+.PHONY: warn-noprefix install
+warn-noprefix:
+ @echo "no prefix configured, did you run ./configure?"
+install: warn-noprefix
+else
+LIBDIR = $(prefix)/lib/taler-wallet-cli
+BINDIR=$(prefix)/bin
+NODEDIR=$(LIBDIR)/node_modules/taler-wallet-cli
+.PHONY: install install-nodeps deps
+install-nodeps:
+ ./build-node.mjs
+ @echo installing wallet CLI to $(DESTDIR)$(prefix)
+ install -d $(DESTDIR)$(BINDIR)
+ install -d $(DESTDIR)$(LIBDIR)/build
+ install -d $(DESTDIR)$(NODEDIR)/bin
+ install -d $(DESTDIR)$(NODEDIR)/dist
+ install ./dist/taler-wallet-cli-bundled.cjs $(DESTDIR)$(NODEDIR)/dist/
+ install ./dist/taler-wallet-cli-bundled.cjs.map $(DESTDIR)$(NODEDIR)/dist/
+ install ./bin/taler-wallet-cli.mjs $(DESTDIR)$(NODEDIR)/bin/
+ install ../idb-bridge/node_modules/better-sqlite3/build/Release/better_sqlite3.node $(DESTDIR)$(LIBDIR)/build/ \
+ || echo "sqlite3 unavailable, better-sqlite3 native module not found"
+ ln -sf ../lib/taler-wallet-cli/node_modules/taler-wallet-cli/bin/taler-wallet-cli.mjs $(DESTDIR)$(BINDIR)/taler-wallet-cli
+deps:
+ pnpm install --frozen-lockfile --filter @gnu-taler/taler-wallet-cli...
+ pnpm run --filter @gnu-taler/taler-wallet-cli... compile
+install:
+ $(MAKE) deps
+ $(MAKE) install-nodeps
+endif
+
+.PHONY: deb
+deb:
+ dpkg-buildpackage -rfakeroot -b -uc -us
diff --git a/packages/taler-wallet-cli/README.md b/packages/taler-wallet-cli/README.md
new file mode 100644
index 000000000..27b2373c9
--- /dev/null
+++ b/packages/taler-wallet-cli/README.md
@@ -0,0 +1,9 @@
+# taler-wallet-cli
+
+This package provides `taler-wallet-cli`, the command-line interface for the
+GNU Taler wallet.
+
+## sqlite3 backend
+
+To be able to use the sqlite3 backend, make sure that better-sqlite3
+is installed as an optional dependency in the ../idb-bridge package.
diff --git a/packages/taler-wallet-cli/assets/.gitkeep b/packages/taler-wallet-cli/assets/.gitkeep
deleted file mode 100644
index e69de29bb..000000000
--- a/packages/taler-wallet-cli/assets/.gitkeep
+++ /dev/null
diff --git a/packages/taler-wallet-cli/bin/taler-wallet-cli b/packages/taler-wallet-cli/bin/taler-wallet-cli
deleted file mode 100755
index ca8008e30..000000000
--- a/packages/taler-wallet-cli/bin/taler-wallet-cli
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/usr/bin/env node
-try {
- require('source-map-support').install();
-} catch (e) {
- // Do nothing.
-}
-require('../dist/taler-wallet-cli.js').main();
diff --git a/packages/taler-wallet-cli/bin/taler-wallet-cli-local.mjs b/packages/taler-wallet-cli/bin/taler-wallet-cli-local.mjs
new file mode 100755
index 000000000..3620330b0
--- /dev/null
+++ b/packages/taler-wallet-cli/bin/taler-wallet-cli-local.mjs
@@ -0,0 +1,8 @@
+#!/usr/bin/env node
+
+// Execute the wallet CLI from the source directory.
+// This script is meant for testing and must not
+// be installed.
+
+import { main } from '../lib/index.js';
+main();
diff --git a/packages/taler-wallet-cli/bin/taler-wallet-cli.mjs b/packages/taler-wallet-cli/bin/taler-wallet-cli.mjs
new file mode 100755
index 000000000..082007632
--- /dev/null
+++ b/packages/taler-wallet-cli/bin/taler-wallet-cli.mjs
@@ -0,0 +1,19 @@
+#!/usr/bin/env node
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems SA
+
+ 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.
+
+ 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
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { main } from '../dist/taler-wallet-cli-bundled.cjs';
+main();
diff --git a/packages/taler-wallet-cli/build-node.mjs b/packages/taler-wallet-cli/build-node.mjs
new file mode 100755
index 000000000..047fc4527
--- /dev/null
+++ b/packages/taler-wallet-cli/build-node.mjs
@@ -0,0 +1,75 @@
+#!/usr/bin/env node
+/*
+ This file is part of GNU Taler
+ (C) 2022 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 esbuild from "esbuild";
+import path from "path";
+import fs from "fs";
+
+const BASE = process.cwd();
+
+let GIT_ROOT = BASE;
+while (!fs.existsSync(path.join(GIT_ROOT, ".git")) && GIT_ROOT !== "/") {
+ GIT_ROOT = path.join(GIT_ROOT, "../");
+}
+if (GIT_ROOT === "/") {
+ console.log("not found");
+ process.exit(1);
+}
+const GIT_HASH = GIT_ROOT === "/" ? undefined : git_hash();
+
+let _package = JSON.parse(fs.readFileSync(path.join(BASE, "package.json")));
+
+function git_hash() {
+ const rev = fs
+ .readFileSync(path.join(GIT_ROOT, ".git", "HEAD"))
+ .toString()
+ .trim()
+ .split(/.*[: ]/)
+ .slice(-1)[0];
+ if (rev.indexOf("/") === -1) {
+ return rev;
+ } else {
+ return fs
+ .readFileSync(path.join(GIT_ROOT, ".git", rev))
+ .toString()
+ .trim();
+ }
+}
+
+export const buildConfig = {
+ entryPoints: ["src/index.ts"],
+ outfile: "dist/taler-wallet-cli-bundled.cjs",
+ bundle: true,
+ minify: false,
+ target: ["es2020"],
+ format: "cjs",
+ platform: "node",
+ sourcemap: true,
+ inject: ["src/import-meta-url.js"],
+ define: {
+ __VERSION__: `"${_package.version}"`,
+ __GIT_HASH__: `"${GIT_HASH}"`,
+ "walletCoreBuildInfo.implementationSemver": `"${_package.version}"`,
+ "walletCoreBuildInfo.implementationGitHash": `"${GIT_HASH}"`,
+ ["import.meta.url"]: "import_meta_url",
+ },
+};
+
+esbuild.build(buildConfig).catch((e) => {
+ console.log(e);
+ process.exit(1);
+});
diff --git a/packages/taler-wallet-cli/build-qtart.mjs b/packages/taler-wallet-cli/build-qtart.mjs
new file mode 100755
index 000000000..04b2e718f
--- /dev/null
+++ b/packages/taler-wallet-cli/build-qtart.mjs
@@ -0,0 +1,77 @@
+#!/usr/bin/env node
+/*
+ This file is part of GNU Taler
+ (C) 2022 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 esbuild from "esbuild";
+import path from "path";
+import fs from "fs";
+
+const BASE = process.cwd();
+
+let GIT_ROOT = BASE;
+while (!fs.existsSync(path.join(GIT_ROOT, ".git")) && GIT_ROOT !== "/") {
+ GIT_ROOT = path.join(GIT_ROOT, "../");
+}
+if (GIT_ROOT === "/") {
+ console.log("not found");
+ process.exit(1);
+}
+const GIT_HASH = GIT_ROOT === "/" ? undefined : git_hash();
+
+let _package = JSON.parse(fs.readFileSync(path.join(BASE, "package.json")));
+
+function git_hash() {
+ const rev = fs
+ .readFileSync(path.join(GIT_ROOT, ".git", "HEAD"))
+ .toString()
+ .trim()
+ .split(/.*[: ]/)
+ .slice(-1)[0];
+ if (rev.indexOf("/") === -1) {
+ return rev;
+ } else {
+ return fs
+ .readFileSync(path.join(GIT_ROOT, ".git", rev))
+ .toString()
+ .trim();
+ }
+}
+
+export const buildConfig = {
+ entryPoints: ["src/index.ts"],
+ outfile: "dist/taler-wallet-cli.qtart.mjs",
+ bundle: true,
+ minify: false,
+ target: ["es2020"],
+ format: "esm",
+ platform: "neutral",
+ mainFields: ["module", "main"],
+ conditions: ["qtart"],
+ sourcemap: true,
+ // quickjs standard library
+ external: ["std", "os", "better-sqlite3"],
+ define: {
+ __VERSION__: `"${_package.version}"`,
+ __GIT_HASH__: `"${GIT_HASH}"`,
+ "walletCoreBuildInfo.implementationSemver": `"${_package.version}"`,
+ "walletCoreBuildInfo.implementationGitHash": `"${GIT_HASH}"`,
+ },
+};
+
+esbuild.build(buildConfig).catch((e) => {
+ console.log(e);
+ process.exit(1);
+});
diff --git a/packages/taler-wallet-cli/debian/README b/packages/taler-wallet-cli/debian/README
new file mode 100644
index 000000000..d221c6e37
--- /dev/null
+++ b/packages/taler-wallet-cli/debian/README
@@ -0,0 +1,8 @@
+For the moment, building the Debian package needs
+a preliminary manual step to install the taler-wallet-cli
+Node.JS package. In the future, this will either be invoked
+by DH, or added as packaging instructions to Debian.
+
+$ ./configure --prefix=/usr
+$ make install
+$ dpkg-buildpackage -rfakeroot -b -uc -us
diff --git a/packages/taler-wallet-cli/debian/changelog b/packages/taler-wallet-cli/debian/changelog
new file mode 100644
index 000000000..e136caa61
--- /dev/null
+++ b/packages/taler-wallet-cli/debian/changelog
@@ -0,0 +1,129 @@
+taler-wallet-cli (0.10.7) unstable; urgency=low
+
+ * Release 0.10.7
+
+ -- Florian Dold <dold@taler.net> Mon, 22 Apr 2024 20:16:39 +0200
+
+taler-wallet-cli (0.10.6) unstable; urgency=low
+
+ * Release 0.10.6
+
+ -- Florian Dold <dold@taler.net> Wed, 10 Apr 2024 15:19:33 +0200
+
+taler-wallet-cli (0.10.5) unstable; urgency=low
+
+ * Release 0.10.5
+
+ -- Florian Dold <dold@taler.net> Tue, 09 Apr 2024 16:13:58 +0200
+
+taler-wallet-cli (0.10.4) unstable; urgency=low
+
+ * Release 0.10.4
+
+ -- Florian Dold <dold@taler.net> Tue, 09 Apr 2024 14:39:44 +0200
+
+taler-wallet-cli (0.9.4a) unstable; urgency=low
+
+ * Release v0.9.4a.
+
+ -- Florian Dold <dold@taler.net> Thu, 07 Mar 2024 23:43:05 +0100
+
+taler-wallet-cli (0.9.3-1) unstable; urgency=low
+
+ * Misc bugfixes.
+
+ -- Christian Grothoff <grothoff@gnu.org> Tue, 13 Dec 2023 18:53:15 -0700
+
+taler-wallet-cli (0.9.3) unstable; urgency=low
+
+ * First release for GNU Taler v0.9.3.
+
+ -- Christian Grothoff <grothoff@gnu.org> Wed, 29 Nov 2023 08:53:15 -0800
+
+taler-wallet-cli (0.9.2-2) unstable; urgency=low
+
+ * Release with various Debian package fixes.
+
+ -- Christian Grothoff <grothoff@gnu.org> Sat, 3 Mar 2023 23:47:15 -0300
+
+taler-wallet-cli (0.9.2) unstable; urgency=low
+
+ * Official 0.9.2 release.
+
+ -- Christian Grothoff <grothoff@gnu.org> Sat, 25 Feb 2023 12:47:15 -0300
+
+taler-wallet-cli (0.9.1) unstable; urgency=low
+
+ * Official 0.9.1 release.
+
+ -- Christian Grothoff <grothoff@gnu.org> Thu, 26 Jan 2023 12:47:15 -0300
+
+taler-wallet-cli (0.9.0) unstable; urgency=low
+
+ * Official 0.9.0 release.
+
+ -- Christian Grothoff <grothoff@gnu.org> Sat, 5 Nov 2022 12:47:15 -0300
+
+taler-wallet-cli (0.8.99) unstable; urgency=low
+
+ * Preview for 0.9 wallet.
+
+ -- Christian Grothoff <grothoff@gnu.org> Tue, 21 Jun 2022 12:47:15 -0300
+
+taler-wallet-cli (0.8.3) unstable; urgency=low
+
+ * Official 0.8.3 release.
+
+ -- Sebastian Marchano <sebasjm@gmail.com> Fri, 26 Nov 2021 12:47:15 -0300
+
+taler-wallet-cli (0.8.2) unstable; urgency=low
+
+ * Official 0.8.2 release.
+
+ -- Florian Dold <dold@taler.net> Tue, 24 Aug 2021 15:47:15 +0200
+
+taler-wallet-cli (0.0.1-6) unstable; urgency=low
+
+ * Fix deposit tracking.
+
+ -- Florian Dold <dold@taler.net> Sat, 07 Aug 2021 17:40:08 +0200
+
+taler-wallet-cli (0.0.1-5) unstable; urgency=low
+
+ * Performance improvements.
+
+ -- Florian Dold <dold@taler.net> Fri, 06 Aug 2021 17:16:18 +0200
+
+taler-wallet-cli (0.0.1-4) unstable; urgency=low
+
+ * Deployment linting improvements.
+
+ -- Florian Dold <dold@taler.net> Fri, 06 Aug 2021 11:58:34 +0200
+
+taler-wallet-cli (0.0.1-3) unstable; urgency=low
+
+ * Deployment linting improvements.
+
+ -- Florian Dold <dold@taler.net> Thu, 05 Aug 2021 00:02:33 +0200
+
+taler-wallet-cli (0.0.1-2) unstable; urgency=low
+
+ * Improved denomination generator.
+
+ -- Florian Dold <dold@taler.net> Wed, 04 Aug 2021 23:26:17 +0200
+
+taler-wallet-cli (0.0.1-1) unstable; urgency=low
+
+ * Added exchange deployment linting tool.
+
+ -- Florian Dold <dold@taler.net> Wed, 04 Aug 2021 23:05:47 +0200
+
+taler-wallet-cli (0.0.1) unstable; urgency=low
+
+ * Initial Release.
+
+ -- Florian Dold <dold@taler.net> Sun, 14 Jul 2021 15:00:00 +0100
+
+Local variables:
+mode: debian-changelog
+End:
diff --git a/packages/taler-wallet-cli/debian/control b/packages/taler-wallet-cli/debian/control
new file mode 100644
index 000000000..41b507480
--- /dev/null
+++ b/packages/taler-wallet-cli/debian/control
@@ -0,0 +1,16 @@
+Source: taler-wallet-cli
+Section: networking
+Priority: optional
+Maintainer: Taler Systems SA <deb@taler.net>
+Uploaders: Christian Grothoff <grothoff@gnu.org>, Florian Dold <dold@taler.net>
+Build-Depends: debhelper-compat (= 12),
+Standards-Version: 4.1.0
+Vcs-Git: https://git.taler.net/wallet-core.git
+Homepage: https://taler.net/
+
+Package: taler-wallet-cli
+Architecture: all
+Depends: nodejs,
+ ${misc:Depends}
+Recommends:
+Description: This is a command-line interface version of the GNU Taler wallet.
diff --git a/packages/taler-wallet-cli/debian/copyright b/packages/taler-wallet-cli/debian/copyright
new file mode 100644
index 000000000..555d608fa
--- /dev/null
+++ b/packages/taler-wallet-cli/debian/copyright
@@ -0,0 +1,699 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: GNU Taler
+Upstream-Contact: Christian Grothoff <christian@grothoff.org>
+Source: https://taler.net/
+
+Files: *
+Copyright:
+ (C) 2013-2020 Taler Systems SA
+License: AGPL-3+
+Comment: Many contributors are mentioned in AUTHORS
+
+Files: debian/*
+Copyright:
+ (C) 2020 Christian Grothoff <grothoff@gnu.org>
+License: GPL-3+
+
+Files: debian/po/*
+Copyright:
+License: GPL-3+
+
+License: GPL-3+
+ This program 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 of the License, or
+ (at your option) any later version.
+ .
+ This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+ .
+ The complete text of the GNU General Public License
+ can be found in /usr/share/common-licenses/GPL-3 file.
+
+License: AGPL-3+
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+ .
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+ .
+ Preamble
+ .
+ The GNU Affero General Public License is a free, copyleft license for
+ software and other kinds of works, specifically designed to ensure
+ cooperation with the community in the case of network server software.
+ .
+ The licenses for most software and other practical works are designed
+ to take away your freedom to share and change the works. By contrast,
+ our General Public Licenses are intended to guarantee your freedom to
+ share and change all versions of a program--to make sure it remains free
+ software for all its users.
+ .
+ When we speak of free software, we are referring to freedom, not
+ price. Our General Public Licenses are designed to make sure that you
+ have the freedom to distribute copies of free software (and charge for
+ them if you wish), that you receive source code or can get it if you
+ want it, that you can change the software or use pieces of it in new
+ free programs, and that you know you can do these things.
+ .
+ Developers that use our General Public Licenses protect your rights
+ with two steps: (1) assert copyright on the software, and (2) offer
+ you this License which gives you legal permission to copy, distribute
+ and/or modify the software.
+ .
+ A secondary benefit of defending all users' freedom is that
+ improvements made in alternate versions of the program, if they
+ receive widespread use, become available for other developers to
+ incorporate. Many developers of free software are heartened and
+ encouraged by the resulting cooperation. However, in the case of
+ software used on network servers, this result may fail to come about.
+ The GNU General Public License permits making a modified version and
+ letting the public access it on a server without ever releasing its
+ source code to the public.
+ .
+ The GNU Affero General Public License is designed specifically to
+ ensure that, in such cases, the modified source code becomes available
+ to the community. It requires the operator of a network server to
+ provide the source code of the modified version running there to the
+ users of that server. Therefore, public use of a modified version, on
+ a publicly accessible server, gives the public access to the source
+ code of the modified version.
+ .
+ An older license, called the Affero General Public License and
+ published by Affero, was designed to accomplish similar goals. This is
+ a different license, not a version of the Affero GPL, but Affero has
+ released a new version of the Affero GPL which permits relicensing under
+ this license.
+ .
+ The precise terms and conditions for copying, distribution and
+ modification follow.
+ .
+ TERMS AND CONDITIONS
+ .
+ 0. Definitions.
+ .
+ "This License" refers to version 3 of the GNU Affero General Public License.
+ .
+ "Copyright" also means copyright-like laws that apply to other kinds of
+ works, such as semiconductor masks.
+ .
+ "The Program" refers to any copyrightable work licensed under this
+ License. Each licensee is addressed as "you". "Licensees" and
+ "recipients" may be individuals or organizations.
+ .
+ To "modify" a work means to copy from or adapt all or part of the work
+ in a fashion requiring copyright permission, other than the making of an
+ exact copy. The resulting work is called a "modified version" of the
+ earlier work or a work "based on" the earlier work.
+ .
+ A "covered work" means either the unmodified Program or a work based
+ on the Program.
+ .
+ To "propagate" a work means to do anything with it that, without
+ permission, would make you directly or secondarily liable for
+ infringement under applicable copyright law, except executing it on a
+ computer or modifying a private copy. Propagation includes copying,
+ distribution (with or without modification), making available to the
+ public, and in some countries other activities as well.
+ .
+ To "convey" a work means any kind of propagation that enables other
+ parties to make or receive copies. Mere interaction with a user through
+ a computer network, with no transfer of a copy, is not conveying.
+ .
+ An interactive user interface displays "Appropriate Legal Notices"
+ to the extent that it includes a convenient and prominently visible
+ feature that (1) displays an appropriate copyright notice, and (2)
+ tells the user that there is no warranty for the work (except to the
+ extent that warranties are provided), that licensees may convey the
+ work under this License, and how to view a copy of this License. If
+ the interface presents a list of user commands or options, such as a
+ menu, a prominent item in the list meets this criterion.
+ .
+ 1. Source Code.
+ .
+ The "source code" for a work means the preferred form of the work
+ for making modifications to it. "Object code" means any non-source
+ form of a work.
+ .
+ A "Standard Interface" means an interface that either is an official
+ standard defined by a recognized standards body, or, in the case of
+ interfaces specified for a particular programming language, one that
+ is widely used among developers working in that language.
+ .
+ The "System Libraries" of an executable work include anything, other
+ than the work as a whole, that (a) is included in the normal form of
+ packaging a Major Component, but which is not part of that Major
+ Component, and (b) serves only to enable use of the work with that
+ Major Component, or to implement a Standard Interface for which an
+ implementation is available to the public in source code form. A
+ "Major Component", in this context, means a major essential component
+ (kernel, window system, and so on) of the specific operating system
+ (if any) on which the executable work runs, or a compiler used to
+ produce the work, or an object code interpreter used to run it.
+ .
+ The "Corresponding Source" for a work in object code form means all
+ the source code needed to generate, install, and (for an executable
+ work) run the object code and to modify the work, including scripts to
+ control those activities. However, it does not include the work's
+ System Libraries, or general-purpose tools or generally available free
+ programs which are used unmodified in performing those activities but
+ which are not part of the work. For example, Corresponding Source
+ includes interface definition files associated with source files for
+ the work, and the source code for shared libraries and dynamically
+ linked subprograms that the work is specifically designed to require,
+ such as by intimate data communication or control flow between those
+ subprograms and other parts of the work.
+ .
+ The Corresponding Source need not include anything that users
+ can regenerate automatically from other parts of the Corresponding
+ Source.
+ .
+ The Corresponding Source for a work in source code form is that
+ same work.
+ .
+ 2. Basic Permissions.
+ .
+ All rights granted under this License are granted for the term of
+ copyright on the Program, and are irrevocable provided the stated
+ conditions are met. This License explicitly affirms your unlimited
+ permission to run the unmodified Program. The output from running a
+ covered work is covered by this License only if the output, given its
+ content, constitutes a covered work. This License acknowledges your
+ rights of fair use or other equivalent, as provided by copyright law.
+ .
+ You may make, run and propagate covered works that you do not
+ convey, without conditions so long as your license otherwise remains
+ in force. You may convey covered works to others for the sole purpose
+ of having them make modifications exclusively for you, or provide you
+ with facilities for running those works, provided that you comply with
+ the terms of this License in conveying all material for which you do
+ not control copyright. Those thus making or running the covered works
+ for you must do so exclusively on your behalf, under your direction
+ and control, on terms that prohibit them from making any copies of
+ your copyrighted material outside their relationship with you.
+ .
+ Conveying under any other circumstances is permitted solely under
+ the conditions stated below. Sublicensing is not allowed; section 10
+ makes it unnecessary.
+ .
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+ .
+ No covered work shall be deemed part of an effective technological
+ measure under any applicable law fulfilling obligations under article
+ 11 of the WIPO copyright treaty adopted on 20 December 1996, or
+ similar laws prohibiting or restricting circumvention of such
+ measures.
+ .
+ When you convey a covered work, you waive any legal power to forbid
+ circumvention of technological measures to the extent such circumvention
+ is effected by exercising rights under this License with respect to
+ the covered work, and you disclaim any intention to limit operation or
+ modification of the work as a means of enforcing, against the work's
+ users, your or third parties' legal rights to forbid circumvention of
+ technological measures.
+ .
+ 4. Conveying Verbatim Copies.
+ .
+ You may convey verbatim copies of the Program's source code as you
+ receive it, in any medium, provided that you conspicuously and
+ appropriately publish on each copy an appropriate copyright notice;
+ keep intact all notices stating that this License and any
+ non-permissive terms added in accord with section 7 apply to the code;
+ keep intact all notices of the absence of any warranty; and give all
+ recipients a copy of this License along with the Program.
+ .
+ You may charge any price or no price for each copy that you convey,
+ and you may offer support or warranty protection for a fee.
+ .
+ 5. Conveying Modified Source Versions.
+ .
+ You may convey a work based on the Program, or the modifications to
+ produce it from the Program, in the form of source code under the
+ terms of section 4, provided that you also meet all of these conditions:
+ .
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+ .
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+ .
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+ .
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+ .
+ A compilation of a covered work with other separate and independent
+ works, which are not by their nature extensions of the covered work,
+ and which are not combined with it such as to form a larger program,
+ in or on a volume of a storage or distribution medium, is called an
+ "aggregate" if the compilation and its resulting copyright are not
+ used to limit the access or legal rights of the compilation's users
+ beyond what the individual works permit. Inclusion of a covered work
+ in an aggregate does not cause this License to apply to the other
+ parts of the aggregate.
+ .
+ 6. Conveying Non-Source Forms.
+ .
+ You may convey a covered work in object code form under the terms
+ of sections 4 and 5, provided that you also convey the
+ machine-readable Corresponding Source under the terms of this License,
+ in one of these ways:
+ .
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+ .
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+ .
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+ .
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+ .
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+ .
+ A separable portion of the object code, whose source code is excluded
+ from the Corresponding Source as a System Library, need not be
+ included in conveying the object code work.
+ .
+ A "User Product" is either (1) a "consumer product", which means any
+ tangible personal property which is normally used for personal, family,
+ or household purposes, or (2) anything designed or sold for incorporation
+ into a dwelling. In determining whether a product is a consumer product,
+ doubtful cases shall be resolved in favor of coverage. For a particular
+ product received by a particular user, "normally used" refers to a
+ typical or common use of that class of product, regardless of the status
+ of the particular user or of the way in which the particular user
+ actually uses, or expects or is expected to use, the product. A product
+ is a consumer product regardless of whether the product has substantial
+ commercial, industrial or non-consumer uses, unless such uses represent
+ the only significant mode of use of the product.
+ .
+ "Installation Information" for a User Product means any methods,
+ procedures, authorization keys, or other information required to install
+ and execute modified versions of a covered work in that User Product from
+ a modified version of its Corresponding Source. The information must
+ suffice to ensure that the continued functioning of the modified object
+ code is in no case prevented or interfered with solely because
+ modification has been made.
+ .
+ If you convey an object code work under this section in, or with, or
+ specifically for use in, a User Product, and the conveying occurs as
+ part of a transaction in which the right of possession and use of the
+ User Product is transferred to the recipient in perpetuity or for a
+ fixed term (regardless of how the transaction is characterized), the
+ Corresponding Source conveyed under this section must be accompanied
+ by the Installation Information. But this requirement does not apply
+ if neither you nor any third party retains the ability to install
+ modified object code on the User Product (for example, the work has
+ been installed in ROM).
+ .
+ The requirement to provide Installation Information does not include a
+ requirement to continue to provide support service, warranty, or updates
+ for a work that has been modified or installed by the recipient, or for
+ the User Product in which it has been modified or installed. Access to a
+ network may be denied when the modification itself materially and
+ adversely affects the operation of the network or violates the rules and
+ protocols for communication across the network.
+ .
+ Corresponding Source conveyed, and Installation Information provided,
+ in accord with this section must be in a format that is publicly
+ documented (and with an implementation available to the public in
+ source code form), and must require no special password or key for
+ unpacking, reading or copying.
+ .
+ 7. Additional Terms.
+ .
+ "Additional permissions" are terms that supplement the terms of this
+ License by making exceptions from one or more of its conditions.
+ Additional permissions that are applicable to the entire Program shall
+ be treated as though they were included in this License, to the extent
+ that they are valid under applicable law. If additional permissions
+ apply only to part of the Program, that part may be used separately
+ under those permissions, but the entire Program remains governed by
+ this License without regard to the additional permissions.
+ .
+ When you convey a copy of a covered work, you may at your option
+ remove any additional permissions from that copy, or from any part of
+ it. (Additional permissions may be written to require their own
+ removal in certain cases when you modify the work.) You may place
+ additional permissions on material, added by you to a covered work,
+ for which you have or can give appropriate copyright permission.
+ .
+ Notwithstanding any other provision of this License, for material you
+ add to a covered work, you may (if authorized by the copyright holders of
+ that material) supplement the terms of this License with terms:
+ .
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+ .
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+ .
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+ .
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+ .
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+ .
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+ .
+ All other non-permissive additional terms are considered "further
+ restrictions" within the meaning of section 10. If the Program as you
+ received it, or any part of it, contains a notice stating that it is
+ governed by this License along with a term that is a further
+ restriction, you may remove that term. If a license document contains
+ a further restriction but permits relicensing or conveying under this
+ License, you may add to a covered work material governed by the terms
+ of that license document, provided that the further restriction does
+ not survive such relicensing or conveying.
+ .
+ If you add terms to a covered work in accord with this section, you
+ must place, in the relevant source files, a statement of the
+ additional terms that apply to those files, or a notice indicating
+ where to find the applicable terms.
+ .
+ Additional terms, permissive or non-permissive, may be stated in the
+ form of a separately written license, or stated as exceptions;
+ the above requirements apply either way.
+ .
+ 8. Termination.
+ .
+ You may not propagate or modify a covered work except as expressly
+ provided under this License. Any attempt otherwise to propagate or
+ modify it is void, and will automatically terminate your rights under
+ this License (including any patent licenses granted under the third
+ paragraph of section 11).
+ .
+ However, if you cease all violation of this License, then your
+ license from a particular copyright holder is reinstated (a)
+ provisionally, unless and until the copyright holder explicitly and
+ finally terminates your license, and (b) permanently, if the copyright
+ holder fails to notify you of the violation by some reasonable means
+ prior to 60 days after the cessation.
+ .
+ Moreover, your license from a particular copyright holder is
+ reinstated permanently if the copyright holder notifies you of the
+ violation by some reasonable means, this is the first time you have
+ received notice of violation of this License (for any work) from that
+ copyright holder, and you cure the violation prior to 30 days after
+ your receipt of the notice.
+ .
+ Termination of your rights under this section does not terminate the
+ licenses of parties who have received copies or rights from you under
+ this License. If your rights have been terminated and not permanently
+ reinstated, you do not qualify to receive new licenses for the same
+ material under section 10.
+ .
+ 9. Acceptance Not Required for Having Copies.
+ .
+ You are not required to accept this License in order to receive or
+ run a copy of the Program. Ancillary propagation of a covered work
+ occurring solely as a consequence of using peer-to-peer transmission
+ to receive a copy likewise does not require acceptance. However,
+ nothing other than this License grants you permission to propagate or
+ modify any covered work. These actions infringe copyright if you do
+ not accept this License. Therefore, by modifying or propagating a
+ covered work, you indicate your acceptance of this License to do so.
+ .
+ 10. Automatic Licensing of Downstream Recipients.
+ .
+ Each time you convey a covered work, the recipient automatically
+ receives a license from the original licensors, to run, modify and
+ propagate that work, subject to this License. You are not responsible
+ for enforcing compliance by third parties with this License.
+ .
+ An "entity transaction" is a transaction transferring control of an
+ organization, or substantially all assets of one, or subdividing an
+ organization, or merging organizations. If propagation of a covered
+ work results from an entity transaction, each party to that
+ transaction who receives a copy of the work also receives whatever
+ licenses to the work the party's predecessor in interest had or could
+ give under the previous paragraph, plus a right to possession of the
+ Corresponding Source of the work from the predecessor in interest, if
+ the predecessor has it or can get it with reasonable efforts.
+ .
+ You may not impose any further restrictions on the exercise of the
+ rights granted or affirmed under this License. For example, you may
+ not impose a license fee, royalty, or other charge for exercise of
+ rights granted under this License, and you may not initiate litigation
+ (including a cross-claim or counterclaim in a lawsuit) alleging that
+ any patent claim is infringed by making, using, selling, offering for
+ sale, or importing the Program or any portion of it.
+ .
+ 11. Patents.
+ .
+ A "contributor" is a copyright holder who authorizes use under this
+ License of the Program or a work on which the Program is based. The
+ work thus licensed is called the contributor's "contributor version".
+ .
+ A contributor's "essential patent claims" are all patent claims
+ owned or controlled by the contributor, whether already acquired or
+ hereafter acquired, that would be infringed by some manner, permitted
+ by this License, of making, using, or selling its contributor version,
+ but do not include claims that would be infringed only as a
+ consequence of further modification of the contributor version. For
+ purposes of this definition, "control" includes the right to grant
+ patent sublicenses in a manner consistent with the requirements of
+ this License.
+ .
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+ patent license under the contributor's essential patent claims, to
+ make, use, sell, offer for sale, import and otherwise run, modify and
+ propagate the contents of its contributor version.
+ .
+ In the following three paragraphs, a "patent license" is any express
+ agreement or commitment, however denominated, not to enforce a patent
+ (such as an express permission to practice a patent or covenant not to
+ sue for patent infringement). To "grant" such a patent license to a
+ party means to make such an agreement or commitment not to enforce a
+ patent against the party.
+ .
+ If you convey a covered work, knowingly relying on a patent license,
+ and the Corresponding Source of the work is not available for anyone
+ to copy, free of charge and under the terms of this License, through a
+ publicly available network server or other readily accessible means,
+ then you must either (1) cause the Corresponding Source to be so
+ available, or (2) arrange to deprive yourself of the benefit of the
+ patent license for this particular work, or (3) arrange, in a manner
+ consistent with the requirements of this License, to extend the patent
+ license to downstream recipients. "Knowingly relying" means you have
+ actual knowledge that, but for the patent license, your conveying the
+ covered work in a country, or your recipient's use of the covered work
+ in a country, would infringe one or more identifiable patents in that
+ country that you have reason to believe are valid.
+ .
+ If, pursuant to or in connection with a single transaction or
+ arrangement, you convey, or propagate by procuring conveyance of, a
+ covered work, and grant a patent license to some of the parties
+ receiving the covered work authorizing them to use, propagate, modify
+ or convey a specific copy of the covered work, then the patent license
+ you grant is automatically extended to all recipients of the covered
+ work and works based on it.
+ .
+ A patent license is "discriminatory" if it does not include within
+ the scope of its coverage, prohibits the exercise of, or is
+ conditioned on the non-exercise of one or more of the rights that are
+ specifically granted under this License. You may not convey a covered
+ work if you are a party to an arrangement with a third party that is
+ in the business of distributing software, under which you make payment
+ to the third party based on the extent of your activity of conveying
+ the work, and under which the third party grants, to any of the
+ parties who would receive the covered work from you, a discriminatory
+ patent license (a) in connection with copies of the covered work
+ conveyed by you (or copies made from those copies), or (b) primarily
+ for and in connection with specific products or compilations that
+ contain the covered work, unless you entered into that arrangement,
+ or that patent license was granted, prior to 28 March 2007.
+ .
+ Nothing in this License shall be construed as excluding or limiting
+ any implied license or other defenses to infringement that may
+ otherwise be available to you under applicable patent law.
+ .
+ 12. No Surrender of Others' Freedom.
+ .
+ If conditions are imposed on you (whether by court order, agreement or
+ otherwise) that contradict the conditions of this License, they do not
+ excuse you from the conditions of this License. If you cannot convey a
+ covered work so as to satisfy simultaneously your obligations under this
+ License and any other pertinent obligations, then as a consequence you may
+ not convey it at all. For example, if you agree to terms that obligate you
+ to collect a royalty for further conveying from those to whom you convey
+ the Program, the only way you could satisfy both those terms and this
+ License would be to refrain entirely from conveying the Program.
+ .
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+ .
+ Notwithstanding any other provision of this License, if you modify the
+ Program, your modified version must prominently offer all users
+ interacting with it remotely through a computer network (if your version
+ supports such interaction) an opportunity to receive the Corresponding
+ Source of your version by providing access to the Corresponding Source
+ from a network server at no charge, through some standard or customary
+ means of facilitating copying of software. This Corresponding Source
+ shall include the Corresponding Source for any work covered by version 3
+ of the GNU General Public License that is incorporated pursuant to the
+ following paragraph.
+ .
+ Notwithstanding any other provision of this License, you have
+ permission to link or combine any covered work with a work licensed
+ under version 3 of the GNU General Public License into a single
+ combined work, and to convey the resulting work. The terms of this
+ License will continue to apply to the part which is the covered work,
+ but the work with which it is combined will remain governed by version
+ 3 of the GNU General Public License.
+ .
+ 14. Revised Versions of this License.
+ .
+ The Free Software Foundation may publish revised and/or new versions of
+ the GNU Affero General Public License from time to time. Such new versions
+ will be similar in spirit to the present version, but may differ in detail to
+ address new problems or concerns.
+ .
+ Each version is given a distinguishing version number. If the
+ Program specifies that a certain numbered version of the GNU Affero General
+ Public License "or any later version" applies to it, you have the
+ option of following the terms and conditions either of that numbered
+ version or of any later version published by the Free Software
+ Foundation. If the Program does not specify a version number of the
+ GNU Affero General Public License, you may choose any version ever published
+ by the Free Software Foundation.
+ .
+ If the Program specifies that a proxy can decide which future
+ versions of the GNU Affero General Public License can be used, that proxy's
+ public statement of acceptance of a version permanently authorizes you
+ to choose that version for the Program.
+ .
+ Later license versions may give you additional or different
+ permissions. However, no additional obligations are imposed on any
+ author or copyright holder as a result of your choosing to follow a
+ later version.
+ .
+ 15. Disclaimer of Warranty.
+ .
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+ APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+ HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+ OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+ IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+ .
+ 16. Limitation of Liability.
+ .
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+ THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+ GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+ USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+ DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+ PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+ EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGES.
+ .
+ 17. Interpretation of Sections 15 and 16.
+ .
+ If the disclaimer of warranty and limitation of liability provided
+ above cannot be given local legal effect according to their terms,
+ reviewing courts shall apply local law that most closely approximates
+ an absolute waiver of all civil liability in connection with the
+ Program, unless a warranty or assumption of liability accompanies a
+ copy of the Program in return for a fee.
+ .
+ END OF TERMS AND CONDITIONS
+ .
+ How to Apply These Terms to Your New Programs
+ .
+ If you develop a new program, and you want it to be of the greatest
+ possible use to the public, the best way to achieve this is to make it
+ free software which everyone can redistribute and change under these terms.
+ .
+ To do so, attach the following notices to the program. It is safest
+ to attach them to the start of each source file to most effectively
+ state the exclusion of warranty; and each file should have at least
+ the "copyright" line and a pointer to where the full notice is found.
+ .
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+ .
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ .
+ This program 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 Affero General Public License for more details.
+ .
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+ .
+ Also add information on how to contact you by electronic and paper mail.
+ .
+ If your software can interact with users remotely through a computer
+ network, you should also make sure that it provides a way for users to
+ get its source. For example, if your program is a web application, its
+ interface could display a "Source" link that leads users to an archive
+ of the code. There are many ways you could offer source, and different
+ solutions will be better for different programs; see section 13 for the
+ specific requirements.
+ .
+ You should also get your employer (if you work as a programmer) or school,
+ if any, to sign a "copyright disclaimer" for the program, if necessary.
+ For more information on this, and how to apply and follow the GNU AGPL, see
+ <http://www.gnu.org/licenses/>.
diff --git a/packages/taler-wallet-cli/debian/rules b/packages/taler-wallet-cli/debian/rules
new file mode 100755
index 000000000..0a8d6408c
--- /dev/null
+++ b/packages/taler-wallet-cli/debian/rules
@@ -0,0 +1,19 @@
+#!/usr/bin/make -f
+
+%:
+ dh ${@}
+
+# Override because our configure doesn't like extra arguments.
+override_dh_auto_configure:
+ ./configure --prefix=/usr
+
+override_dh_builddeb:
+ dh_builddeb -- -Zgzip
+
+# Override this step because it's very slow and likely
+# unnecessary for us.
+override_dh_strip_nondeterminism:
+ true
+
+get-orig-source:
+ uscan --force-download --rename
diff --git a/packages/taler-wallet-cli/package.json b/packages/taler-wallet-cli/package.json
index 52a8f817e..922556749 100644
--- a/packages/taler-wallet-cli/package.json
+++ b/packages/taler-wallet-cli/package.json
@@ -1,9 +1,9 @@
{
"name": "@gnu-taler/taler-wallet-cli",
- "version": "0.8.1",
+ "version": "0.10.7",
"description": "",
"engines": {
- "node": ">=0.12.0"
+ "node": ">=0.18.0"
},
"repository": {
"type": "git",
@@ -11,14 +11,15 @@
},
"author": "Florian Dold",
"license": "GPL-3.0",
- "main": "dist/taler-wallet-cli.js",
"bin": {
- "taler-wallet-cli": "./bin/taler-wallet-cli"
+ "taler-wallet-cli": "./bin/taler-wallet-cli.mjs"
},
+ "type": "module",
"scripts": {
- "prepare": "tsc && rollup -c",
- "compile": "tsc && rollup -c",
- "clean": "rimraf lib dist tsconfig.tsbuildinfo",
+ "compile": "tsc && ./build-node.mjs",
+ "test": "tsc",
+ "typedoc": "typedoc --out dist/typedoc ./src/",
+ "clean": "rm -rf lib dist tsconfig.tsbuildinfo",
"pretty": "prettier --write src"
},
"files": [
@@ -30,25 +31,14 @@
"src/"
],
"devDependencies": {
- "@rollup/plugin-commonjs": "^17.0.0",
- "@rollup/plugin-json": "^4.1.0",
- "@rollup/plugin-node-resolve": "^11.1.0",
- "@rollup/plugin-replace": "^2.3.4",
- "@types/node": "^14.14.22",
- "prettier": "^2.2.1",
- "rimraf": "^3.0.2",
- "rollup": "^2.37.1",
- "rollup-plugin-sourcemaps": "^0.6.3",
- "rollup-plugin-terser": "^7.0.2",
- "typedoc": "^0.20.16",
- "typescript": "^4.1.3"
+ "@types/node": "^18.11.17",
+ "prettier": "^3.1.1",
+ "typedoc": "^0.25.4",
+ "typescript": "^5.3.3"
},
"dependencies": {
"@gnu-taler/taler-util": "workspace:*",
"@gnu-taler/taler-wallet-core": "workspace:*",
- "axios": "^0.21.1",
- "cancellationtoken": "^2.2.0",
- "source-map-support": "^0.5.19",
- "tslib": "^2.1.0"
+ "tslib": "^2.6.2"
}
}
diff --git a/packages/taler-wallet-cli/rollup.config.js b/packages/taler-wallet-cli/rollup.config.js
deleted file mode 100644
index e7d7a7007..000000000
--- a/packages/taler-wallet-cli/rollup.config.js
+++ /dev/null
@@ -1,32 +0,0 @@
-// rollup.config.js
-import commonjs from "@rollup/plugin-commonjs";
-import nodeResolve from "@rollup/plugin-node-resolve";
-import json from "@rollup/plugin-json";
-import builtins from "builtin-modules";
-import pkg from "./package.json";
-import sourcemaps from 'rollup-plugin-sourcemaps';
-
-export default {
- input: "lib/index.js",
- output: {
- file: pkg.main,
- format: "cjs",
- sourcemap: true,
- },
- external: builtins,
- plugins: [
- nodeResolve({
- preferBuiltins: true,
- }),
-
- sourcemaps(),
-
- commonjs({
- sourceMap: true,
- transformMixedEsModules: true,
- }),
-
- json(),
- ],
-}
-
diff --git a/packages/taler-wallet-cli/src/assets.ts b/packages/taler-wallet-cli/src/assets.ts
deleted file mode 100644
index 72fc9fe04..000000000
--- a/packages/taler-wallet-cli/src/assets.ts
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- 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 path from "path";
-import fs from "fs";
-
-/**
- * Resolve an asset name into an absolute filename.
- *
- * The asset file should be placed in the "assets" directory
- * at the top level of the package (i.e. next to package.json).
- */
-export function resolveAsset(name: string): string {
- const n = __filename;
- const d = __dirname;
- let assetPath: string;
- // Currently both asset paths are the same.
- // This might change if the file that contains "resolveAsset"
- // ever moves. Thus, we're keeping the case distinction.
- if (n.endsWith("assets.js")) {
- // We're not bundled. Path is relative to the current file.
- assetPath = path.resolve(path.join(d, "..", "assets", name));
- } else if (n.endsWith("taler-wallet-cli.js")) {
- // We're bundled. Currently, this path is the same
- // FIXME: Take into account some ASSETS environment variable?
- assetPath = path.resolve(path.join(d, "..", "assets", name));
- } else {
- throw Error("Can't resolve asset (unknown)");
- }
- if (!fs.existsSync(assetPath)) {
- throw Error(`Asset '${name} not found'`);
- }
- return assetPath;
-}
diff --git a/packages/taler-wallet-cli/src/bench1.ts b/packages/taler-wallet-cli/src/bench1.ts
deleted file mode 100644
index 4a2651f36..000000000
--- a/packages/taler-wallet-cli/src/bench1.ts
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- 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 {
- buildCodecForObject,
- codecForNumber,
- codecForString,
- codecOptional,
-} from "@gnu-taler/taler-util";
-import {
- getDefaultNodeWallet,
- NodeHttpLib,
- WalletApiOperation,
-} from "@gnu-taler/taler-wallet-core";
-
-/**
- * Entry point for the benchmark.
- *
- * The benchmark runs against an existing Taler deployment and does not
- * set up its own services.
- */
-export async function runBench1(configJson: any): Promise<void> {
- // Validate the configuration file for this benchmark.
- const b1conf = codecForBench1Config().decode(configJson);
-
- const myHttpLib = new NodeHttpLib();
- const wallet = await getDefaultNodeWallet({
- // No persistent DB storage.
- persistentStoragePath: undefined,
- httpLib: myHttpLib,
- });
- await wallet.client.call(WalletApiOperation.InitWallet, {});
-
- const numIter = b1conf.iterations ?? 1;
-
- for (let i = 0; i < numIter; i++) {
- await wallet.client.call(WalletApiOperation.WithdrawFakebank, {
- amount: "TESTKUDOS:10",
- bank: b1conf.bank,
- exchange: b1conf.exchange,
- });
-
- await wallet.runTaskLoop({
- stopWhenDone: true,
- });
-
- await wallet.client.call(WalletApiOperation.CreateDepositGroup, {
- amount: "TESTKUDOS:5",
- depositPaytoUri: "payto://x-taler-bank/localhost/foo",
- });
-
- await wallet.runTaskLoop({
- stopWhenDone: true,
- });
- }
-
- wallet.stop();
-}
-
-/**
- * Format of the configuration file passed to the benchmark
- */
-interface Bench1Config {
- /**
- * Base URL of the bank.
- */
- bank: string;
-
- /**
- * Base URL of the exchange.
- */
- exchange: string;
-
- /**
- * How many withdraw/deposit iterations should be made?
- * Defaults to 1.
- */
- iterations?: number;
-}
-
-/**
- * Schema validation codec for Bench1Config.
- */
-const codecForBench1Config = () =>
- buildCodecForObject<Bench1Config>()
- .property("bank", codecForString())
- .property("exchange", codecForString())
- .property("iterations", codecOptional(codecForNumber()))
- .build("Bench1Config");
diff --git a/packages/taler-wallet-cli/src/clk.ts b/packages/taler-wallet-cli/src/clk.ts
deleted file mode 100644
index ca6dcc1a4..000000000
--- a/packages/taler-wallet-cli/src/clk.ts
+++ /dev/null
@@ -1,614 +0,0 @@
-/*
- This file is part of GNU Taler
- (C) 2019 GNUnet e.V.
-
- 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 process from "process";
-import path from "path";
-import readline from "readline";
-
-class Converter<T> {}
-
-export const INT = new Converter<number>();
-export const STRING: Converter<string> = new Converter<string>();
-
-export interface OptionArgs<T> {
- help?: string;
- default?: T;
- onPresentHandler?: (v: T) => void;
-}
-
-export interface ArgumentArgs<T> {
- metavar?: string;
- help?: string;
- default?: T;
-}
-
-export interface SubcommandArgs {
- help?: string;
-}
-
-export interface FlagArgs {
- help?: string;
-}
-
-export interface ProgramArgs {
- help?: string;
-}
-
-interface ArgumentDef {
- name: string;
- conv: Converter<any>;
- args: ArgumentArgs<any>;
- required: boolean;
-}
-
-interface SubcommandDef {
- commandGroup: CommandGroup<any, any>;
- name: string;
- args: SubcommandArgs;
-}
-
-type ActionFn<TG> = (x: TG) => void;
-
-type SubRecord<S extends keyof any, N extends keyof any, V> = {
- [Y in S]: { [X in N]: V };
-};
-
-interface OptionDef {
- name: string;
- flagspec: string[];
- /**
- * Converter, only present for options, not for flags.
- */
- conv?: Converter<any>;
- args: OptionArgs<any>;
- isFlag: boolean;
- required: boolean;
-}
-
-function splitOpt(opt: string): { key: string; value?: string } {
- const idx = opt.indexOf("=");
- if (idx == -1) {
- return { key: opt };
- }
- return { key: opt.substring(0, idx), value: opt.substring(idx + 1) };
-}
-
-function formatListing(key: string, value?: string): string {
- const res = " " + key;
- if (!value) {
- return res;
- }
- if (res.length >= 25) {
- return res + "\n" + " " + value;
- } else {
- return res.padEnd(24) + " " + value;
- }
-}
-
-export class CommandGroup<GN extends keyof any, TG> {
- private shortOptions: { [name: string]: OptionDef } = {};
- private longOptions: { [name: string]: OptionDef } = {};
- private subcommandMap: { [name: string]: SubcommandDef } = {};
- private subcommands: SubcommandDef[] = [];
- private options: OptionDef[] = [];
- private arguments: ArgumentDef[] = [];
-
- private myAction?: ActionFn<TG>;
-
- constructor(
- private argKey: string,
- private name: string | null,
- private scArgs: SubcommandArgs,
- ) {}
-
- action(f: ActionFn<TG>): void {
- if (this.myAction) {
- throw Error("only one action supported per command");
- }
- this.myAction = f;
- }
-
- requiredOption<N extends keyof any, V>(
- name: N,
- flagspec: string[],
- conv: Converter<V>,
- args: OptionArgs<V> = {},
- ): CommandGroup<GN, TG & SubRecord<GN, N, V>> {
- const def: OptionDef = {
- args: args,
- conv: conv,
- flagspec: flagspec,
- isFlag: false,
- required: true,
- name: name as string,
- };
- this.options.push(def);
- for (const flag of flagspec) {
- if (flag.startsWith("--")) {
- const flagname = flag.substring(2);
- this.longOptions[flagname] = def;
- } else if (flag.startsWith("-")) {
- const flagname = flag.substring(1);
- this.shortOptions[flagname] = def;
- } else {
- throw Error("option must start with '-' or '--'");
- }
- }
- return this as any;
- }
-
- maybeOption<N extends keyof any, V>(
- name: N,
- flagspec: string[],
- conv: Converter<V>,
- args: OptionArgs<V> = {},
- ): CommandGroup<GN, TG & SubRecord<GN, N, V | undefined>> {
- const def: OptionDef = {
- args: args,
- conv: conv,
- flagspec: flagspec,
- isFlag: false,
- required: false,
- name: name as string,
- };
- this.options.push(def);
- for (const flag of flagspec) {
- if (flag.startsWith("--")) {
- const flagname = flag.substring(2);
- this.longOptions[flagname] = def;
- } else if (flag.startsWith("-")) {
- const flagname = flag.substring(1);
- this.shortOptions[flagname] = def;
- } else {
- throw Error("option must start with '-' or '--'");
- }
- }
- return this as any;
- }
-
- requiredArgument<N extends keyof any, V>(
- name: N,
- conv: Converter<V>,
- args: ArgumentArgs<V> = {},
- ): CommandGroup<GN, TG & SubRecord<GN, N, V>> {
- const argDef: ArgumentDef = {
- args: args,
- conv: conv,
- name: name as string,
- required: true,
- };
- this.arguments.push(argDef);
- return this as any;
- }
-
- maybeArgument<N extends keyof any, V>(
- name: N,
- conv: Converter<V>,
- args: ArgumentArgs<V> = {},
- ): CommandGroup<GN, TG & SubRecord<GN, N, V | undefined>> {
- const argDef: ArgumentDef = {
- args: args,
- conv: conv,
- name: name as string,
- required: false,
- };
- this.arguments.push(argDef);
- return this as any;
- }
-
- flag<N extends string, V>(
- name: N,
- flagspec: string[],
- args: OptionArgs<V> = {},
- ): CommandGroup<GN, TG & SubRecord<GN, N, boolean>> {
- const def: OptionDef = {
- args: args,
- flagspec: flagspec,
- isFlag: true,
- required: false,
- name: name as string,
- };
- this.options.push(def);
- for (const flag of flagspec) {
- if (flag.startsWith("--")) {
- const flagname = flag.substring(2);
- this.longOptions[flagname] = def;
- } else if (flag.startsWith("-")) {
- const flagname = flag.substring(1);
- this.shortOptions[flagname] = def;
- } else {
- throw Error("option must start with '-' or '--'");
- }
- }
- return this as any;
- }
-
- subcommand<GN extends keyof any>(
- argKey: GN,
- name: string,
- args: SubcommandArgs = {},
- ): CommandGroup<GN, TG> {
- const cg = new CommandGroup<GN, {}>(argKey as string, name, args);
- const def: SubcommandDef = {
- commandGroup: cg,
- name: name as string,
- args: args,
- };
- cg.flag("help", ["-h", "--help"], {
- help: "Show this message and exit.",
- });
- this.subcommandMap[name as string] = def;
- this.subcommands.push(def);
- this.subcommands = this.subcommands.sort((x1, x2) => {
- const a = x1.name;
- const b = x2.name;
- if (a === b) {
- return 0;
- } else if (a < b) {
- return -1;
- } else {
- return 1;
- }
- });
- return cg as any;
- }
-
- printHelp(progName: string, parents: CommandGroup<any, any>[]): void {
- let usageSpec = "";
- for (const p of parents) {
- usageSpec += (p.name ?? progName) + " ";
- if (p.arguments.length >= 1) {
- usageSpec += "<ARGS...> ";
- }
- }
- usageSpec += (this.name ?? progName) + " ";
- if (this.subcommands.length != 0) {
- usageSpec += "COMMAND ";
- }
- for (const a of this.arguments) {
- const argName = a.args.metavar ?? a.name;
- usageSpec += `<${argName}> `;
- }
- usageSpec = usageSpec.trimRight();
- console.log(`Usage: ${usageSpec}`);
- if (this.scArgs.help) {
- console.log();
- console.log(this.scArgs.help);
- }
- if (this.options.length != 0) {
- console.log();
- console.log("Options:");
- for (const opt of this.options) {
- let optSpec = opt.flagspec.join(", ");
- if (!opt.isFlag) {
- optSpec = optSpec + "=VALUE";
- }
- console.log(formatListing(optSpec, opt.args.help));
- }
- }
-
- if (this.subcommands.length != 0) {
- console.log();
- console.log("Commands:");
- for (const subcmd of this.subcommands) {
- console.log(formatListing(subcmd.name, subcmd.args.help));
- }
- }
- }
-
- /**
- * Run the (sub-)command with the given command line parameters.
- */
- run(
- progname: string,
- parents: CommandGroup<any, any>[],
- unparsedArgs: string[],
- parsedArgs: any,
- ): void {
- let posArgIndex = 0;
- let argsTerminated = false;
- let i;
- let foundSubcommand: CommandGroup<any, any> | undefined = undefined;
- const myArgs: any = (parsedArgs[this.argKey] = {});
- const foundOptions: { [name: string]: boolean } = {};
- const currentName = this.name ?? progname;
- for (i = 0; i < unparsedArgs.length; i++) {
- const argVal = unparsedArgs[i];
- if (argsTerminated == false) {
- if (argVal === "--") {
- argsTerminated = true;
- continue;
- }
- if (argVal.startsWith("--")) {
- const opt = argVal.substring(2);
- const r = splitOpt(opt);
- const d = this.longOptions[r.key];
- if (!d) {
- console.error(
- `error: unknown option '--${r.key}' for ${currentName}`,
- );
- process.exit(-1);
- throw Error("not reached");
- }
- if (d.isFlag) {
- if (r.value !== undefined) {
- console.error(`error: flag '--${r.key}' does not take a value`);
- process.exit(-1);
- throw Error("not reached");
- }
- foundOptions[d.name] = true;
- myArgs[d.name] = true;
- } else {
- if (r.value === undefined) {
- if (i === unparsedArgs.length - 1) {
- console.error(`error: option '--${r.key}' needs an argument`);
- process.exit(-1);
- throw Error("not reached");
- }
- myArgs[d.name] = unparsedArgs[i + 1];
- i++;
- } else {
- myArgs[d.name] = r.value;
- }
- foundOptions[d.name] = true;
- }
- continue;
- }
- if (argVal.startsWith("-") && argVal != "-") {
- const optShort = argVal.substring(1);
- for (let si = 0; si < optShort.length; si++) {
- const chr = optShort[si];
- const opt = this.shortOptions[chr];
- if (!opt) {
- console.error(`error: option '-${chr}' not known`);
- process.exit(-1);
- }
- if (opt.isFlag) {
- myArgs[opt.name] = true;
- foundOptions[opt.name] = true;
- } else {
- if (si == optShort.length - 1) {
- if (i === unparsedArgs.length - 1) {
- console.error(`error: option '-${chr}' needs an argument`);
- process.exit(-1);
- throw Error("not reached");
- } else {
- myArgs[opt.name] = unparsedArgs[i + 1];
- i++;
- }
- } else {
- myArgs[opt.name] = optShort.substring(si + 1);
- }
- foundOptions[opt.name] = true;
- break;
- }
- }
- continue;
- }
- }
- if (this.subcommands.length != 0) {
- const subcmd = this.subcommandMap[argVal];
- if (!subcmd) {
- console.error(`error: unknown command '${argVal}'`);
- process.exit(-1);
- throw Error("not reached");
- }
- foundSubcommand = subcmd.commandGroup;
- break;
- } else {
- const d = this.arguments[posArgIndex];
- if (!d) {
- console.error(`error: too many arguments for ${currentName}`);
- process.exit(-1);
- throw Error("not reached");
- }
- myArgs[d.name] = unparsedArgs[i];
- posArgIndex++;
- }
- }
-
- if (parsedArgs[this.argKey].help) {
- this.printHelp(progname, parents);
- process.exit(0);
- throw Error("not reached");
- }
-
- for (let i = posArgIndex; i < this.arguments.length; i++) {
- const d = this.arguments[i];
- if (d.required) {
- if (d.args.default !== undefined) {
- myArgs[d.name] = d.args.default;
- } else {
- console.error(
- `error: missing positional argument '${d.name}' for ${currentName}`,
- );
- process.exit(-1);
- throw Error("not reached");
- }
- }
- }
-
- for (const option of this.options) {
- if (option.isFlag == false && option.required == true) {
- if (!foundOptions[option.name]) {
- if (option.args.default !== undefined) {
- myArgs[option.name] = option.args.default;
- } else {
- const name = option.flagspec.join(",");
- console.error(`error: missing option '${name}'`);
- process.exit(-1);
- throw Error("not reached");
- }
- }
- }
- }
-
- for (const option of this.options) {
- const ph = option.args.onPresentHandler;
- if (ph && foundOptions[option.name]) {
- ph(myArgs[option.name]);
- }
- }
-
- if (foundSubcommand) {
- foundSubcommand.run(
- progname,
- Array.prototype.concat(parents, [this]),
- unparsedArgs.slice(i + 1),
- parsedArgs,
- );
- } else if (this.myAction) {
- let r;
- try {
- r = this.myAction(parsedArgs);
- } catch (e) {
- console.error(`An error occurred while running ${currentName}`);
- console.error(e);
- process.exit(1);
- }
- Promise.resolve(r).catch((e) => {
- console.error(`An error occurred while running ${currentName}`);
- console.error(e);
- process.exit(1);
- });
- } else {
- this.printHelp(progname, parents);
- process.exit(-1);
- throw Error("not reached");
- }
- }
-}
-
-export class Program<PN extends keyof any, T> {
- private mainCommand: CommandGroup<any, any>;
-
- constructor(argKey: string, args: ProgramArgs = {}) {
- this.mainCommand = new CommandGroup<any, any>(argKey, null, {
- help: args.help,
- });
- this.mainCommand.flag("help", ["-h", "--help"], {
- help: "Show this message and exit.",
- });
- }
-
- run(): void {
- const args = process.argv;
- if (args.length < 2) {
- console.error(
- "Error while parsing command line arguments: not enough arguments",
- );
- process.exit(-1);
- }
- const progname = path.basename(args[1]);
- const rest = args.slice(2);
-
- this.mainCommand.run(progname, [], rest, {});
- }
-
- subcommand<GN extends keyof any>(
- argKey: GN,
- name: string,
- args: SubcommandArgs = {},
- ): CommandGroup<GN, T> {
- const cmd = this.mainCommand.subcommand(argKey, name as string, args);
- return cmd as any;
- }
-
- requiredOption<N extends keyof any, V>(
- name: N,
- flagspec: string[],
- conv: Converter<V>,
- args: OptionArgs<V> = {},
- ): Program<PN, T & SubRecord<PN, N, V>> {
- this.mainCommand.requiredOption(name, flagspec, conv, args);
- return this as any;
- }
-
- maybeOption<N extends keyof any, V>(
- name: N,
- flagspec: string[],
- conv: Converter<V>,
- args: OptionArgs<V> = {},
- ): Program<PN, T & SubRecord<PN, N, V | undefined>> {
- this.mainCommand.maybeOption(name, flagspec, conv, args);
- return this as any;
- }
-
- /**
- * Add a flag (option without value) to the program.
- */
- flag<N extends string>(
- name: N,
- flagspec: string[],
- args: OptionArgs<boolean> = {},
- ): Program<PN, T & SubRecord<PN, N, boolean>> {
- this.mainCommand.flag(name, flagspec, args);
- return this as any;
- }
-
- /**
- * Add a required positional argument to the program.
- */
- requiredArgument<N extends keyof any, V>(
- name: N,
- conv: Converter<V>,
- args: ArgumentArgs<V> = {},
- ): Program<N, T & SubRecord<PN, N, V>> {
- this.mainCommand.requiredArgument(name, conv, args);
- return this as any;
- }
-
- /**
- * Add an optional argument to the program.
- */
- maybeArgument<N extends keyof any, V>(
- name: N,
- conv: Converter<V>,
- args: ArgumentArgs<V> = {},
- ): Program<N, T & SubRecord<PN, N, V | undefined>> {
- this.mainCommand.maybeArgument(name, conv, args);
- return this as any;
- }
-}
-
-export type GetArgType<T> = T extends Program<any, infer AT>
- ? AT
- : T extends CommandGroup<any, infer AT>
- ? AT
- : any;
-
-export function program<PN extends keyof any>(
- argKey: PN,
- args: ProgramArgs = {},
-): Program<PN, {}> {
- return new Program(argKey as string, args);
-}
-
-export function prompt(question: string): Promise<string> {
- const stdinReadline = readline.createInterface({
- input: process.stdin,
- output: process.stdout,
- });
- return new Promise<string>((resolve, reject) => {
- stdinReadline.question(question, (res) => {
- resolve(res);
- stdinReadline.close();
- });
- });
-}
diff --git a/packages/taler-wallet-cli/src/env1.ts b/packages/taler-wallet-cli/src/env1.ts
deleted file mode 100644
index eb7da352c..000000000
--- a/packages/taler-wallet-cli/src/env1.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- 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 { URL } from "@gnu-taler/taler-util";
-import { CoinConfig, defaultCoinConfig } from "./harness/denomStructures.js";
-import {
- GlobalTestState,
- setupDb,
- FakeBankService,
- ExchangeService,
-} from "./harness/harness.js";
-
-/**
- * Entry point for the benchmark.
- *
- * The benchmark runs against an existing Taler deployment and does not
- * set up its own services.
- */
-export async function runEnv1(t: GlobalTestState): Promise<void> {
- const db = await setupDb(t);
-
- const bank = await FakeBankService.create(t, {
- currency: "TESTKUDOS",
- httpPort: 8082,
- });
-
- const exchange = ExchangeService.create(t, {
- name: "testexchange-1",
- currency: "TESTKUDOS",
- httpPort: 8081,
- database: db.connStr,
- });
-
- exchange.addBankAccount("1", {
- accountName: "exchange",
- accountPassword: "x",
- wireGatewayApiBaseUrl: new URL("/exchange/", bank.baseUrl).href,
- accountPaytoUri: "payto://x-taler-bank/localhost/exchange",
- });
-
- await bank.start();
-
- await bank.pingUntilAvailable();
-
- const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS"));
- exchange.addCoinConfigList(coinConfig);
-
- await exchange.start();
- await exchange.pingUntilAvailable();
-
- console.log("setup done!");
-}
diff --git a/packages/taler-wallet-cli/src/harness/denomStructures.ts b/packages/taler-wallet-cli/src/harness/denomStructures.ts
deleted file mode 100644
index 5ab9aca00..000000000
--- a/packages/taler-wallet-cli/src/harness/denomStructures.ts
+++ /dev/null
@@ -1,151 +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/>
- */
-
-export interface CoinConfig {
- name: string;
- value: string;
- durationWithdraw: string;
- durationSpend: string;
- durationLegal: string;
- feeWithdraw: string;
- feeDeposit: string;
- feeRefresh: string;
- feeRefund: string;
- rsaKeySize: number;
-}
-
-const coinCommon = {
- durationLegal: "3 years",
- durationSpend: "2 years",
- durationWithdraw: "7 days",
- rsaKeySize: 1024,
-};
-
-export const coin_ct1 = (curr: string): CoinConfig => ({
- ...coinCommon,
- name: `${curr}_ct1`,
- value: `${curr}:0.01`,
- feeDeposit: `${curr}:0.00`,
- feeRefresh: `${curr}:0.01`,
- feeRefund: `${curr}:0.00`,
- feeWithdraw: `${curr}:0.01`,
-});
-
-export const coin_ct10 = (curr: string): CoinConfig => ({
- ...coinCommon,
- name: `${curr}_ct10`,
- value: `${curr}:0.10`,
- feeDeposit: `${curr}:0.01`,
- feeRefresh: `${curr}:0.01`,
- feeRefund: `${curr}:0.00`,
- feeWithdraw: `${curr}:0.01`,
-});
-
-export const coin_u1 = (curr: string): CoinConfig => ({
- ...coinCommon,
- name: `${curr}_u1`,
- value: `${curr}:1`,
- feeDeposit: `${curr}:0.02`,
- feeRefresh: `${curr}:0.02`,
- feeRefund: `${curr}:0.02`,
- feeWithdraw: `${curr}:0.02`,
-});
-
-export const coin_u2 = (curr: string): CoinConfig => ({
- ...coinCommon,
- name: `${curr}_u2`,
- value: `${curr}:2`,
- feeDeposit: `${curr}:0.02`,
- feeRefresh: `${curr}:0.02`,
- feeRefund: `${curr}:0.02`,
- feeWithdraw: `${curr}:0.02`,
-});
-
-export const coin_u4 = (curr: string): CoinConfig => ({
- ...coinCommon,
- name: `${curr}_u4`,
- value: `${curr}:4`,
- feeDeposit: `${curr}:0.02`,
- feeRefresh: `${curr}:0.02`,
- feeRefund: `${curr}:0.02`,
- feeWithdraw: `${curr}:0.02`,
-});
-
-export const coin_u8 = (curr: string): CoinConfig => ({
- ...coinCommon,
- name: `${curr}_u8`,
- value: `${curr}:8`,
- feeDeposit: `${curr}:0.16`,
- feeRefresh: `${curr}:0.16`,
- feeRefund: `${curr}:0.16`,
- feeWithdraw: `${curr}:0.16`,
-});
-
-const coin_u10 = (curr: string): CoinConfig => ({
- ...coinCommon,
- name: `${curr}_u10`,
- value: `${curr}:10`,
- feeDeposit: `${curr}:0.2`,
- feeRefresh: `${curr}:0.2`,
- feeRefund: `${curr}:0.2`,
- feeWithdraw: `${curr}:0.2`,
-});
-
-export const defaultCoinConfig = [
- coin_ct1,
- coin_ct10,
- coin_u1,
- coin_u2,
- coin_u4,
- coin_u8,
- coin_u10,
-];
-
-const coinCheapCommon = (curr: string) => ({
- durationLegal: "3 years",
- durationSpend: "2 years",
- durationWithdraw: "7 days",
- rsaKeySize: 1024,
- feeRefresh: `${curr}:0.2`,
- feeRefund: `${curr}:0.2`,
- feeWithdraw: `${curr}:0.2`,
-});
-
-export function makeNoFeeCoinConfig(curr: string): CoinConfig[] {
- const cc: CoinConfig[] = [];
-
- for (let i = 0; i < 16; i++) {
- const ct = 2 ** i;
-
- const unit = Math.floor(ct / 100);
- const cent = ct % 100;
-
- cc.push({
- durationLegal: "3 years",
- durationSpend: "2 years",
- durationWithdraw: "7 days",
- rsaKeySize: 1024,
- name: `${curr}-u${i}`,
- feeDeposit: `${curr}:0`,
- feeRefresh: `${curr}:0`,
- feeRefund: `${curr}:0`,
- feeWithdraw: `${curr}:0`,
- value: `${curr}:${unit}.${cent}`,
- });
- }
-
- return cc;
-}
diff --git a/packages/taler-wallet-cli/src/harness/faultInjection.ts b/packages/taler-wallet-cli/src/harness/faultInjection.ts
deleted file mode 100644
index 4c3d0c123..000000000
--- a/packages/taler-wallet-cli/src/harness/faultInjection.ts
+++ /dev/null
@@ -1,256 +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/>
- */
-
-/**
- * Fault injection proxy.
- *
- * @author Florian Dold <dold@taler.net>
- */
-
-/**
- * Imports
- */
-import * as http from "http";
-import { URL } from "url";
-import {
- GlobalTestState,
- ExchangeService,
- ExchangeServiceInterface,
- MerchantServiceInterface,
- MerchantService,
-} from "../harness/harness.js";
-
-export interface FaultProxyConfig {
- inboundPort: number;
- targetPort: number;
-}
-
-/**
- * Fault injection context. Modified by fault injection functions.
- */
-export interface FaultInjectionRequestContext {
- requestUrl: string;
- method: string;
- requestHeaders: Record<string, string | string[] | undefined>;
- requestBody?: Buffer;
- dropRequest: boolean;
-}
-
-export interface FaultInjectionResponseContext {
- request: FaultInjectionRequestContext;
- statusCode: number;
- responseHeaders: Record<string, string | string[] | undefined>;
- responseBody: Buffer | undefined;
- dropResponse: boolean;
-}
-
-export interface FaultSpec {
- modifyRequest?: (ctx: FaultInjectionRequestContext) => Promise<void>;
- modifyResponse?: (ctx: FaultInjectionResponseContext) => Promise<void>;
-}
-
-export class FaultProxy {
- constructor(
- private globalTestState: GlobalTestState,
- private faultProxyConfig: FaultProxyConfig,
- ) {}
-
- private currentFaultSpecs: FaultSpec[] = [];
-
- start() {
- const server = http.createServer((req, res) => {
- const requestChunks: Buffer[] = [];
- const requestUrl = `http://localhost:${this.faultProxyConfig.inboundPort}${req.url}`;
- console.log("request for", new URL(requestUrl));
- req.on("data", (chunk) => {
- requestChunks.push(chunk);
- });
- req.on("end", async () => {
- console.log("end of data");
- let requestBuffer: Buffer | undefined;
- if (requestChunks.length > 0) {
- requestBuffer = Buffer.concat(requestChunks);
- }
- console.log("full request body", requestBuffer);
-
- const faultReqContext: FaultInjectionRequestContext = {
- dropRequest: false,
- method: req.method!!,
- requestHeaders: req.headers,
- requestUrl,
- requestBody: requestBuffer,
- };
-
- for (const faultSpec of this.currentFaultSpecs) {
- if (faultSpec.modifyRequest) {
- await faultSpec.modifyRequest(faultReqContext);
- }
- }
-
- if (faultReqContext.dropRequest) {
- res.destroy();
- return;
- }
-
- const faultedUrl = new URL(faultReqContext.requestUrl);
-
- const proxyRequest = http.request({
- method: faultReqContext.method,
- host: "localhost",
- port: this.faultProxyConfig.targetPort,
- path: faultedUrl.pathname + faultedUrl.search,
- headers: faultReqContext.requestHeaders,
- });
-
- console.log(
- `proxying request to target path '${
- faultedUrl.pathname + faultedUrl.search
- }'`,
- );
-
- if (faultReqContext.requestBody) {
- proxyRequest.write(faultReqContext.requestBody);
- }
- proxyRequest.end();
- proxyRequest.on("response", (proxyResp) => {
- console.log("gotten response from target", proxyResp.statusCode);
- const respChunks: Buffer[] = [];
- proxyResp.on("data", (proxyRespData) => {
- respChunks.push(proxyRespData);
- });
- proxyResp.on("end", async () => {
- console.log("end of target response");
- let responseBuffer: Buffer | undefined;
- if (respChunks.length > 0) {
- responseBuffer = Buffer.concat(respChunks);
- }
- const faultRespContext: FaultInjectionResponseContext = {
- request: faultReqContext,
- dropResponse: false,
- responseBody: responseBuffer,
- responseHeaders: proxyResp.headers,
- statusCode: proxyResp.statusCode!!,
- };
- for (const faultSpec of this.currentFaultSpecs) {
- const modResponse = faultSpec.modifyResponse;
- if (modResponse) {
- await modResponse(faultRespContext);
- }
- }
- if (faultRespContext.dropResponse) {
- req.destroy();
- return;
- }
- if (faultRespContext.responseBody) {
- // We must accommodate for potentially changed content length
- faultRespContext.responseHeaders[
- "content-length"
- ] = `${faultRespContext.responseBody.byteLength}`;
- }
- console.log("writing response head");
- res.writeHead(
- faultRespContext.statusCode,
- http.STATUS_CODES[faultRespContext.statusCode],
- faultRespContext.responseHeaders,
- );
- if (faultRespContext.responseBody) {
- res.write(faultRespContext.responseBody);
- }
- res.end();
- });
- });
- });
- });
-
- server.listen(this.faultProxyConfig.inboundPort);
- this.globalTestState.servers.push(server);
- }
-
- addFault(f: FaultSpec) {
- this.currentFaultSpecs.push(f);
- }
-
- clearAllFaults() {
- this.currentFaultSpecs = [];
- }
-}
-
-export class FaultInjectedExchangeService implements ExchangeServiceInterface {
- baseUrl: string;
- port: number;
- faultProxy: FaultProxy;
-
- get name(): string {
- return this.innerExchange.name;
- }
-
- get masterPub(): string {
- return this.innerExchange.masterPub;
- }
-
- private innerExchange: ExchangeService;
-
- constructor(
- t: GlobalTestState,
- e: ExchangeService,
- proxyInboundPort: number,
- ) {
- this.innerExchange = e;
- this.faultProxy = new FaultProxy(t, {
- inboundPort: proxyInboundPort,
- targetPort: e.port,
- });
- this.faultProxy.start();
-
- const exchangeUrl = new URL(e.baseUrl);
- exchangeUrl.port = `${proxyInboundPort}`;
- this.baseUrl = exchangeUrl.href;
- this.port = proxyInboundPort;
- }
-}
-
-export class FaultInjectedMerchantService implements MerchantServiceInterface {
- baseUrl: string;
- port: number;
- faultProxy: FaultProxy;
-
- get name(): string {
- return this.innerMerchant.name;
- }
-
- private innerMerchant: MerchantService;
- private inboundPort: number;
-
- constructor(
- t: GlobalTestState,
- m: MerchantService,
- proxyInboundPort: number,
- ) {
- this.innerMerchant = m;
- this.faultProxy = new FaultProxy(t, {
- inboundPort: proxyInboundPort,
- targetPort: m.port,
- });
- this.faultProxy.start();
- this.inboundPort = proxyInboundPort;
- }
-
- makeInstanceBaseUrl(instanceName?: string | undefined): string {
- const url = new URL(this.innerMerchant.makeInstanceBaseUrl(instanceName));
- url.port = `${this.inboundPort}`;
- return url.href;
- }
-}
diff --git a/packages/taler-wallet-cli/src/harness/harness.ts b/packages/taler-wallet-cli/src/harness/harness.ts
deleted file mode 100644
index b4ac16dbf..000000000
--- a/packages/taler-wallet-cli/src/harness/harness.ts
+++ /dev/null
@@ -1,1779 +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/>
- */
-
-/**
- * Test harness for various GNU Taler components.
- * Also provides a fault-injection proxy.
- *
- * @author Florian Dold <dold@taler.net>
- */
-
-/**
- * Imports
- */
-import * as util from "util";
-import * as fs from "fs";
-import * as path from "path";
-import * as http from "http";
-import * as readline from "readline";
-import { deepStrictEqual } from "assert";
-import { ChildProcess, spawn } from "child_process";
-import { URL } from "url";
-import axios, { AxiosError } from "axios";
-import {
- codecForMerchantOrderPrivateStatusResponse,
- codecForPostOrderResponse,
- PostOrderRequest,
- PostOrderResponse,
- MerchantOrderPrivateStatusResponse,
- TippingReserveStatus,
- TipCreateConfirmation,
- TipCreateRequest,
- MerchantInstancesResponse,
-} from "./merchantApiTypes";
-import {
- openPromise,
- OperationFailedError,
- WalletCoreApiClient,
-} from "@gnu-taler/taler-wallet-core";
-import {
- AmountJson,
- Amounts,
- Configuration,
- AmountString,
- Codec,
- buildCodecForObject,
- codecForString,
- Duration,
- parsePaytoUri,
- CoreApiResponse,
- createEddsaKeyPair,
- eddsaGetPublic,
- EddsaKeyPair,
- encodeCrock,
- getRandomBytes,
-} from "@gnu-taler/taler-util";
-import { CoinConfig } from "./denomStructures.js";
-
-const exec = util.promisify(require("child_process").exec);
-
-export async function delayMs(ms: number): Promise<void> {
- return new Promise((resolve, reject) => {
- setTimeout(() => resolve(), ms);
- });
-}
-
-export interface WithAuthorization {
- Authorization?: string;
-}
-
-interface WaitResult {
- code: number | null;
- signal: NodeJS.Signals | null;
-}
-
-/**
- * Run a shell command, return stdout.
- */
-export async function sh(
- t: GlobalTestState,
- logName: string,
- command: string,
- env: { [index: string]: string | undefined } = process.env,
-): Promise<string> {
- console.log("running command", command);
- return new Promise((resolve, reject) => {
- const stdoutChunks: Buffer[] = [];
- const proc = spawn(command, {
- stdio: ["inherit", "pipe", "pipe"],
- shell: true,
- env: env,
- });
- proc.stdout.on("data", (x) => {
- if (x instanceof Buffer) {
- stdoutChunks.push(x);
- } else {
- throw Error("unexpected data chunk type");
- }
- });
- const stderrLogFileName = path.join(t.testDir, `${logName}-stderr.log`);
- const stderrLog = fs.createWriteStream(stderrLogFileName, {
- flags: "a",
- });
- proc.stderr.pipe(stderrLog);
- proc.on("exit", (code, signal) => {
- console.log(`child process exited (${code} / ${signal})`);
- if (code != 0) {
- reject(Error(`Unexpected exit code ${code} for '${command}'`));
- return;
- }
- const b = Buffer.concat(stdoutChunks).toString("utf-8");
- resolve(b);
- });
- proc.on("error", () => {
- reject(Error("Child process had error"));
- });
- });
-}
-
-function shellescape(args: string[]) {
- const ret = args.map((s) => {
- if (/[^A-Za-z0-9_\/:=-]/.test(s)) {
- s = "'" + s.replace(/'/g, "'\\''") + "'";
- s = s.replace(/^(?:'')+/g, "").replace(/\\'''/g, "\\'");
- }
- return s;
- });
- return ret.join(" ");
-}
-
-/**
- * Run a shell command, return stdout.
- *
- * Log stderr to a log file.
- */
-export async function runCommand(
- t: GlobalTestState,
- logName: string,
- command: string,
- args: string[],
- env: { [index: string]: string | undefined } = process.env,
-): Promise<string> {
- console.log("running command", shellescape([command, ...args]));
- return new Promise((resolve, reject) => {
- const stdoutChunks: Buffer[] = [];
- const proc = spawn(command, args, {
- stdio: ["inherit", "pipe", "pipe"],
- shell: false,
- env: env,
- });
- proc.stdout.on("data", (x) => {
- if (x instanceof Buffer) {
- stdoutChunks.push(x);
- } else {
- throw Error("unexpected data chunk type");
- }
- });
- const stderrLogFileName = path.join(t.testDir, `${logName}-stderr.log`);
- const stderrLog = fs.createWriteStream(stderrLogFileName, {
- flags: "a",
- });
- proc.stderr.pipe(stderrLog);
- proc.on("exit", (code, signal) => {
- console.log(`child process exited (${code} / ${signal})`);
- if (code != 0) {
- reject(Error(`Unexpected exit code ${code} for '${command}'`));
- return;
- }
- const b = Buffer.concat(stdoutChunks).toString("utf-8");
- resolve(b);
- });
- proc.on("error", () => {
- reject(Error("Child process had error"));
- });
- });
-}
-
-export class ProcessWrapper {
- private waitPromise: Promise<WaitResult>;
- constructor(public proc: ChildProcess) {
- this.waitPromise = new Promise((resolve, reject) => {
- proc.on("exit", (code, signal) => {
- resolve({ code, signal });
- });
- proc.on("error", (err) => {
- reject(err);
- });
- });
- }
-
- wait(): Promise<WaitResult> {
- return this.waitPromise;
- }
-}
-
-export class GlobalTestParams {
- testDir: string;
-}
-
-export class GlobalTestState {
- testDir: string;
- procs: ProcessWrapper[];
- servers: http.Server[];
- inShutdown: boolean = false;
- constructor(params: GlobalTestParams) {
- this.testDir = params.testDir;
- this.procs = [];
- this.servers = [];
- }
-
- async assertThrowsOperationErrorAsync(
- block: () => Promise<void>,
- ): Promise<OperationFailedError> {
- try {
- await block();
- } catch (e) {
- if (e instanceof OperationFailedError) {
- return e;
- }
- throw Error(`expected OperationFailedError to be thrown, but got ${e}`);
- }
- throw Error(
- `expected OperationFailedError to be thrown, but block finished without throwing`,
- );
- }
-
- 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 {
- if (!e.isAxiosError) {
- throw Error("expected axios error");
- }
- }
-
- assertTrue(b: boolean): asserts b {
- if (!b) {
- throw Error("test assertion failed");
- }
- }
-
- assertDeepEqual<T>(actual: any, expected: T): asserts actual is T {
- deepStrictEqual(actual, expected);
- }
-
- assertAmountEquals(
- amtActual: string | AmountJson,
- amtExpected: string | AmountJson,
- ): void {
- if (Amounts.cmp(amtActual, amtExpected) != 0) {
- throw Error(
- `test assertion failed: expected ${Amounts.stringify(
- amtExpected,
- )} but got ${Amounts.stringify(amtActual)}`,
- );
- }
- }
-
- assertAmountLeq(a: string | AmountJson, b: string | AmountJson): void {
- if (Amounts.cmp(a, b) > 0) {
- throw Error(
- `test assertion failed: expected ${Amounts.stringify(
- a,
- )} to be less or equal (leq) than ${Amounts.stringify(b)}`,
- );
- }
- }
-
- shutdownSync(): void {
- for (const s of this.servers) {
- s.close();
- s.removeAllListeners();
- }
- for (const p of this.procs) {
- if (p.proc.exitCode == null) {
- p.proc.kill("SIGTERM");
- }
- }
- }
-
- spawnService(
- command: string,
- args: string[],
- logName: string,
- env: { [index: string]: string | undefined } = process.env,
- ): ProcessWrapper {
- console.log(
- `spawning process (${logName}): ${shellescape([command, ...args])}`,
- );
- const proc = spawn(command, args, {
- stdio: ["inherit", "pipe", "pipe"],
- env: env,
- });
- console.log(`spawned process (${logName}) with pid ${proc.pid}`);
- proc.on("error", (err) => {
- console.log(`could not start process (${command})`, err);
- });
- proc.on("exit", (code, signal) => {
- console.log(`process ${logName} exited`);
- });
- const stderrLogFileName = this.testDir + `/${logName}-stderr.log`;
- const stderrLog = fs.createWriteStream(stderrLogFileName, {
- flags: "a",
- });
- proc.stderr.pipe(stderrLog);
- const stdoutLogFileName = this.testDir + `/${logName}-stdout.log`;
- const stdoutLog = fs.createWriteStream(stdoutLogFileName, {
- flags: "a",
- });
- proc.stdout.pipe(stdoutLog);
- const procWrap = new ProcessWrapper(proc);
- this.procs.push(procWrap);
- return procWrap;
- }
-
- async shutdown(): Promise<void> {
- if (this.inShutdown) {
- return;
- }
- if (shouldLingerInTest()) {
- console.log("refusing to shut down, lingering was requested");
- return;
- }
- this.inShutdown = true;
- console.log("shutting down");
- for (const s of this.servers) {
- s.close();
- s.removeAllListeners();
- }
- for (const p of this.procs) {
- if (p.proc.exitCode == null) {
- console.log("killing process", p.proc.pid);
- p.proc.kill("SIGTERM");
- await p.wait();
- }
- }
- }
-}
-
-export function shouldLingerInTest(): boolean {
- return !!process.env["TALER_TEST_LINGER"];
-}
-
-export interface TalerConfigSection {
- options: Record<string, string | undefined>;
-}
-
-export interface TalerConfig {
- sections: Record<string, TalerConfigSection>;
-}
-
-export interface DbInfo {
- /**
- * Postgres connection string.
- */
- connStr: string;
-
- dbname: string;
-}
-
-export async function setupDb(gc: GlobalTestState): Promise<DbInfo> {
- const dbname = "taler-integrationtest";
- await exec(`dropdb "${dbname}" || true`);
- await exec(`createdb "${dbname}"`);
- return {
- connStr: `postgres:///${dbname}`,
- dbname,
- };
-}
-
-export interface BankConfig {
- currency: string;
- httpPort: number;
- database: string;
- allowRegistrations: boolean;
- maxDebt?: string;
-}
-
-export interface FakeBankConfig {
- currency: string;
- httpPort: number;
-}
-
-function setTalerPaths(config: Configuration, home: string) {
- config.setString("paths", "taler_home", home);
- // We need to make sure that the path of taler_runtime_dir isn't too long,
- // as it contains unix domain sockets (108 character limit).
- const runDir = fs.mkdtempSync("/tmp/taler-test-");
- config.setString("paths", "taler_runtime_dir", runDir);
- config.setString(
- "paths",
- "taler_data_home",
- "$TALER_HOME/.local/share/taler/",
- );
- config.setString("paths", "taler_config_home", "$TALER_HOME/.config/taler/");
- config.setString("paths", "taler_cache_home", "$TALER_HOME/.config/taler/");
-}
-
-function setCoin(config: Configuration, c: CoinConfig) {
- const s = `coin_${c.name}`;
- config.setString(s, "value", c.value);
- config.setString(s, "duration_withdraw", c.durationWithdraw);
- config.setString(s, "duration_spend", c.durationSpend);
- config.setString(s, "duration_legal", c.durationLegal);
- config.setString(s, "fee_deposit", c.feeDeposit);
- config.setString(s, "fee_withdraw", c.feeWithdraw);
- config.setString(s, "fee_refresh", c.feeRefresh);
- config.setString(s, "fee_refund", c.feeRefund);
- config.setString(s, "rsa_keysize", `${c.rsaKeySize}`);
-}
-
-/**
- * Send an HTTP request until it succeeds or the
- * process dies.
- */
-export async function pingProc(
- proc: ProcessWrapper | undefined,
- url: string,
- serviceName: string,
-): Promise<void> {
- if (!proc || proc.proc.exitCode !== null) {
- throw Error(`service process ${serviceName} not started, can't ping`);
- }
- while (true) {
- try {
- console.log(`pinging ${serviceName}`);
- const resp = await axios.get(url);
- console.log(`service ${serviceName} available`);
- return;
- } catch (e: any) {
- console.log(`service ${serviceName} not ready:`, e.toString());
- await delayMs(1000);
- }
- if (!proc || proc.proc.exitCode !== null) {
- throw Error(`service process ${serviceName} stopped unexpectedly`);
- }
- }
-}
-
-export interface HarnessExchangeBankAccount {
- accountName: string;
- accountPassword: string;
- accountPaytoUri: string;
- wireGatewayApiBaseUrl: string;
-}
-
-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}`, 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: HarnessExchangeBankAccount;
- 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 async function abortWithdrawalOperation(
- bank: BankServiceInterface,
- bankUser: BankUser,
- wopi: WithdrawalOperationInfo,
- ): Promise<void> {
- const url = new URL(
- `accounts/${bankUser.username}/withdrawals/${wopi.withdrawal_id}/abort`,
- bank.baseUrl,
- );
- await axios.post(
- url.href,
- {},
- {
- auth: bankUser,
- },
- );
- }
-}
-
-export class BankService implements BankServiceInterface {
- proc: ProcessWrapper | undefined;
-
- static fromExistingConfig(gc: GlobalTestState): BankService {
- const cfgFilename = gc.testDir + "/bank.conf";
- console.log("reading bank config from", cfgFilename);
- const config = Configuration.load(cfgFilename);
- const bc: BankConfig = {
- allowRegistrations: config
- .getYesNo("bank", "allow_registrations")
- .required(),
- currency: config.getString("taler", "currency").required(),
- database: config.getString("bank", "database").required(),
- httpPort: config.getNumber("bank", "http_port").required(),
- };
- return new BankService(gc, bc, cfgFilename);
- }
-
- static async create(
- gc: GlobalTestState,
- bc: BankConfig,
- ): Promise<BankService> {
- const config = new Configuration();
- setTalerPaths(config, gc.testDir + "/talerhome");
- config.setString("taler", "currency", bc.currency);
- config.setString("bank", "database", bc.database);
- config.setString("bank", "http_port", `${bc.httpPort}`);
- config.setString("bank", "serve", "http");
- config.setString("bank", "max_debt_bank", `${bc.currency}:999999`);
- config.setString("bank", "max_debt", bc.maxDebt ?? `${bc.currency}:100`);
- config.setString(
- "bank",
- "allow_registrations",
- bc.allowRegistrations ? "yes" : "no",
- );
- const cfgFilename = gc.testDir + "/bank.conf";
- config.write(cfgFilename);
-
- await sh(
- gc,
- "taler-bank-manage_django",
- `taler-bank-manage -c '${cfgFilename}' django migrate`,
- );
- await sh(
- gc,
- "taler-bank-manage_django",
- `taler-bank-manage -c '${cfgFilename}' django provide_accounts`,
- );
-
- return new BankService(gc, bc, cfgFilename);
- }
-
- setSuggestedExchange(e: ExchangeServiceInterface, exchangePayto: string) {
- const config = Configuration.load(this.configFile);
- config.setString("bank", "suggested_exchange", e.baseUrl);
- config.setString("bank", "suggested_exchange_payto", exchangePayto);
- }
-
- get baseUrl(): string {
- return `http://localhost:${this.bankConfig.httpPort}/`;
- }
-
- async createExchangeAccount(
- accountName: string,
- password: string,
- ): Promise<HarnessExchangeBankAccount> {
- await sh(
- this.globalTestState,
- "taler-bank-manage_django",
- `taler-bank-manage -c '${this.configFile}' django add_bank_account ${accountName}`,
- );
- await sh(
- this.globalTestState,
- "taler-bank-manage_django",
- `taler-bank-manage -c '${this.configFile}' django changepassword_unsafe ${accountName} ${password}`,
- );
- await sh(
- this.globalTestState,
- "taler-bank-manage_django",
- `taler-bank-manage -c '${this.configFile}' django top_up ${accountName} ${this.bankConfig.currency}:100000`,
- );
- return {
- accountName: accountName,
- accountPassword: password,
- accountPaytoUri: `payto://x-taler-bank/${accountName}`,
- wireGatewayApiBaseUrl: `http://localhost:${this.bankConfig.httpPort}/taler-wire-gateway/${accountName}/`,
- };
- }
-
- get port() {
- return this.bankConfig.httpPort;
- }
-
- private constructor(
- private globalTestState: GlobalTestState,
- private bankConfig: BankConfig,
- private configFile: string,
- ) {}
-
- async start(): Promise<void> {
- this.proc = this.globalTestState.spawnService(
- "taler-bank-manage",
- ["-c", this.configFile, "serve"],
- "bank",
- );
- }
-
- async pingUntilAvailable(): Promise<void> {
- const url = `http://localhost:${this.bankConfig.httpPort}/config`;
- await pingProc(this.proc, url, "bank");
- }
-}
-
-export class FakeBankService {
- proc: ProcessWrapper | undefined;
-
- static fromExistingConfig(gc: GlobalTestState): FakeBankService {
- const cfgFilename = gc.testDir + "/bank.conf";
- console.log("reading fakebank config from", cfgFilename);
- const config = Configuration.load(cfgFilename);
- const bc: FakeBankConfig = {
- currency: config.getString("taler", "currency").required(),
- httpPort: config.getNumber("bank", "http_port").required(),
- };
- return new FakeBankService(gc, bc, cfgFilename);
- }
-
- static async create(
- gc: GlobalTestState,
- bc: FakeBankConfig,
- ): Promise<FakeBankService> {
- const config = new Configuration();
- setTalerPaths(config, gc.testDir + "/talerhome");
- config.setString("taler", "currency", bc.currency);
- config.setString("bank", "http_port", `${bc.httpPort}`);
- const cfgFilename = gc.testDir + "/bank.conf";
- config.write(cfgFilename);
- return new FakeBankService(gc, bc, cfgFilename);
- }
-
- get baseUrl(): string {
- return `http://localhost:${this.bankConfig.httpPort}/`;
- }
-
- get port() {
- return this.bankConfig.httpPort;
- }
-
- private constructor(
- private globalTestState: GlobalTestState,
- private bankConfig: FakeBankConfig,
- private configFile: string,
- ) {}
-
- async start(): Promise<void> {
- this.proc = this.globalTestState.spawnService(
- "taler-fakebank-run",
- ["-c", this.configFile],
- "fakebank",
- );
- }
-
- async pingUntilAvailable(): Promise<void> {
- // Fakebank doesn't have "/config", so we ping just "/".
- const url = `http://localhost:${this.bankConfig.httpPort}/`;
- await pingProc(this.proc, url, "bank");
- }
-}
-
-export interface BankUser {
- username: string;
- password: string;
- accountPaytoUri: string;
-}
-
-export interface WithdrawalOperationInfo {
- withdrawal_id: string;
- taler_withdraw_uri: string;
-}
-
-const codecForWithdrawalOperationInfo = (): Codec<WithdrawalOperationInfo> =>
- buildCodecForObject<WithdrawalOperationInfo>()
- .property("withdrawal_id", codecForString())
- .property("taler_withdraw_uri", codecForString())
- .build("WithdrawalOperationInfo");
-
-export interface ExchangeConfig {
- name: string;
- currency: string;
- roundUnit?: string;
- httpPort: number;
- database: string;
-}
-
-export interface ExchangeServiceInterface {
- readonly baseUrl: string;
- readonly port: number;
- readonly name: string;
- readonly masterPub: string;
-}
-
-export class ExchangeService implements ExchangeServiceInterface {
- static fromExistingConfig(gc: GlobalTestState, exchangeName: string) {
- const cfgFilename = gc.testDir + `/exchange-${exchangeName}.conf`;
- const config = Configuration.load(cfgFilename);
- const ec: ExchangeConfig = {
- currency: config.getString("taler", "currency").required(),
- database: config.getString("exchangedb-postgres", "config").required(),
- httpPort: config.getNumber("exchange", "port").required(),
- name: exchangeName,
- roundUnit: config.getString("taler", "currency_round_unit").required(),
- };
- const privFile = config.getPath("exchange", "master_priv_file").required();
- const eddsaPriv = fs.readFileSync(privFile);
- const keyPair: EddsaKeyPair = {
- eddsaPriv,
- eddsaPub: eddsaGetPublic(eddsaPriv),
- };
- return new ExchangeService(gc, ec, cfgFilename, keyPair);
- }
-
- private currentTimetravel: Duration | undefined;
-
- setTimetravel(t: Duration | undefined): void {
- if (this.isRunning()) {
- throw Error("can't set time travel while the exchange is running");
- }
- this.currentTimetravel = t;
- }
-
- private get timetravelArg(): string | undefined {
- if (this.currentTimetravel && this.currentTimetravel.d_ms !== "forever") {
- // Convert to microseconds
- return `--timetravel=+${this.currentTimetravel.d_ms * 1000}`;
- }
- return undefined;
- }
-
- /**
- * Return an empty array if no time travel is set,
- * and an array with the time travel command line argument
- * otherwise.
- */
- private get timetravelArgArr(): string[] {
- const tta = this.timetravelArg;
- if (tta) {
- return [tta];
- }
- return [];
- }
-
- async runWirewatchOnce() {
- await runCommand(
- this.globalState,
- `exchange-${this.name}-wirewatch-once`,
- "taler-exchange-wirewatch",
- [...this.timetravelArgArr, "-c", this.configFilename, "-t"],
- );
- }
-
- async runAggregatorOnce() {
- await runCommand(
- this.globalState,
- `exchange-${this.name}-aggregator-once`,
- "taler-exchange-aggregator",
- [...this.timetravelArgArr, "-c", this.configFilename, "-t"],
- );
- }
-
- 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) {
- const config = Configuration.load(this.configFilename);
- f(config);
- config.write(this.configFilename);
- }
-
- static create(gc: GlobalTestState, e: ExchangeConfig) {
- const config = new Configuration();
- config.setString("taler", "currency", e.currency);
- config.setString(
- "taler",
- "currency_round_unit",
- e.roundUnit ?? `${e.currency}:0.01`,
- );
- setTalerPaths(config, gc.testDir + "/talerhome");
- config.setString(
- "exchange",
- "revocation_dir",
- "${TALER_DATA_HOME}/exchange/revocations",
- );
- config.setString("exchange", "max_keys_caching", "forever");
- config.setString("exchange", "db", "postgres");
- config.setString(
- "exchange-offline",
- "master_priv_file",
- "${TALER_DATA_HOME}/exchange/offline-keys/master.priv",
- );
- config.setString("exchange", "serve", "tcp");
- config.setString("exchange", "port", `${e.httpPort}`);
-
- config.setString("exchangedb-postgres", "config", e.database);
-
- config.setString("taler-exchange-secmod-eddsa", "lookahead_sign", "20 s");
- config.setString("taler-exchange-secmod-rsa", "lookahead_sign", "20 s");
-
- const exchangeMasterKey = createEddsaKeyPair();
-
- config.setString(
- "exchange",
- "master_public_key",
- encodeCrock(exchangeMasterKey.eddsaPub),
- );
-
- const masterPrivFile = config
- .getPath("exchange-offline", "master_priv_file")
- .required();
-
- fs.mkdirSync(path.dirname(masterPrivFile), { recursive: true });
-
- fs.writeFileSync(masterPrivFile, Buffer.from(exchangeMasterKey.eddsaPriv));
-
- const cfgFilename = gc.testDir + `/exchange-${e.name}.conf`;
- config.write(cfgFilename);
- return new ExchangeService(gc, e, cfgFilename, exchangeMasterKey);
- }
-
- addOfferedCoins(offeredCoins: ((curr: string) => CoinConfig)[]) {
- const config = Configuration.load(this.configFilename);
- offeredCoins.forEach((cc) =>
- setCoin(config, cc(this.exchangeConfig.currency)),
- );
- config.write(this.configFilename);
- }
-
- addCoinConfigList(ccs: CoinConfig[]) {
- const config = Configuration.load(this.configFilename);
- ccs.forEach((cc) => setCoin(config, cc));
- config.write(this.configFilename);
- }
-
- get masterPub() {
- return encodeCrock(this.keyPair.eddsaPub);
- }
-
- get port() {
- return this.exchangeConfig.httpPort;
- }
-
- async addBankAccount(
- localName: string,
- exchangeBankAccount: HarnessExchangeBankAccount,
- ): Promise<void> {
- const config = Configuration.load(this.configFilename);
- config.setString(
- `exchange-account-${localName}`,
- "wire_response",
- `\${TALER_DATA_HOME}/exchange/account-${localName}.json`,
- );
- config.setString(
- `exchange-account-${localName}`,
- "payto_uri",
- exchangeBankAccount.accountPaytoUri,
- );
- config.setString(`exchange-account-${localName}`, "enable_credit", "yes");
- config.setString(`exchange-account-${localName}`, "enable_debit", "yes");
- config.setString(
- `exchange-accountcredentials-${localName}`,
- "wire_gateway_url",
- exchangeBankAccount.wireGatewayApiBaseUrl,
- );
- config.setString(
- `exchange-accountcredentials-${localName}`,
- "wire_gateway_auth_method",
- "basic",
- );
- config.setString(
- `exchange-accountcredentials-${localName}`,
- "username",
- exchangeBankAccount.accountName,
- );
- config.setString(
- `exchange-accountcredentials-${localName}`,
- "password",
- exchangeBankAccount.accountPassword,
- );
- config.write(this.configFilename);
- }
-
- exchangeHttpProc: ProcessWrapper | undefined;
- exchangeWirewatchProc: ProcessWrapper | undefined;
-
- helperCryptoRsaProc: ProcessWrapper | undefined;
- helperCryptoEddsaProc: ProcessWrapper | undefined;
-
- constructor(
- private globalState: GlobalTestState,
- private exchangeConfig: ExchangeConfig,
- private configFilename: string,
- private keyPair: EddsaKeyPair,
- ) {}
-
- get name() {
- return this.exchangeConfig.name;
- }
-
- get baseUrl() {
- return `http://localhost:${this.exchangeConfig.httpPort}/`;
- }
-
- isRunning(): boolean {
- return !!this.exchangeWirewatchProc || !!this.exchangeHttpProc;
- }
-
- async stop(): Promise<void> {
- const wirewatch = this.exchangeWirewatchProc;
- if (wirewatch) {
- wirewatch.proc.kill("SIGTERM");
- await wirewatch.wait();
- this.exchangeWirewatchProc = undefined;
- }
- const httpd = this.exchangeHttpProc;
- if (httpd) {
- httpd.proc.kill("SIGTERM");
- await httpd.wait();
- this.exchangeHttpProc = undefined;
- }
- const cryptoRsa = this.helperCryptoRsaProc;
- if (cryptoRsa) {
- cryptoRsa.proc.kill("SIGTERM");
- await cryptoRsa.wait();
- this.helperCryptoRsaProc = undefined;
- }
- const cryptoEddsa = this.helperCryptoEddsaProc;
- if (cryptoEddsa) {
- cryptoEddsa.proc.kill("SIGTERM");
- await cryptoEddsa.wait();
- this.helperCryptoRsaProc = undefined;
- }
- }
-
- /**
- * Update keys signing the keys generated by the security module
- * with the offline signing key.
- */
- async keyup(): Promise<void> {
- await runCommand(
- this.globalState,
- "exchange-offline",
- "taler-exchange-offline",
- ["-c", this.configFilename, "download", "sign", "upload"],
- );
-
- const accounts: string[] = [];
- const accountTargetTypes: Set<string> = new Set();
-
- const config = Configuration.load(this.configFilename);
- for (const sectionName of config.getSectionNames()) {
- if (sectionName.startsWith("EXCHANGE-ACCOUNT-")) {
- 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);
- }
- }
-
- console.log("configuring bank accounts", accounts);
-
- for (const acc of accounts) {
- await runCommand(
- this.globalState,
- "exchange-offline",
- "taler-exchange-offline",
- ["-c", this.configFilename, "enable-account", acc, "upload"],
- );
- }
-
- const year = new Date().getFullYear();
- for (const accTargetType of accountTargetTypes.values()) {
- for (let i = year; i < year + 5; i++) {
- await runCommand(
- this.globalState,
- "exchange-offline",
- "taler-exchange-offline",
- [
- "-c",
- this.configFilename,
- "wire-fee",
- `${i}`,
- accTargetType,
- `${this.exchangeConfig.currency}:0.01`,
- `${this.exchangeConfig.currency}:0.01`,
- "upload",
- ],
- );
- }
- }
- }
-
- async revokeDenomination(denomPubHash: string) {
- if (!this.isRunning()) {
- throw Error("exchange must be running when revoking denominations");
- }
- await runCommand(
- this.globalState,
- "exchange-offline",
- "taler-exchange-offline",
- [
- "-c",
- this.configFilename,
- "revoke-denomination",
- denomPubHash,
- "upload",
- ],
- );
- }
-
- async purgeSecmodKeys(): Promise<void> {
- const cfg = Configuration.load(this.configFilename);
- const rsaKeydir = cfg
- .getPath("taler-exchange-secmod-rsa", "KEY_DIR")
- .required();
- const eddsaKeydir = cfg
- .getPath("taler-exchange-secmod-eddsa", "KEY_DIR")
- .required();
- // Be *VERY* careful when changing this, or you will accidentally delete user data.
- await sh(this.globalState, "rm-secmod-keys", `rm -rf ${rsaKeydir}/COIN_*`);
- await sh(this.globalState, "rm-secmod-keys", `rm ${eddsaKeydir}/*`);
- }
-
- async purgeDatabase(): Promise<void> {
- await sh(
- this.globalState,
- "exchange-dbinit",
- `taler-exchange-dbinit -r -c "${this.configFilename}"`,
- );
- }
-
- async start(): Promise<void> {
- if (this.isRunning()) {
- throw Error("exchange is already running");
- }
- await sh(
- this.globalState,
- "exchange-dbinit",
- `taler-exchange-dbinit -c "${this.configFilename}"`,
- );
-
- this.helperCryptoEddsaProc = this.globalState.spawnService(
- "taler-exchange-secmod-eddsa",
- ["-c", this.configFilename, "-LDEBUG", ...this.timetravelArgArr],
- `exchange-crypto-eddsa-${this.name}`,
- );
-
- this.helperCryptoRsaProc = this.globalState.spawnService(
- "taler-exchange-secmod-rsa",
- ["-c", this.configFilename, "-LDEBUG", ...this.timetravelArgArr],
- `exchange-crypto-rsa-${this.name}`,
- );
-
- this.exchangeWirewatchProc = this.globalState.spawnService(
- "taler-exchange-wirewatch",
- ["-c", this.configFilename, ...this.timetravelArgArr],
- `exchange-wirewatch-${this.name}`,
- );
-
- this.exchangeHttpProc = this.globalState.spawnService(
- "taler-exchange-httpd",
- ["-c", this.configFilename, ...this.timetravelArgArr],
- `exchange-httpd-${this.name}`,
- );
-
- await this.pingUntilAvailable();
- await this.keyup();
- }
-
- async pingUntilAvailable(): Promise<void> {
- // We request /management/keys, since /keys can block
- // when we didn't do the key setup yet.
- const url = `http://localhost:${this.exchangeConfig.httpPort}/management/keys`;
- await pingProc(this.exchangeHttpProc, url, `exchange (${this.name})`);
- }
-}
-
-export interface MerchantConfig {
- name: string;
- currency: string;
- httpPort: number;
- database: string;
-}
-
-export interface PrivateOrderStatusQuery {
- instance?: string;
- orderId: string;
- sessionId?: string;
-}
-
-export interface MerchantServiceInterface {
- makeInstanceBaseUrl(instanceName?: string): string;
- readonly port: number;
- readonly name: string;
-}
-
-export class MerchantApiClient {
- constructor(
- private baseUrl: string,
- public readonly auth: MerchantAuthConfiguration,
- ) {}
-
- async changeAuth(auth: MerchantAuthConfiguration): Promise<void> {
- const url = new URL("private/auth", this.baseUrl);
- await axios.post(url.href, auth, {
- headers: this.makeAuthHeader(),
- });
- }
-
- async deleteInstance(instanceId: string) {
- const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
- await axios.delete(url.href, {
- headers: this.makeAuthHeader(),
- });
- }
-
- async createInstance(req: MerchantInstanceConfig): Promise<void> {
- const url = new URL("management/instances", this.baseUrl);
- await axios.post(url.href, req, {
- headers: this.makeAuthHeader(),
- });
- }
-
- async getInstances(): Promise<MerchantInstancesResponse> {
- const url = new URL("management/instances", this.baseUrl);
- const resp = await axios.get(url.href, {
- headers: this.makeAuthHeader(),
- });
- return resp.data;
- }
-
- async getInstanceFullDetails(instanceId: string): Promise<any> {
- const url = new URL(`management/instances/${instanceId}`, this.baseUrl);
- try {
- const resp = await axios.get(url.href, {
- headers: this.makeAuthHeader(),
- });
- return resp.data;
- } catch (e) {
- throw e;
- }
- }
-
- makeAuthHeader(): Record<string, string> {
- switch (this.auth.method) {
- case "external":
- return {};
- case "token":
- return {
- Authorization: `Bearer ${this.auth.token}`,
- };
- }
- }
-}
-
-/**
- * FIXME: This should be deprecated in favor of MerchantApiClient
- */
-export namespace MerchantPrivateApi {
- export async function createOrder(
- merchantService: MerchantServiceInterface,
- instanceName: string,
- req: PostOrderRequest,
- withAuthorization: WithAuthorization = {},
- ): Promise<PostOrderResponse> {
- const baseUrl = merchantService.makeInstanceBaseUrl(instanceName);
- let url = new URL("private/orders", baseUrl);
- const resp = await axios.post(url.href, req, {
- headers: withAuthorization,
- });
- return codecForPostOrderResponse().decode(resp.data);
- }
-
- export async function queryPrivateOrderStatus(
- merchantService: MerchantServiceInterface,
- query: PrivateOrderStatusQuery,
- withAuthorization: WithAuthorization = {},
- ): Promise<MerchantOrderPrivateStatusResponse> {
- const reqUrl = new URL(
- `private/orders/${query.orderId}`,
- merchantService.makeInstanceBaseUrl(query.instance),
- );
- if (query.sessionId) {
- reqUrl.searchParams.set("session_id", query.sessionId);
- }
- const resp = await axios.get(reqUrl.href, { headers: withAuthorization });
- return codecForMerchantOrderPrivateStatusResponse().decode(resp.data);
- }
-
- export async function giveRefund(
- merchantService: MerchantServiceInterface,
- r: {
- instance: string;
- orderId: string;
- amount: string;
- justification: string;
- },
- ): Promise<{ talerRefundUri: string }> {
- const reqUrl = new URL(
- `private/orders/${r.orderId}/refund`,
- merchantService.makeInstanceBaseUrl(r.instance),
- );
- const resp = await axios.post(reqUrl.href, {
- refund: r.amount,
- reason: r.justification,
- });
- return {
- talerRefundUri: resp.data.taler_refund_uri,
- };
- }
-
- export async function createTippingReserve(
- merchantService: MerchantServiceInterface,
- instance: string,
- req: CreateMerchantTippingReserveRequest,
- ): Promise<CreateMerchantTippingReserveConfirmation> {
- const reqUrl = new URL(
- `private/reserves`,
- merchantService.makeInstanceBaseUrl(instance),
- );
- const resp = await axios.post(reqUrl.href, req);
- // FIXME: validate
- return resp.data;
- }
-
- export async function queryTippingReserves(
- merchantService: MerchantServiceInterface,
- instance: string,
- ): Promise<TippingReserveStatus> {
- const reqUrl = new URL(
- `private/reserves`,
- merchantService.makeInstanceBaseUrl(instance),
- );
- const resp = await axios.get(reqUrl.href);
- // FIXME: validate
- return resp.data;
- }
-
- export async function giveTip(
- merchantService: MerchantServiceInterface,
- instance: string,
- req: TipCreateRequest,
- ): Promise<TipCreateConfirmation> {
- const reqUrl = new URL(
- `private/tips`,
- merchantService.makeInstanceBaseUrl(instance),
- );
- const resp = await axios.post(reqUrl.href, req);
- // FIXME: validate
- return resp.data;
- }
-}
-
-export interface CreateMerchantTippingReserveRequest {
- // Amount that the merchant promises to put into the reserve
- initial_balance: AmountString;
-
- // Exchange the merchant intends to use for tipping
- exchange_url: string;
-
- // Desired wire method, for example "iban" or "x-taler-bank"
- wire_method: string;
-}
-
-export interface CreateMerchantTippingReserveConfirmation {
- // Public key identifying the reserve
- reserve_pub: string;
-
- // Wire account of the exchange where to transfer the funds
- payto_uri: string;
-}
-
-export class MerchantService implements MerchantServiceInterface {
- static fromExistingConfig(gc: GlobalTestState, name: string) {
- const cfgFilename = gc.testDir + `/merchant-${name}.conf`;
- const config = Configuration.load(cfgFilename);
- const mc: MerchantConfig = {
- currency: config.getString("taler", "currency").required(),
- database: config.getString("merchantdb-postgres", "config").required(),
- httpPort: config.getNumber("merchant", "port").required(),
- name,
- };
- return new MerchantService(gc, mc, cfgFilename);
- }
-
- proc: ProcessWrapper | undefined;
-
- constructor(
- private globalState: GlobalTestState,
- private merchantConfig: MerchantConfig,
- private configFilename: string,
- ) {}
-
- private currentTimetravel: Duration | undefined;
-
- private isRunning(): boolean {
- return !!this.proc;
- }
-
- setTimetravel(t: Duration | undefined): void {
- if (this.isRunning()) {
- throw Error("can't set time travel while the exchange is running");
- }
- this.currentTimetravel = t;
- }
-
- private get timetravelArg(): string | undefined {
- if (this.currentTimetravel && this.currentTimetravel.d_ms !== "forever") {
- // Convert to microseconds
- return `--timetravel=+${this.currentTimetravel.d_ms * 1000}`;
- }
- return undefined;
- }
-
- /**
- * Return an empty array if no time travel is set,
- * and an array with the time travel command line argument
- * otherwise.
- */
- private get timetravelArgArr(): string[] {
- const tta = this.timetravelArg;
- if (tta) {
- return [tta];
- }
- return [];
- }
-
- get port(): number {
- return this.merchantConfig.httpPort;
- }
-
- get name(): string {
- return this.merchantConfig.name;
- }
-
- async stop(): Promise<void> {
- const httpd = this.proc;
- if (httpd) {
- httpd.proc.kill("SIGTERM");
- await httpd.wait();
- this.proc = undefined;
- }
- }
-
- async start(): Promise<void> {
- await exec(`taler-merchant-dbinit -c "${this.configFilename}"`);
-
- this.proc = this.globalState.spawnService(
- "taler-merchant-httpd",
- ["-LDEBUG", "-c", this.configFilename, ...this.timetravelArgArr],
- `merchant-${this.merchantConfig.name}`,
- );
- }
-
- static async create(
- gc: GlobalTestState,
- mc: MerchantConfig,
- ): Promise<MerchantService> {
- const config = new Configuration();
- config.setString("taler", "currency", mc.currency);
-
- const cfgFilename = gc.testDir + `/merchant-${mc.name}.conf`;
- setTalerPaths(config, gc.testDir + "/talerhome");
- config.setString("merchant", "serve", "tcp");
- config.setString("merchant", "port", `${mc.httpPort}`);
- config.setString(
- "merchant",
- "keyfile",
- "${TALER_DATA_HOME}/merchant/merchant.priv",
- );
- config.setString("merchantdb-postgres", "config", mc.database);
- config.write(cfgFilename);
-
- return new MerchantService(gc, mc, cfgFilename);
- }
-
- addExchange(e: ExchangeServiceInterface): void {
- const config = Configuration.load(this.configFilename);
- config.setString(
- `merchant-exchange-${e.name}`,
- "exchange_base_url",
- e.baseUrl,
- );
- config.setString(
- `merchant-exchange-${e.name}`,
- "currency",
- this.merchantConfig.currency,
- );
- config.setString(`merchant-exchange-${e.name}`, "master_key", e.masterPub);
- config.write(this.configFilename);
- }
-
- async addDefaultInstance(): Promise<void> {
- return await this.addInstance({
- id: "default",
- name: "Default Instance",
- paytoUris: [`payto://x-taler-bank/merchant-default`],
- auth: {
- method: "external",
- },
- });
- }
-
- async addInstance(
- instanceConfig: PartialMerchantInstanceConfig,
- ): Promise<void> {
- if (!this.proc) {
- throw Error("merchant must be running to add instance");
- }
- console.log("adding instance");
- const url = `http://localhost:${this.merchantConfig.httpPort}/management/instances`;
- const auth = instanceConfig.auth ?? { method: "external" };
- await axios.post(url, {
- auth,
- payto_uris: instanceConfig.paytoUris,
- id: instanceConfig.id,
- name: instanceConfig.name,
- address: instanceConfig.address ?? {},
- jurisdiction: instanceConfig.jurisdiction ?? {},
- default_max_wire_fee:
- instanceConfig.defaultMaxWireFee ??
- `${this.merchantConfig.currency}:1.0`,
- default_wire_fee_amortization:
- instanceConfig.defaultWireFeeAmortization ?? 3,
- default_max_deposit_fee:
- instanceConfig.defaultMaxDepositFee ??
- `${this.merchantConfig.currency}:1.0`,
- default_wire_transfer_delay: instanceConfig.defaultWireTransferDelay ?? {
- d_ms: "forever",
- },
- default_pay_delay: instanceConfig.defaultPayDelay ?? { d_ms: "forever" },
- });
- }
-
- makeInstanceBaseUrl(instanceName?: string): string {
- if (instanceName === undefined || instanceName === "default") {
- return `http://localhost:${this.merchantConfig.httpPort}/`;
- } else {
- return `http://localhost:${this.merchantConfig.httpPort}/instances/${instanceName}/`;
- }
- }
-
- async pingUntilAvailable(): Promise<void> {
- const url = `http://localhost:${this.merchantConfig.httpPort}/config`;
- await pingProc(this.proc, url, `merchant (${this.merchantConfig.name})`);
- }
-}
-
-export interface MerchantAuthConfiguration {
- method: "external" | "token";
- token?: string;
-}
-
-export interface PartialMerchantInstanceConfig {
- auth?: MerchantAuthConfiguration;
- id: string;
- name: string;
- paytoUris: string[];
- address?: unknown;
- jurisdiction?: unknown;
- defaultMaxWireFee?: string;
- defaultMaxDepositFee?: string;
- defaultWireFeeAmortization?: number;
- defaultWireTransferDelay?: Duration;
- defaultPayDelay?: Duration;
-}
-
-export interface MerchantInstanceConfig {
- auth: MerchantAuthConfiguration;
- id: string;
- name: string;
- payto_uris: string[];
- address: unknown;
- jurisdiction: unknown;
- default_max_wire_fee: string;
- default_max_deposit_fee: string;
- default_wire_fee_amortization: number;
- default_wire_transfer_delay: Duration;
- default_pay_delay: Duration;
-}
-
-type TestStatus = "pass" | "fail" | "skip";
-
-export interface TestRunResult {
- /**
- * Name of the test.
- */
- name: string;
-
- /**
- * How long did the test run?
- */
- timeSec: number;
-
- status: TestStatus;
-
- reason?: string;
-}
-
-export async function runTestWithState(
- gc: GlobalTestState,
- testMain: (t: GlobalTestState) => Promise<void>,
- testName: string,
- linger: boolean = false,
-): Promise<TestRunResult> {
- const startMs = new Date().getTime();
-
- const p = openPromise();
- let status: TestStatus;
-
- const handleSignal = (s: string) => {
- console.warn(
- `**** received fatal process event, terminating test ${testName}`,
- );
- gc.shutdownSync();
- process.exit(1);
- };
-
- process.on("SIGINT", handleSignal);
- process.on("SIGTERM", handleSignal);
- process.on("unhandledRejection", handleSignal);
- process.on("uncaughtException", handleSignal);
-
- try {
- console.log("running test in directory", gc.testDir);
- await Promise.race([testMain(gc), p.promise]);
- status = "pass";
- if (linger) {
- const rl = readline.createInterface({
- input: process.stdin,
- output: process.stdout,
- terminal: true,
- });
- await new Promise<void>((resolve, reject) => {
- rl.question("Press enter to shut down test.", () => {
- resolve();
- });
- });
- rl.close();
- }
- } 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) {
- return "'" + s.replace("\\", "\\\\").replace("'", "\\'") + "'";
-}
-
-export class WalletCli {
- private currentTimetravel: Duration | undefined;
- private _client: WalletCoreApiClient;
-
- setTimetravel(d: Duration | undefined) {
- this.currentTimetravel = d;
- }
-
- private get timetravelArg(): string | undefined {
- if (this.currentTimetravel && this.currentTimetravel.d_ms !== "forever") {
- // Convert to microseconds
- return `--timetravel=${this.currentTimetravel.d_ms * 1000}`;
- }
- return undefined;
- }
-
- constructor(
- private globalTestState: GlobalTestState,
- private name: string = "default",
- ) {
- const self = this;
- this._client = {
- async call(op: any, payload: any): Promise<any> {
- console.log("calling wallet with timetravel arg", self.timetravelArg);
- const resp = await sh(
- self.globalTestState,
- `wallet-${self.name}`,
- `taler-wallet-cli ${
- self.timetravelArg ?? ""
- } --no-throttle --wallet-db '${self.dbfile}' api '${op}' ${shellWrap(
- JSON.stringify(payload),
- )}`,
- );
- console.log(resp);
- const ar = JSON.parse(resp) as CoreApiResponse;
- if (ar.type === "error") {
- throw new OperationFailedError(ar.error);
- } else {
- return ar.result;
- }
- },
- };
- }
-
- get dbfile(): string {
- return this.globalTestState.testDir + `/walletdb-${this.name}.json`;
- }
-
- deleteDatabase() {
- fs.unlinkSync(this.dbfile);
- }
-
- private get timetravelArgArr(): string[] {
- const tta = this.timetravelArg;
- if (tta) {
- return [tta];
- }
- return [];
- }
-
- get client(): WalletCoreApiClient {
- return this._client;
- }
-
- async runUntilDone(args: { maxRetries?: number } = {}): Promise<void> {
- await runCommand(
- this.globalTestState,
- `wallet-${this.name}`,
- "taler-wallet-cli",
- [
- "--no-throttle",
- ...this.timetravelArgArr,
- "--wallet-db",
- this.dbfile,
- "run-until-done",
- ...(args.maxRetries ? ["--max-retries", `${args.maxRetries}`] : []),
- ],
- );
- }
-
- async runPending(): Promise<void> {
- await runCommand(
- this.globalTestState,
- `wallet-${this.name}`,
- "taler-wallet-cli",
- [
- "--no-throttle",
- ...this.timetravelArgArr,
- "--wallet-db",
- this.dbfile,
- "run-pending",
- ],
- );
- }
-}
diff --git a/packages/taler-wallet-cli/src/harness/helpers.ts b/packages/taler-wallet-cli/src/harness/helpers.ts
deleted file mode 100644
index 3b4e1643f..000000000
--- a/packages/taler-wallet-cli/src/harness/helpers.ts
+++ /dev/null
@@ -1,406 +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/>
- */
-
-/**
- * Helpers to create typical test environments.
- *
- * @author Florian Dold <dold@taler.net>
- */
-
-/**
- * Imports
- */
-import {
- FaultInjectedExchangeService,
- FaultInjectedMerchantService,
-} from "./faultInjection";
-import { CoinConfig, defaultCoinConfig } from "./denomStructures";
-import {
- AmountString,
- Duration,
- ContractTerms,
- PreparePayResultType,
- ConfirmPayResultType,
-} from "@gnu-taler/taler-util";
-import {
- DbInfo,
- BankService,
- ExchangeService,
- MerchantService,
- WalletCli,
- GlobalTestState,
- setupDb,
- ExchangeServiceInterface,
- BankApi,
- BankAccessApi,
- MerchantServiceInterface,
- MerchantPrivateApi,
- HarnessExchangeBankAccount,
- WithAuthorization,
-} from "./harness.js";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-export interface SimpleTestEnvironment {
- commonDb: DbInfo;
- bank: BankService;
- exchange: ExchangeService;
- exchangeBankAccount: HarnessExchangeBankAccount;
- merchant: MerchantService;
- wallet: WalletCli;
-}
-
-export function getRandomIban(countryCode: string): string {
- return `${countryCode}715001051796${(Math.random() * 100000000)
- .toString()
- .substring(0, 6)}`;
-}
-
-export function getRandomString(): string {
- return Math.random().toString(36).substring(2);
-}
-
-/**
- * Run a test case with a simple TESTKUDOS Taler environment, consisting
- * of one exchange, one bank and one merchant.
- */
-export async function createSimpleTestkudosEnvironment(
- t: GlobalTestState,
- coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")),
-): Promise<SimpleTestEnvironment> {
- 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.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://x-taler-bank/merchant-default`],
- });
-
- await merchant.addInstance({
- id: "minst1",
- name: "minst1",
- paytoUris: ["payto://x-taler-bank/minst1"],
- });
-
- console.log("setup done!");
-
- const wallet = new WalletCli(t);
-
- return {
- commonDb: db,
- exchange,
- merchant,
- wallet,
- bank,
- exchangeBankAccount,
- };
-}
-
-export interface FaultyMerchantTestEnvironment {
- commonDb: DbInfo;
- bank: BankService;
- exchange: ExchangeService;
- faultyExchange: FaultInjectedExchangeService;
- exchangeBankAccount: HarnessExchangeBankAccount;
- merchant: MerchantService;
- faultyMerchant: FaultInjectedMerchantService;
- wallet: WalletCli;
-}
-
-/**
- * Run a test case with a simple TESTKUDOS Taler environment, consisting
- * of one exchange, one bank and one merchant.
- */
-export async function createFaultInjectedMerchantTestkudosEnvironment(
- t: GlobalTestState,
-): Promise<FaultyMerchantTestEnvironment> {
- 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 faultyMerchant = new FaultInjectedMerchantService(t, merchant, 9083);
- const faultyExchange = new FaultInjectedExchangeService(t, exchange, 9081);
-
- const exchangeBankAccount = await bank.createExchangeAccount(
- "MyExchange",
- "x",
- );
- exchange.addBankAccount("1", exchangeBankAccount);
-
- bank.setSuggestedExchange(
- faultyExchange,
- exchangeBankAccount.accountPaytoUri,
- );
-
- await bank.start();
-
- await bank.pingUntilAvailable();
-
- exchange.addOfferedCoins(defaultCoinConfig);
-
- await exchange.start();
- await exchange.pingUntilAvailable();
-
- merchant.addExchange(faultyExchange);
-
- await merchant.start();
- await merchant.pingUntilAvailable();
-
- await merchant.addInstance({
- id: "default",
- name: "Default Instance",
- paytoUris: [`payto://x-taler-bank/merchant-default`],
- });
-
- await merchant.addInstance({
- id: "minst1",
- name: "minst1",
- paytoUris: ["payto://x-taler-bank/minst1"],
- });
-
- console.log("setup done!");
-
- const wallet = new WalletCli(t);
-
- return {
- commonDb: db,
- exchange,
- merchant,
- wallet,
- bank,
- exchangeBankAccount,
- faultyMerchant,
- faultyExchange,
- };
-}
-
-/**
- * Withdraw balance.
- */
-export async function startWithdrawViaBank(
- t: GlobalTestState,
- p: {
- wallet: WalletCli;
- bank: BankService;
- exchange: ExchangeServiceInterface;
- amount: AmountString;
- },
-): Promise<void> {
- const { wallet, bank, exchange, amount } = p;
-
- const user = await BankApi.createRandomBankUser(bank);
- const wop = await BankAccessApi.createWithdrawalOperation(bank, user, amount);
-
- // Hand it to the wallet
-
- await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, {
- talerWithdrawUri: wop.taler_withdraw_uri,
- });
-
- await wallet.runPending();
-
- // Confirm it
-
- await BankApi.confirmWithdrawalOperation(bank, user, wop);
-
- // Withdraw
-
- await wallet.client.call(WalletApiOperation.AcceptBankIntegratedWithdrawal, {
- exchangeBaseUrl: exchange.baseUrl,
- talerWithdrawUri: wop.taler_withdraw_uri,
- });
-}
-
-/**
- * Withdraw balance.
- */
-export async function withdrawViaBank(
- t: GlobalTestState,
- p: {
- wallet: WalletCli;
- bank: BankService;
- exchange: ExchangeServiceInterface;
- amount: AmountString;
- },
-): Promise<void> {
- const { wallet } = p;
-
- await startWithdrawViaBank(t, p);
-
- await wallet.runUntilDone();
-
- // Check balance
-
- await wallet.client.call(WalletApiOperation.GetBalances, {});
-}
-
-export async function applyTimeTravel(
- timetravelDuration: Duration,
- s: {
- exchange?: ExchangeService;
- merchant?: MerchantService;
- wallet?: WalletCli;
- },
-): Promise<void> {
- if (s.exchange) {
- await s.exchange.stop();
- s.exchange.setTimetravel(timetravelDuration);
- await s.exchange.start();
- await s.exchange.pingUntilAvailable();
- }
-
- if (s.merchant) {
- await s.merchant.stop();
- s.merchant.setTimetravel(timetravelDuration);
- await s.merchant.start();
- await s.merchant.pingUntilAvailable();
- }
-
- if (s.wallet) {
- s.wallet.setTimetravel(timetravelDuration);
- }
-}
-
-/**
- * Make a simple payment and check that it succeeded.
- */
-export async function makeTestPayment(
- t: GlobalTestState,
- args: {
- merchant: MerchantServiceInterface;
- wallet: WalletCli;
- order: Partial<ContractTerms>;
- instance?: string;
- },
- auth: WithAuthorization = {},
-): Promise<void> {
- // Set up order.
-
- const { wallet, merchant } = args;
- const instance = args.instance ?? "default";
-
- const orderResp = await MerchantPrivateApi.createOrder(
- merchant,
- instance,
- {
- order: args.order,
- },
- auth,
- );
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- },
- auth,
- );
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- // Make wallet pay for the order
-
- const preparePayResult = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: orderStatus.taler_pay_uri,
- },
- );
-
- t.assertTrue(
- preparePayResult.status === PreparePayResultType.PaymentPossible,
- );
-
- const r2 = await wallet.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: preparePayResult.proposalId,
- });
-
- t.assertTrue(r2.type === ConfirmPayResultType.Done);
-
- // Check if payment was successful.
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- instance,
- },
- auth,
- );
-
- t.assertTrue(orderStatus.order_status === "paid");
-}
diff --git a/packages/taler-wallet-cli/src/harness/libeufin.ts b/packages/taler-wallet-cli/src/harness/libeufin.ts
deleted file mode 100644
index 11447b389..000000000
--- a/packages/taler-wallet-cli/src/harness/libeufin.ts
+++ /dev/null
@@ -1,1676 +0,0 @@
-/*
- 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 "@gnu-taler/taler-util";
-import { getRandomIban, getRandomString } from "../harness/helpers.js";
-import {
- GlobalTestState,
- DbInfo,
- pingProc,
- ProcessWrapper,
- runCommand,
- setupDb,
- sh,
-} from "../harness/harness.js";
-
-export interface LibeufinSandboxServiceInterface {
- baseUrl: string;
-}
-
-export interface LibeufinNexusServiceInterface {
- baseUrl: string;
-}
-
-export interface LibeufinServices {
- libeufinSandbox: LibeufinSandboxService;
- libeufinNexus: LibeufinNexusService;
- commonDb: DbInfo;
-}
-
-export interface LibeufinSandboxConfig {
- httpPort: number;
- databaseJdbcUri: string;
-}
-
-export interface LibeufinNexusConfig {
- httpPort: number;
- databaseJdbcUri: string;
-}
-
-export interface DeleteBankConnectionRequest {
- bankConnectionId: string;
-}
-
-interface LibeufinNexusMoneyMovement {
- amount: string;
- creditDebitIndicator: string;
- details: {
- debtor: {
- name: string;
- };
- debtorAccount: {
- iban: string;
- };
- debtorAgent: {
- bic: string;
- };
- creditor: {
- name: string;
- };
- creditorAccount: {
- iban: string;
- };
- creditorAgent: {
- bic: string;
- };
- endToEndId: string;
- unstructuredRemittanceInformation: string;
- };
-}
-
-interface LibeufinNexusBatches {
- batchTransactions: Array<LibeufinNexusMoneyMovement>;
-}
-
-interface LibeufinNexusTransaction {
- amount: string;
- creditDebitIndicator: string;
- status: string;
- bankTransactionCode: string;
- valueDate: string;
- bookingDate: string;
- accountServicerRef: string;
- batches: Array<LibeufinNexusBatches>;
-}
-
-interface LibeufinNexusTransactions {
- transactions: Array<LibeufinNexusTransaction>;
-}
-
-export interface LibeufinCliDetails {
- nexusUrl: string;
- sandboxUrl: string;
- nexusDatabaseUri: string;
- sandboxDatabaseUri: string;
- user: LibeufinNexusUser;
-}
-
-export interface LibeufinEbicsSubscriberDetails {
- hostId: string;
- partnerId: string;
- userId: string;
-}
-
-export interface LibeufinEbicsConnectionDetails {
- subscriberDetails: LibeufinEbicsSubscriberDetails;
- ebicsUrl: string;
- connectionName: string;
-}
-
-export interface LibeufinBankAccountDetails {
- currency: string;
- iban: string;
- bic: string;
- personName: string;
- accountName: string;
-}
-
-export interface LibeufinNexusUser {
- username: string;
- password: string;
-}
-
-export interface LibeufinBackupFileDetails {
- passphrase: string;
- outputFile: string;
- connectionName: string;
-}
-
-export interface LibeufinKeyLetterDetails {
- outputFile: string;
- connectionName: string;
-}
-
-export interface LibeufinBankAccountImportDetails {
- offeredBankAccountName: string;
- nexusBankAccountName: string;
- connectionName: string;
-}
-
-export interface BankAccountInfo {
- iban: string;
- bic: string;
- name: string;
- currency: string;
- label: string;
-}
-
-export interface LibeufinPreparedPaymentDetails {
- creditorIban: string;
- creditorBic: string;
- creditorName: string;
- subject: string;
- amount: string;
- currency: string;
- nexusBankAccountName: string;
-}
-
-export interface LibeufinSandboxAddIncomingRequest {
- creditorIban: string;
- creditorBic: string;
- creditorName: string;
- debtorIban: string;
- debtorBic: string;
- debtorName: string;
- subject: string;
- amount: string;
- currency: string;
- uid: string;
- direction: 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> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-sandbox-config",
- "libeufin-sandbox config localhost",
- {
- ...process.env,
- LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxConfig.databaseJdbcUri,
- },
- );
- this.sandboxProc = this.globalTestState.spawnService(
- "libeufin-sandbox",
- ["serve", "--port", `${this.sandboxConfig.httpPort}`],
- "libeufin-sandbox",
- {
- ...process.env,
- LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxConfig.databaseJdbcUri,
- LIBEUFIN_SANDBOX_ADMIN_PASSWORD: "secret",
- },
- );
- }
-
- async c53tick(): Promise<string> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-sandbox-c53tick",
- "libeufin-sandbox camt053tick",
- {
- ...process.env,
- LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxConfig.databaseJdbcUri,
- },
- );
- return stdout;
- }
-
- async makeTransaction(
- debit: string,
- credit: string,
- amount: string, // $currency:x.y
- subject: string,): Promise<string> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-sandbox-maketransfer",
- `libeufin-sandbox make-transaction --debit-account=${debit} --credit-account=${credit} ${amount} "${subject}"`,
- {
- ...process.env,
- LIBEUFIN_SANDBOX_DB_CONNECTION: this.sandboxConfig.databaseJdbcUri,
- },
- );
- return stdout;
- }
-
- async pingUntilAvailable(): Promise<void> {
- const url = this.baseUrl;
- 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"],
- {
- ...process.env,
- LIBEUFIN_NEXUS_DB_CONNECTION: this.nexusConfig.databaseJdbcUri,
- },
- );
-
- this.nexusProc = this.globalTestState.spawnService(
- "libeufin-nexus",
- ["serve", "--port", `${this.nexusConfig.httpPort}`],
- "libeufin-nexus",
- {
- ...process.env,
- LIBEUFIN_NEXUS_DB_CONNECTION: this.nexusConfig.databaseJdbcUri,
- },
- );
- }
-
- async pingUntilAvailable(): Promise<void> {
- const url = `${this.baseUrl}config`;
- await pingProc(this.nexusProc, url, "libeufin-nexus");
- }
-
- async createNexusSuperuser(details: LibeufinNexusUser): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-nexus",
- `libeufin-nexus superuser ${details.username} --password=${details.password}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_DB_CONNECTION: this.nexusConfig.databaseJdbcUri,
- },
- );
- console.log(stdout);
- }
-}
-
-export interface CreateEbicsSubscriberRequest {
- hostID: string;
- userID: string;
- partnerID: string;
- systemID?: string;
-}
-
-export interface TwgAddIncomingRequest {
- amount: string;
- reserve_pub: string;
- debit_account: 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;
-}
-
-/**
- * The bundle aims at minimizing the amount of input
- * data that is required to initialize a new user + Ebics
- * connection.
- */
-export class NexusUserBundle {
- userReq: CreateNexusUserRequest;
- connReq: CreateEbicsBankConnectionRequest;
- anastasisReq: CreateAnastasisFacadeRequest;
- twgReq: CreateTalerWireGatewayFacadeRequest;
- twgTransferPermission: PostNexusPermissionRequest;
- twgHistoryPermission: PostNexusPermissionRequest;
- twgAddIncomingPermission: PostNexusPermissionRequest;
- localAccountName: string;
- remoteAccountName: string;
-
- constructor(salt: string, ebicsURL: string) {
- this.userReq = {
- username: `username-${salt}`,
- password: `password-${salt}`,
- };
-
- this.connReq = {
- name: `connection-${salt}`,
- ebicsURL: ebicsURL,
- hostID: `ebicshost,${salt}`,
- partnerID: `ebicspartner,${salt}`,
- userID: `ebicsuser,${salt}`,
- };
-
- this.twgReq = {
- currency: "EUR",
- name: `twg-${salt}`,
- reserveTransferLevel: "report",
- accountName: `local-account-${salt}`,
- connectionName: `connection-${salt}`,
- };
- this.anastasisReq = {
- currency: "EUR",
- name: `anastasis-${salt}`,
- reserveTransferLevel: "report",
- accountName: `local-account-${salt}`,
- connectionName: `connection-${salt}`,
- };
- this.remoteAccountName = `remote-account-${salt}`;
- this.localAccountName = `local-account-${salt}`;
- this.twgTransferPermission = {
- action: "grant",
- permission: {
- subjectId: `username-${salt}`,
- subjectType: "user",
- resourceType: "facade",
- resourceId: `twg-${salt}`,
- permissionName: "facade.talerWireGateway.transfer",
- },
- };
- this.twgHistoryPermission = {
- action: "grant",
- permission: {
- subjectId: `username-${salt}`,
- subjectType: "user",
- resourceType: "facade",
- resourceId: `twg-${salt}`,
- permissionName: "facade.talerWireGateway.history",
- },
- };
- }
-}
-
-/**
- * The bundle aims at minimizing the amount of input
- * data that is required to initialize a new Sandbox
- * customer, associating their bank account with a Ebics
- * subscriber.
- */
-export class SandboxUserBundle {
- ebicsBankAccount: CreateEbicsBankAccountRequest;
- constructor(salt: string) {
- this.ebicsBankAccount = {
- currency: "EUR",
- bic: "BELADEBEXXX",
- iban: getRandomIban("DE"),
- label: `remote-account-${salt}`,
- name: `Taler Exchange: ${salt}`,
- subscriber: {
- hostID: `ebicshost,${salt}`,
- partnerID: `ebicspartner,${salt}`,
- userID: `ebicsuser,${salt}`,
- },
- };
- }
-}
-
-export class LibeufinCli {
- cliDetails: LibeufinCliDetails;
- globalTestState: GlobalTestState;
-
- constructor(gc: GlobalTestState, cd: LibeufinCliDetails) {
- this.globalTestState = gc;
- this.cliDetails = cd;
- }
-
- env(): any {
- return {
- ...process.env,
- LIBEUFIN_SANDBOX_URL: this.cliDetails.sandboxUrl,
- LIBEUFIN_SANDBOX_USERNAME: "admin",
- LIBEUFIN_SANDBOX_PASSWORD: "secret",
- }
- }
-
- async checkSandbox(): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-checksandbox",
- "libeufin-cli sandbox check",
- this.env()
- );
- }
-
- async createEbicsHost(hostId: string): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-createebicshost",
- `libeufin-cli sandbox ebicshost create --host-id=${hostId}`,
- this.env()
- );
- console.log(stdout);
- }
-
- async createEbicsSubscriber(
- details: LibeufinEbicsSubscriberDetails,
- ): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-createebicssubscriber",
- "libeufin-cli sandbox ebicssubscriber create" +
- ` --host-id=${details.hostId}` +
- ` --partner-id=${details.partnerId}` +
- ` --user-id=${details.userId}`,
- this.env()
- );
- console.log(stdout);
- }
-
- async createEbicsBankAccount(
- sd: LibeufinEbicsSubscriberDetails,
- bankAccountDetails: LibeufinBankAccountDetails,
- ): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-createebicsbankaccount",
- "libeufin-cli sandbox ebicsbankaccount create" +
- ` --currency=${bankAccountDetails.currency}` +
- ` --iban=${bankAccountDetails.iban}` +
- ` --bic=${bankAccountDetails.bic}` +
- ` --person-name='${bankAccountDetails.personName}'` +
- ` --account-name=${bankAccountDetails.accountName}` +
- ` --ebics-host-id=${sd.hostId}` +
- ` --ebics-partner-id=${sd.partnerId}` +
- ` --ebics-user-id=${sd.userId}`,
- this.env()
- );
- console.log(stdout);
- }
-
- async generateTransactions(accountName: string): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-generatetransactions",
- `libeufin-cli sandbox bankaccount generate-transactions ${accountName}`,
- this.env()
- );
- console.log(stdout);
- }
-
- async showSandboxTransactions(accountName: string): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-showsandboxtransactions",
- `libeufin-cli sandbox bankaccount transactions ${accountName}`,
- this.env()
- );
- console.log(stdout);
- }
-
- async createEbicsConnection(
- connectionDetails: LibeufinEbicsConnectionDetails,
- ): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-createebicsconnection",
- `libeufin-cli connections new-ebics-connection` +
- ` --ebics-url=${connectionDetails.ebicsUrl}` +
- ` --host-id=${connectionDetails.subscriberDetails.hostId}` +
- ` --partner-id=${connectionDetails.subscriberDetails.partnerId}` +
- ` --ebics-user-id=${connectionDetails.subscriberDetails.userId}` +
- ` ${connectionDetails.connectionName}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-
- async createBackupFile(details: LibeufinBackupFileDetails): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-createbackupfile",
- `libeufin-cli connections export-backup` +
- ` --passphrase=${details.passphrase}` +
- ` --output-file=${details.outputFile}` +
- ` ${details.connectionName}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-
- async createKeyLetter(details: LibeufinKeyLetterDetails): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-createkeyletter",
- `libeufin-cli connections get-key-letter` +
- ` ${details.connectionName} ${details.outputFile}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-
- async connect(connectionName: string): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-connect",
- `libeufin-cli connections connect ${connectionName}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-
- async downloadBankAccounts(connectionName: string): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-downloadbankaccounts",
- `libeufin-cli connections download-bank-accounts ${connectionName}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-
- async listOfferedBankAccounts(connectionName: string): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-listofferedbankaccounts",
- `libeufin-cli connections list-offered-bank-accounts ${connectionName}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-
- async importBankAccount(
- importDetails: LibeufinBankAccountImportDetails,
- ): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-importbankaccount",
- "libeufin-cli connections import-bank-account" +
- ` --offered-account-id=${importDetails.offeredBankAccountName}` +
- ` --nexus-bank-account-id=${importDetails.nexusBankAccountName}` +
- ` ${importDetails.connectionName}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-
- async fetchTransactions(bankAccountName: string): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-fetchtransactions",
- `libeufin-cli accounts fetch-transactions ${bankAccountName}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-
- async transactions(bankAccountName: string): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-transactions",
- `libeufin-cli accounts transactions ${bankAccountName}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-
- async preparePayment(details: LibeufinPreparedPaymentDetails): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-preparepayment",
- `libeufin-cli accounts prepare-payment` +
- ` --creditor-iban=${details.creditorIban}` +
- ` --creditor-bic=${details.creditorBic}` +
- ` --creditor-name='${details.creditorName}'` +
- ` --payment-subject='${details.subject}'` +
- ` --payment-amount=${details.currency}:${details.amount}` +
- ` ${details.nexusBankAccountName}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-
- async submitPayment(
- details: LibeufinPreparedPaymentDetails,
- paymentUuid: string,
- ): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-submitpayment",
- `libeufin-cli accounts submit-payment` +
- ` --payment-uuid=${paymentUuid}` +
- ` ${details.nexusBankAccountName}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-
- async newAnastasisFacade(req: NewAnastasisFacadeReq): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-new-anastasis-facade",
- `libeufin-cli facades new-anastasis-facade` +
- ` --currency ${req.currency}` +
- ` --facade-name ${req.facadeName}` +
- ` ${req.connectionName} ${req.accountName}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-
-
- async newTalerWireGatewayFacade(req: NewTalerWireGatewayReq): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-new-taler-wire-gateway-facade",
- `libeufin-cli facades new-taler-wire-gateway-facade` +
- ` --currency ${req.currency}` +
- ` --facade-name ${req.facadeName}` +
- ` ${req.connectionName} ${req.accountName}`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-
- async listFacades(): Promise<void> {
- const stdout = await sh(
- this.globalTestState,
- "libeufin-cli-facades-list",
- `libeufin-cli facades list`,
- {
- ...process.env,
- LIBEUFIN_NEXUS_URL: this.cliDetails.nexusUrl,
- LIBEUFIN_NEXUS_USERNAME: this.cliDetails.user.username,
- LIBEUFIN_NEXUS_PASSWORD: this.cliDetails.user.password,
- },
- );
- console.log(stdout);
- }
-}
-
-interface NewAnastasisFacadeReq {
- facadeName: string;
- connectionName: string;
- accountName: string;
- currency: string;
-}
-
-interface NewTalerWireGatewayReq {
- facadeName: string;
- connectionName: string;
- accountName: string;
- currency: string;
-}
-
-export namespace LibeufinSandboxApi {
-
- export async function rotateKeys(
- libeufinSandboxService: LibeufinSandboxServiceInterface,
- hostID: string,
- ) {
- const baseUrl = libeufinSandboxService.baseUrl;
- let url = new URL(`admin/ebics/hosts/${hostID}/rotate-keys`, baseUrl);
- await axios.post(url.href, {}, {
- auth: {
- username: "admin",
- password: "secret",
- },
- });
- }
- 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",
- },
- {
- auth: {
- username: "admin",
- password: "secret",
- },
- });
- }
-
- export async function createBankAccount(
- libeufinSandboxService: LibeufinSandboxServiceInterface,
- req: BankAccountInfo,
- ) {
- const baseUrl = libeufinSandboxService.baseUrl;
- let url = new URL(`admin/bank-accounts/${req.label}`, baseUrl);
- await axios.post(url.href, req, {
- auth: {
- username: "admin",
- password: "secret",
- },
- });
- }
-
- 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, {
- auth: {
- username: "admin",
- password: "secret",
- },
- });
- }
-
- 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, {
- auth: {
- username: "admin",
- password: "secret",
- },
- });
- }
-
- export async function bookPayment2(
- libeufinSandboxService: LibeufinSandboxService,
- req: LibeufinSandboxAddIncomingRequest,
- ) {
- const baseUrl = libeufinSandboxService.baseUrl;
- let url = new URL("admin/payments", baseUrl);
- await axios.post(url.href, req, {
- auth: {
- username: "admin",
- password: "secret",
- },
- });
- }
-
- export async function bookPayment(
- libeufinSandboxService: LibeufinSandboxService,
- creditorBundle: SandboxUserBundle,
- debitorBundle: SandboxUserBundle,
- subject: string,
- amount: string,
- currency: string,
- ) {
- let req: LibeufinSandboxAddIncomingRequest = {
- creditorIban: creditorBundle.ebicsBankAccount.iban,
- creditorBic: creditorBundle.ebicsBankAccount.bic,
- creditorName: creditorBundle.ebicsBankAccount.name,
- debtorIban: debitorBundle.ebicsBankAccount.iban,
- debtorBic: debitorBundle.ebicsBankAccount.bic,
- debtorName: debitorBundle.ebicsBankAccount.name,
- subject: subject,
- amount: amount,
- currency: currency,
- uid: getRandomString(),
- direction: "CRDT",
- };
- await bookPayment2(libeufinSandboxService, 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, {
- auth: {
- username: "admin",
- password: "secret",
- },
- });
- }
-
- 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, {
- auth: {
- username: "admin",
- password: "secret",
- },
- });
- return res.data as SandboxAccountTransactions;
- }
-
- export async function getCamt053(
- libeufinSandboxService: LibeufinSandboxServiceInterface,
- accountLabel: string,
- ): Promise<any> {
- const baseUrl = libeufinSandboxService.baseUrl;
- let url = new URL("admin/payments/camt", baseUrl);
- return await axios.post(url.href, {
- bankaccount: accountLabel,
- type: 53,
- },
- {
- auth: {
- username: "admin",
- password: "secret",
- },
- });
- }
-
- export async function getAccountInfoWithBalance(
- libeufinSandboxService: LibeufinSandboxServiceInterface,
- accountLabel: string,
- ): Promise<any> {
- const baseUrl = libeufinSandboxService.baseUrl;
- let url = new URL(
- `admin/bank-accounts/${accountLabel}`,
- baseUrl,
- );
- return await axios.get(url.href, {
- auth: {
- username: "admin",
- password: "secret",
- },
- });
- }
-}
-
-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 CreateAnastasisFacadeRequest {
- name: string;
- connectionName: string;
- accountName: string;
- currency: string;
- reserveTransferLevel: "report" | "statement" | "notification";
-}
-
-
-export interface CreateTalerWireGatewayFacadeRequest {
- name: string;
- connectionName: string;
- accountName: string;
- currency: string;
- reserveTransferLevel: "report" | "statement" | "notification";
-}
-
-export interface UpdateNexusUserRequest {
- newPassword: string;
-}
-
-export interface NexusAuth {
- auth: {
- username: string;
- password: string;
- };
-}
-
-export interface CreateNexusUserRequest {
- username: string;
- password: string;
-}
-
-export interface PostNexusTaskRequest {
- name: string;
- cronspec: string;
- type: string; // fetch | submit
- params:
- | {
- level: string; // report | statement | all
- rangeType: string; // all | since-last | previous-days | latest
- }
- | {};
-}
-
-export interface PostNexusPermissionRequest {
- action: "revoke" | "grant";
- permission: {
- subjectType: string;
- subjectId: string;
- resourceType: string;
- resourceId: string;
- permissionName: string;
- };
-}
-
-export namespace LibeufinNexusApi {
- export async function getAllConnections(
- nexus: LibeufinNexusServiceInterface,
- ): Promise<any> {
- let url = new URL("bank-connections", nexus.baseUrl);
- const res = await axios.get(url.href, {
- auth: {
- username: "admin",
- password: "test",
- },
- });
- return res;
- }
-
- export async function deleteBankConnection(
- libeufinNexusService: LibeufinNexusServiceInterface,
- req: DeleteBankConnectionRequest,
- ): Promise<any> {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL("bank-connections/delete-connection", baseUrl);
- return await axios.post(url.href, req, {
- auth: {
- username: "admin",
- password: "test",
- },
- });
- }
-
- 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 getBankAccount(
- libeufinNexusService: LibeufinNexusServiceInterface,
- accountName: string,
- ): Promise<any> {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(
- `bank-accounts/${accountName}`,
- baseUrl,
- );
- return await axios.get(
- url.href,
- {
- auth: {
- username: "admin",
- password: "test",
- },
- },
- );
- }
-
-
- export async function submitInitiatedPayment(
- libeufinNexusService: LibeufinNexusServiceInterface,
- accountName: string,
- paymentId: string,
- ): Promise<void> {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(
- `bank-accounts/${accountName}/payment-initiations/${paymentId}/submit`,
- baseUrl,
- );
- await axios.post(
- url.href,
- {},
- {
- 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 getPaymentInitiations(
- libeufinNexusService: LibeufinNexusService,
- accountName: string,
- username: string = "admin",
- password: string = "test",
- ): Promise<void> {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(
- `/bank-accounts/${accountName}/payment-initiations`,
- baseUrl,
- );
- let response = await axios.get(url.href, {
- auth: {
- username: username,
- password: password,
- },
- });
- console.log(
- `Payment initiations of: ${accountName}`,
- JSON.stringify(response.data, null, 2),
- );
- }
-
- export async function getConfig(
- libeufinNexusService: LibeufinNexusService,
- ): Promise<void> {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(`/config`, baseUrl);
- let response = await axios.get(url.href);
- }
-
- // Uses the Anastasis API to get a list of transactions.
- export async function getAnastasisTransactions(
- libeufinNexusService: LibeufinNexusService,
- anastasisBaseUrl: string,
- params: {}, // of the request: {delta: 5, ..}
- username: string = "admin",
- password: string = "test",
- ): Promise<any> {
- let url = new URL("history/incoming", anastasisBaseUrl);
- let response = await axios.get(url.href, { params: params,
- auth: {
- username: username,
- password: password,
- },
- });
- return response;
- }
-
- // FIXME: this function should return some structured
- // object that represents a history.
- export async function getAccountTransactions(
- libeufinNexusService: LibeufinNexusService,
- accountName: string,
- username: string = "admin",
- password: string = "test",
- ): Promise<any> {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(`/bank-accounts/${accountName}/transactions`, baseUrl);
- let response = await axios.get(url.href, {
- auth: {
- username: username,
- password: password,
- },
- });
- return response;
- }
-
- export async function fetchTransactions(
- libeufinNexusService: LibeufinNexusService,
- accountName: string,
- rangeType: string = "all",
- level: string = "report",
- username: string = "admin",
- password: string = "test",
- ): Promise<any> {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(
- `/bank-accounts/${accountName}/fetch-transactions`,
- baseUrl,
- );
- return await axios.post(
- url.href,
- {
- rangeType: rangeType,
- level: level,
- },
- {
- auth: {
- username: username,
- password: password,
- },
- },
- );
- }
-
- export async function changePassword(
- libeufinNexusService: LibeufinNexusServiceInterface,
- username: string,
- req: UpdateNexusUserRequest,
- auth: NexusAuth,
- ) {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(`/users/${username}/password`, baseUrl);
- await axios.post(url.href, req, auth);
- }
-
- export async function getUser(
- libeufinNexusService: LibeufinNexusServiceInterface,
- auth: NexusAuth,
- ): Promise<any> {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(`/user`, baseUrl);
- return await axios.get(url.href, auth);
- }
-
- export async function createUser(
- libeufinNexusService: LibeufinNexusServiceInterface,
- req: CreateNexusUserRequest,
- ) {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(`/users`, baseUrl);
- await axios.post(url.href, req, {
- auth: {
- username: "admin",
- password: "test",
- },
- });
- }
-
- export async function getAllPermissions(
- libeufinNexusService: LibeufinNexusServiceInterface,
- ): Promise<any> {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(`/permissions`, baseUrl);
- return await axios.get(url.href, {
- auth: {
- username: "admin",
- password: "test",
- },
- });
- }
-
- export async function postPermission(
- libeufinNexusService: LibeufinNexusServiceInterface,
- req: PostNexusPermissionRequest,
- ) {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(`/permissions`, baseUrl);
- await axios.post(url.href, req, {
- auth: {
- username: "admin",
- password: "test",
- },
- });
- }
-
- export async function getTasks(
- libeufinNexusService: LibeufinNexusServiceInterface,
- bankAccountName: string,
- // When void, the request returns the list of all the
- // tasks under this bank account.
- taskName: string | void,
- ): Promise<any> {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(`/bank-accounts/${bankAccountName}/schedule`, baseUrl);
- if (taskName) url = new URL(taskName, `${url}/`);
-
- // It's caller's responsibility to interpret the response.
- return await axios.get(url.href, {
- auth: {
- username: "admin",
- password: "test",
- },
- });
- }
-
- export async function deleteTask(
- libeufinNexusService: LibeufinNexusServiceInterface,
- bankAccountName: string,
- taskName: string,
- ) {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(
- `/bank-accounts/${bankAccountName}/schedule/${taskName}`,
- baseUrl,
- );
- await axios.delete(url.href, {
- auth: {
- username: "admin",
- password: "test",
- },
- });
- }
-
- export async function postTask(
- libeufinNexusService: LibeufinNexusServiceInterface,
- bankAccountName: string,
- req: PostNexusTaskRequest,
- ): Promise<any> {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(`/bank-accounts/${bankAccountName}/schedule`, baseUrl);
- return await axios.post(url.href, req, {
- auth: {
- username: "admin",
- password: "test",
- },
- });
- }
-
- export async function deleteFacade(
- libeufinNexusService: LibeufinNexusServiceInterface,
- facadeName: string,
- ): Promise<any> {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL(`facades/${facadeName}`, baseUrl);
- return await axios.delete(url.href, {
- auth: {
- username: "admin",
- password: "test",
- },
- });
- }
-
- export async function getAllFacades(
- libeufinNexusService: LibeufinNexusServiceInterface,
- ): Promise<any> {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL("facades", baseUrl);
- return await axios.get(url.href, {
- auth: {
- username: "admin",
- password: "test",
- },
- });
- }
-
- export async function createAnastasisFacade(
- libeufinNexusService: LibeufinNexusServiceInterface,
- req: CreateAnastasisFacadeRequest,
- ) {
- const baseUrl = libeufinNexusService.baseUrl;
- let url = new URL("facades", baseUrl);
- await axios.post(
- url.href,
- {
- name: req.name,
- type: "anastasis",
- config: {
- bankAccount: req.accountName,
- bankConnection: req.connectionName,
- currency: req.currency,
- reserveTransferLevel: req.reserveTransferLevel,
- },
- },
- {
- 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",
- },
- },
- );
- }
-}
-
-/**
- * Launch Nexus and Sandbox AND creates users / facades / bank accounts /
- * .. all that's required to start making banking traffic.
- */
-export async function launchLibeufinServices(
- t: GlobalTestState,
- nexusUserBundle: NexusUserBundle[],
- sandboxUserBundle: SandboxUserBundle[] = [],
- withFacades: string[] = [], // takes only "twg" and/or "anastasis"
-): Promise<LibeufinServices> {
- 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();
- console.log("Libeufin services launched!");
-
- for (let sb of sandboxUserBundle) {
- await LibeufinSandboxApi.createEbicsHost(
- libeufinSandbox,
- sb.ebicsBankAccount.subscriber.hostID,
- );
- await LibeufinSandboxApi.createEbicsSubscriber(
- libeufinSandbox,
- sb.ebicsBankAccount.subscriber,
- );
- await LibeufinSandboxApi.createEbicsBankAccount(
- libeufinSandbox,
- sb.ebicsBankAccount,
- );
- }
- console.log("Sandbox user(s) / account(s) / subscriber(s): created");
-
- for (let nb of nexusUserBundle) {
- await LibeufinNexusApi.createEbicsBankConnection(libeufinNexus, nb.connReq);
- await LibeufinNexusApi.connectBankConnection(
- libeufinNexus,
- nb.connReq.name,
- );
- await LibeufinNexusApi.fetchAccounts(libeufinNexus, nb.connReq.name);
- await LibeufinNexusApi.importConnectionAccount(
- libeufinNexus,
- nb.connReq.name,
- nb.remoteAccountName,
- nb.localAccountName,
- );
- await LibeufinNexusApi.createUser(libeufinNexus, nb.userReq);
- for (let facade of withFacades) {
- switch (facade) {
- case "twg":
- await LibeufinNexusApi.createTwgFacade(libeufinNexus, nb.twgReq);
- await LibeufinNexusApi.postPermission(
- libeufinNexus,
- nb.twgTransferPermission,
- );
- await LibeufinNexusApi.postPermission(
- libeufinNexus,
- nb.twgHistoryPermission,
- );
- break;
- case "anastasis":
- await LibeufinNexusApi.createAnastasisFacade(libeufinNexus, nb.anastasisReq);
- }
- }
- }
- console.log(
- "Nexus user(s) / connection(s) / facade(s) / permission(s): created",
- );
-
- return {
- commonDb: db,
- libeufinNexus: libeufinNexus,
- libeufinSandbox: libeufinSandbox,
- };
-}
-
-/**
- * Helper function that searches a payment among
- * a list, as returned by Nexus. The key is just
- * the payment subject.
- */
-export function findNexusPayment(
- key: string,
- payments: LibeufinNexusTransactions,
-): LibeufinNexusMoneyMovement | void {
- let transactions = payments["transactions"];
- for (let i = 0; i < transactions.length; i++) {
- let batches = transactions[i]["batches"];
- for (let y = 0; y < batches.length; y++) {
- let movements = batches[y]["batchTransactions"];
- for (let z = 0; z < movements.length; z++) {
- let movement = movements[z];
- if (movement["details"]["unstructuredRemittanceInformation"] == key)
- return movement;
- }
- }
- }
-}
diff --git a/packages/taler-wallet-cli/src/harness/merchantApiTypes.ts b/packages/taler-wallet-cli/src/harness/merchantApiTypes.ts
deleted file mode 100644
index a93a0ed25..000000000
--- a/packages/taler-wallet-cli/src/harness/merchantApiTypes.ts
+++ /dev/null
@@ -1,318 +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/>
- */
-
-/**
- * Test harness for various GNU Taler components.
- * Also provides a fault-injection proxy.
- *
- * @author Florian Dold <dold@taler.net>
- */
-
-/**
- * Imports.
- */
-import {
- ContractTerms,
- Duration,
- Codec,
- buildCodecForObject,
- codecForString,
- codecOptional,
- codecForConstString,
- codecForBoolean,
- codecForNumber,
- codecForContractTerms,
- codecForAny,
- buildCodecForUnion,
- AmountString,
- Timestamp,
- CoinPublicKeyString,
- EddsaPublicKeyString,
- codecForAmountString,
-} from "@gnu-taler/taler-util";
-
-export interface PostOrderRequest {
- // The order must at least contain the minimal
- // order detail, but can override all
- order: Partial<ContractTerms>;
-
- // if set, the backend will then set the refund deadline to the current
- // time plus the specified delay.
- refund_delay?: Duration;
-
- // specifies the payment target preferred by the client. Can be used
- // to select among the various (active) wire methods supported by the instance.
- payment_target?: string;
-
- // FIXME: some fields are missing
-
- // Should a token for claiming the order be generated?
- // False can make sense if the ORDER_ID is sufficiently
- // high entropy to prevent adversarial claims (like it is
- // if the backend auto-generates one). Default is 'true'.
- create_token?: boolean;
-}
-
-export type ClaimToken = string;
-
-export interface PostOrderResponse {
- order_id: string;
- token?: ClaimToken;
-}
-
-export const codecForPostOrderResponse = (): Codec<PostOrderResponse> =>
- buildCodecForObject<PostOrderResponse>()
- .property("order_id", codecForString())
- .property("token", codecOptional(codecForString()))
- .build("PostOrderResponse");
-
-export const codecForCheckPaymentPaidResponse = (): Codec<CheckPaymentPaidResponse> =>
- buildCodecForObject<CheckPaymentPaidResponse>()
- .property("order_status_url", codecForString())
- .property("order_status", codecForConstString("paid"))
- .property("refunded", codecForBoolean())
- .property("wired", codecForBoolean())
- .property("deposit_total", codecForAmountString())
- .property("exchange_ec", codecForNumber())
- .property("exchange_hc", codecForNumber())
- .property("refund_amount", codecForAmountString())
- .property("contract_terms", codecForContractTerms())
- // FIXME: specify
- .property("wire_details", codecForAny())
- .property("wire_reports", codecForAny())
- .property("refund_details", codecForAny())
- .build("CheckPaymentPaidResponse");
-
-export const codecForCheckPaymentUnpaidResponse = (): Codec<CheckPaymentUnpaidResponse> =>
- buildCodecForObject<CheckPaymentUnpaidResponse>()
- .property("order_status", codecForConstString("unpaid"))
- .property("taler_pay_uri", codecForString())
- .property("order_status_url", codecForString())
- .property("already_paid_order_id", codecOptional(codecForString()))
- .build("CheckPaymentPaidResponse");
-
-export const codecForCheckPaymentClaimedResponse = (): Codec<CheckPaymentClaimedResponse> =>
- buildCodecForObject<CheckPaymentClaimedResponse>()
- .property("order_status", codecForConstString("claimed"))
- .property("contract_terms", codecForContractTerms())
- .build("CheckPaymentClaimedResponse");
-
-export const codecForMerchantOrderPrivateStatusResponse = (): Codec<MerchantOrderPrivateStatusResponse> =>
- buildCodecForUnion<MerchantOrderPrivateStatusResponse>()
- .discriminateOn("order_status")
- .alternative("paid", codecForCheckPaymentPaidResponse())
- .alternative("unpaid", codecForCheckPaymentUnpaidResponse())
- .alternative("claimed", codecForCheckPaymentClaimedResponse())
- .build("MerchantOrderPrivateStatusResponse");
-
-export type MerchantOrderPrivateStatusResponse =
- | CheckPaymentPaidResponse
- | CheckPaymentUnpaidResponse
- | CheckPaymentClaimedResponse;
-
-export interface CheckPaymentClaimedResponse {
- // Wallet claimed the order, but didn't pay yet.
- order_status: "claimed";
-
- contract_terms: ContractTerms;
-}
-
-export interface CheckPaymentPaidResponse {
- // did the customer pay for this contract
- order_status: "paid";
-
- // Was the payment refunded (even partially)
- refunded: boolean;
-
- // Did the exchange wire us the funds
- wired: boolean;
-
- // Total amount the exchange deposited into our bank account
- // for this contract, excluding fees.
- deposit_total: AmountString;
-
- // Numeric error code indicating errors the exchange
- // encountered tracking the wire transfer for this purchase (before
- // we even got to specific coin issues).
- // 0 if there were no issues.
- exchange_ec: number;
-
- // HTTP status code returned by the exchange when we asked for
- // information to track the wire transfer for this purchase.
- // 0 if there were no issues.
- exchange_hc: number;
-
- // Total amount that was refunded, 0 if refunded is false.
- refund_amount: AmountString;
-
- // Contract terms
- contract_terms: ContractTerms;
-
- // Ihe wire transfer status from the exchange for this order if available, otherwise empty array
- wire_details: TransactionWireTransfer[];
-
- // Reports about trouble obtaining wire transfer details, empty array if no trouble were encountered.
- wire_reports: TransactionWireReport[];
-
- // The refund details for this order. One entry per
- // refunded coin; empty array if there are no refunds.
- refund_details: RefundDetails[];
-
- order_status_url: string;
-}
-
-export interface CheckPaymentUnpaidResponse {
- order_status: "unpaid";
-
- // URI that the wallet must process to complete the payment.
- taler_pay_uri: string;
-
- order_status_url: string;
-
- // Alternative order ID which was paid for already in the same session.
- // Only given if the same product was purchased before in the same session.
- already_paid_order_id?: string;
-
- // We do we NOT return the contract terms here because they may not
- // exist in case the wallet did not yet claim them.
-}
-
-export interface RefundDetails {
- // Reason given for the refund
- reason: string;
-
- // when was the refund approved
- timestamp: Timestamp;
-
- // Total amount that was refunded (minus a refund fee).
- amount: AmountString;
-}
-
-export interface TransactionWireTransfer {
- // Responsible exchange
- exchange_url: string;
-
- // 32-byte wire transfer identifier
- wtid: string;
-
- // execution time of the wire transfer
- execution_time: Timestamp;
-
- // Total amount that has been wire transferred
- // to the merchant
- amount: AmountString;
-
- // Was this transfer confirmed by the merchant via the
- // POST /transfers API, or is it merely claimed by the exchange?
- confirmed: boolean;
-}
-
-export interface TransactionWireReport {
- // Numerical error code
- code: number;
-
- // Human-readable error description
- hint: string;
-
- // Numerical error code from the exchange.
- exchange_ec: number;
-
- // HTTP status code received from the exchange.
- exchange_hc: number;
-
- // Public key of the coin for which we got the exchange error.
- coin_pub: CoinPublicKeyString;
-}
-
-export interface TippingReserveStatus {
- // Array of all known reserves (possibly empty!)
- reserves: ReserveStatusEntry[];
-}
-
-export interface ReserveStatusEntry {
- // Public key of the reserve
- reserve_pub: string;
-
- // Timestamp when it was established
- creation_time: Timestamp;
-
- // Timestamp when it expires
- expiration_time: Timestamp;
-
- // Initial amount as per reserve creation call
- merchant_initial_amount: AmountString;
-
- // Initial amount as per exchange, 0 if exchange did
- // not confirm reserve creation yet.
- exchange_initial_amount: AmountString;
-
- // Amount picked up so far.
- pickup_amount: AmountString;
-
- // Amount approved for tips that exceeds the pickup_amount.
- committed_amount: AmountString;
-
- // Is this reserve active (false if it was deleted but not purged)
- active: boolean;
-}
-
-export interface TipCreateConfirmation {
- // Unique tip identifier for the tip that was created.
- tip_id: string;
-
- // taler://tip URI for the tip
- taler_tip_uri: string;
-
- // URL that will directly trigger processing
- // the tip when the browser is redirected to it
- tip_status_url: string;
-
- // when does the tip expire
- tip_expiration: Timestamp;
-}
-
-export interface TipCreateRequest {
- // Amount that the customer should be tipped
- amount: AmountString;
-
- // Justification for giving the tip
- justification: string;
-
- // URL that the user should be directed to after tipping,
- // will be included in the tip_token.
- next_url: string;
-}
-
-export interface MerchantInstancesResponse {
- // List of instances that are present in the backend (see Instance)
- instances: MerchantInstanceDetail[];
-}
-
-export interface MerchantInstanceDetail {
- // Merchant name corresponding to this instance.
- name: string;
-
- // Merchant instance this response is about ($INSTANCE)
- id: string;
-
- // Public key of the merchant/instance, in Crockford Base32 encoding.
- merchant_pub: EddsaPublicKeyString;
-
- // List of the payment targets supported by this instance. Clients can
- // specify the desired payment target in /order requests. Note that
- // front-ends do not have to support wallets selecting payment targets.
- payment_targets: string[];
-}
diff --git a/packages/taler-wallet-cli/src/harness/sync.ts b/packages/taler-wallet-cli/src/harness/sync.ts
deleted file mode 100644
index 16be89eff..000000000
--- a/packages/taler-wallet-cli/src/harness/sync.ts
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- 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 { URL } from "@gnu-taler/taler-util";
-import * as fs from "fs";
-import * as util from "util";
-import {
- GlobalTestState,
- pingProc,
- ProcessWrapper,
-} from "../harness/harness.js";
-import { Configuration } from "@gnu-taler/taler-util";
-
-const exec = util.promisify(require("child_process").exec);
-
-export interface SyncConfig {
- /**
- * Human-readable name used in the test harness logs.
- */
- name: string;
-
- httpPort: number;
-
- /**
- * Database connection string (only postgres is supported).
- */
- database: string;
-
- annualFee: string;
-
- currency: string;
-
- uploadLimitMb: number;
-
- /**
- * Fulfillment URL used for contract terms related to
- * sync.
- */
- fulfillmentUrl: string;
-
- paymentBackendUrl: string;
-}
-
-function setSyncPaths(config: Configuration, home: string) {
- config.setString("paths", "sync_home", home);
- // We need to make sure that the path of taler_runtime_dir isn't too long,
- // as it contains unix domain sockets (108 character limit).
- const runDir = fs.mkdtempSync("/tmp/taler-test-");
- config.setString("paths", "sync_runtime_dir", runDir);
- config.setString("paths", "sync_data_home", "$SYNC_HOME/.local/share/sync/");
- config.setString("paths", "sync_config_home", "$SYNC_HOME/.config/sync/");
- config.setString("paths", "sync_cache_home", "$SYNC_HOME/.config/sync/");
-}
-
-export class SyncService {
- static async create(
- gc: GlobalTestState,
- sc: SyncConfig,
- ): Promise<SyncService> {
- const config = new Configuration();
-
- const cfgFilename = gc.testDir + `/sync-${sc.name}.conf`;
- setSyncPaths(config, gc.testDir + "/synchome");
- config.setString("taler", "currency", sc.currency);
- config.setString("sync", "serve", "tcp");
- config.setString("sync", "port", `${sc.httpPort}`);
- config.setString("sync", "db", "postgres");
- config.setString("syncdb-postgres", "config", sc.database);
- config.setString("sync", "payment_backend_url", sc.paymentBackendUrl);
- config.setString("sync", "upload_limit_mb", `${sc.uploadLimitMb}`);
- config.write(cfgFilename);
-
- return new SyncService(gc, sc, cfgFilename);
- }
-
- proc: ProcessWrapper | undefined;
-
- get baseUrl(): string {
- return `http://localhost:${this.syncConfig.httpPort}/`;
- }
-
- async start(): Promise<void> {
- await exec(`sync-dbinit -c "${this.configFilename}"`);
-
- this.proc = this.globalState.spawnService(
- "sync-httpd",
- ["-LDEBUG", "-c", this.configFilename],
- `sync-${this.syncConfig.name}`,
- );
- }
-
- async pingUntilAvailable(): Promise<void> {
- const url = new URL("config", this.baseUrl).href;
- await pingProc(this.proc, url, "sync");
- }
-
- constructor(
- private globalState: GlobalTestState,
- private syncConfig: SyncConfig,
- private configFilename: string,
- ) {}
-}
diff --git a/packages/taler-wallet-cli/src/import-meta-url.js b/packages/taler-wallet-cli/src/import-meta-url.js
new file mode 100644
index 000000000..c0e657160
--- /dev/null
+++ b/packages/taler-wallet-cli/src/import-meta-url.js
@@ -0,0 +1,2 @@
+// Helper to make 'import.meta.url' available in esbuild-bundled code as well.
+export const import_meta_url = require("url").pathToFileURL(__filename);
diff --git a/packages/taler-wallet-cli/src/index.ts b/packages/taler-wallet-cli/src/index.ts
index 142e98e7c..a1b008f5e 100644
--- a/packages/taler-wallet-cli/src/index.ts
+++ b/packages/taler-wallet-cli/src/index.ts
@@ -17,49 +17,59 @@
/**
* Imports.
*/
-import os from "os";
-import fs from "fs";
-import path from "path";
-import { deepStrictEqual } from "assert";
-// Polyfill for encoding which isn't present globally in older nodejs versions
-import { TextEncoder, TextDecoder } from "util";
-// @ts-ignore
-global.TextEncoder = TextEncoder;
-// @ts-ignore
-global.TextDecoder = TextDecoder;
-import * as clk from "./clk.js";
-import { getTestInfo, runTests } from "./integrationtests/testrunner.js";
import {
- PreparePayResultType,
- setDangerousTimetravel,
- classifyTalerUri,
- TalerUriType,
- RecoveryMergeStrategy,
- Amounts,
+ AbsoluteTime,
addPaytoQueryParams,
+ AgeRestriction,
+ AmountString,
codecForList,
codecForString,
+ CoreApiResponse,
+ Duration,
+ encodeCrock,
+ getErrorDetailFromException,
+ getRandomBytes,
+ InitRequest,
+ j2s,
Logger,
- Configuration,
- decodeCrock,
- rsaBlind,
+ NotificationType,
+ parsePaytoUri,
+ parseTalerUri,
+ PreparePayResultType,
+ sampleWalletCoreTransactions,
+ setDangerousTimetravel,
+ setGlobalLogLevelFromString,
+ summarizeTalerErrorDetail,
+ TalerUriAction,
+ TransactionIdStr,
+ WalletNotification,
} from "@gnu-taler/taler-util";
+import { clk } from "@gnu-taler/taler-util/clk";
+import {
+ getenv,
+ pathHomedir,
+ processExit,
+ readFile,
+ readlinePrompt,
+ setUnhandledRejectionHandler,
+} from "@gnu-taler/taler-util/compat";
+import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
+import { JsonMessage, runRpcServer } from "@gnu-taler/taler-util/twrpc";
import {
- NodeHttpLib,
- getDefaultNodeWallet,
- OperationFailedAndReportedError,
- OperationFailedError,
- NodeThreadCryptoWorkerFactory,
- CryptoApi,
- walletCoreDebugFlags,
+ AccessStats,
+ createNativeWalletHost2,
+ nativeCrypto,
+ Wallet,
WalletApiOperation,
WalletCoreApiClient,
- Wallet,
} from "@gnu-taler/taler-wallet-core";
-import { lintExchangeDeployment } from "./lint.js";
-import { runBench1 } from "./bench1.js";
-import { runEnv1 } from "./env1.js";
-import { GlobalTestState, runTestWithState } from "./harness/harness.js";
+import {
+ createRemoteWallet,
+ getClientFromRemoteWallet,
+ makeNotificationWaiter,
+} from "@gnu-taler/taler-wallet-core/remote";
+
+import * as fs from "node:fs";
// This module also serves as the entry point for the crypto
// thread worker, and thus must expose these two handlers.
@@ -70,7 +80,19 @@ export {
const logger = new Logger("taler-wallet-cli.ts");
-const defaultWalletDbPath = os.homedir + "/" + ".talerwalletdb.json";
+let observabilityEventFile: string | undefined = undefined;
+
+const EXIT_EXCEPTION = 4;
+const EXIT_API_ERROR = 5;
+
+setUnhandledRejectionHandler((error: any) => {
+ logger.error("unhandledRejection", error.message);
+ logger.error("stack", error.stack);
+ processExit(1);
+});
+
+const defaultWalletDbPath = pathHomedir() + "/" + ".talerwalletdb.sqlite3";
+const defaultWalletCoreSocket = pathHomedir() + "/" + ".wallet-core.sock";
function assertUnreachable(x: never): never {
throw new Error("Didn't expect to get here");
@@ -87,7 +109,7 @@ async function doPay(
if (result.status === PreparePayResultType.InsufficientBalance) {
console.log("contract", result.contractTerms);
console.error("insufficient balance");
- process.exit(1);
+ processExit(1);
return;
}
if (result.status === PreparePayResultType.AlreadyConfirmed) {
@@ -96,8 +118,7 @@ async function doPay(
} else {
console.log("payment already in progress");
}
-
- process.exit(0);
+ processExit(0);
return;
}
if (result.status === "payment-possible") {
@@ -139,11 +160,11 @@ function applyVerbose(verbose: boolean): void {
// TODO
}
+declare const __VERSION__: string;
+declare const __GIT_HASH__: string;
function printVersion(): void {
- // eslint-disable-next-line @typescript-eslint/no-var-requires
- const info = require("../package.json");
- console.log(`${info.version}`);
- process.exit(0);
+ console.log(`${__VERSION__} ${__GIT_HASH__}`);
+ processExit(0);
}
export const walletCli = clk
@@ -151,7 +172,10 @@ export const walletCli = clk
help: "Command line interface for the GNU Taler wallet.",
})
.maybeOption("walletDbFile", ["--wallet-db"], clk.STRING, {
- help: "location of the wallet database file",
+ help: "Location of the wallet database file",
+ })
+ .maybeOption("walletConnection", ["--wallet-connection"], clk.STRING, {
+ help: "Connect to an RPC wallet",
})
.maybeOption("timetravel", ["--timetravel"], clk.INT, {
help: "modify system time by given offset in microseconds",
@@ -161,61 +185,188 @@ export const walletCli = clk
setDangerousTimetravel(x / 1000);
},
})
+ .maybeOption("cryptoWorker", ["--crypto-worker"], clk.STRING, {
+ help: "Override crypto worker implementation type.",
+ })
+ .maybeOption("log", ["-L", "--log"], clk.STRING, {
+ help: "configure log level (NONE, ..., TRACE)",
+ onPresentHandler: (x) => {
+ setGlobalLogLevelFromString(x);
+ },
+ })
.maybeOption("inhibit", ["--inhibit"], clk.STRING, {
- help:
- "Inhibit running certain operations, useful for debugging and testing.",
+ help: "Inhibit running certain operations, useful for debugging and testing.",
})
.flag("noThrottle", ["--no-throttle"], {
help: "Don't do any request throttling.",
})
+ .flag("noHttp", ["--no-http"], {
+ help: "Allow unsafe http connections.",
+ })
.flag("version", ["-v", "--version"], {
onPresentHandler: printVersion,
})
.flag("verbose", ["-V", "--verbose"], {
help: "Enable verbose output.",
+ })
+ .flag("skipDefaults", ["--skip-defaults"], {
+ help: "Skip configuring default exchanges.",
});
type WalletCliArgsType = clk.GetArgType<typeof walletCli>;
-async function withWallet<T>(
+function checkEnvFlag(name: string): boolean {
+ const val = getenv(name);
+ if (val == "1") {
+ return true;
+ }
+ return false;
+}
+
+export interface WalletContext {
+ /**
+ * High-level client for making API requests to wallet-core.
+ */
+ client: WalletCoreApiClient;
+
+ /**
+ * Low-level interface for making API requests to wallet-core.
+ */
+ makeCoreApiRequest(
+ operation: string,
+ payload: unknown,
+ ): Promise<CoreApiResponse>;
+
+ /**
+ * Return a promise that resolves after the wallet has emitted a notification
+ * that meets the criteria of the "cond" predicate.
+ */
+ waitForNotificationCond<T>(
+ cond: (n: WalletNotification) => T | false | undefined,
+ ): Promise<T>;
+}
+
+interface CreateWalletResult {
+ wallet: Wallet;
+ getStats: () => AccessStats;
+}
+
+async function createLocalWallet(
walletCliArgs: WalletCliArgsType,
- f: (w: { client: WalletCoreApiClient; ws: Wallet }) => Promise<T>,
-): Promise<T> {
+ args: WalletRunArgs,
+ notificationHandler?: (n: WalletNotification) => void,
+): Promise<CreateWalletResult> {
const dbPath = walletCliArgs.wallet.walletDbFile ?? defaultWalletDbPath;
- const myHttpLib = new NodeHttpLib();
- if (walletCliArgs.wallet.noThrottle) {
- myHttpLib.setThrottling(false);
- }
- const wallet = await getDefaultNodeWallet({
- persistentStoragePath: dbPath,
+ const myHttpLib = createPlatformHttpLib({
+ enableThrottling: walletCliArgs.wallet.noThrottle ? false : true,
+ requireTls: walletCliArgs.wallet.noHttp,
+ });
+ const wh = await createNativeWalletHost2({
+ persistentStoragePath: dbPath !== ":memory:" ? dbPath : undefined,
httpLib: myHttpLib,
+ notifyHandler: (n) => {
+ logger.info(`wallet notification: ${j2s(n)}`);
+ if (notificationHandler) {
+ notificationHandler(n);
+ }
+ },
+ cryptoWorkerType: walletCliArgs.wallet.cryptoWorker as any,
});
+
applyVerbose(walletCliArgs.wallet.verbose);
+ const res = { wallet: wh.wallet, getStats: wh.getDbStats };
+
+ if (args.noInit) {
+ return res;
+ }
try {
- const w = {
- ws: wallet,
- client: wallet.client,
- };
- await wallet.handleCoreApiRequest("initWallet", "native-init", {});
- const ret = await f(w);
- return ret;
+ await wh.wallet.handleCoreApiRequest(
+ WalletApiOperation.InitWallet,
+ "native-init",
+ {
+ config: {
+ lazyTaskLoop: args.lazyTaskLoop,
+ testing: {
+ devModeActive: checkEnvFlag("TALER_WALLET_DEV_MODE"),
+ denomselAllowLate: checkEnvFlag(
+ "TALER_WALLET_DEBUG_DENOMSEL_ALLOW_LATE",
+ ),
+ emitObservabilityEvents: observabilityEventFile != null,
+ skipDefaults: walletCliArgs.wallet.skipDefaults,
+ },
+ },
+ } satisfies InitRequest,
+ );
+ return res;
} catch (e) {
- if (
- e instanceof OperationFailedAndReportedError ||
- e instanceof OperationFailedError
- ) {
- console.error("Operation failed: " + e.message);
- console.error(
- "Error details:",
- JSON.stringify(e.operationError, undefined, 2),
- );
- } else {
- console.error("caught unhandled exception (bug?):", e);
+ const ed = getErrorDetailFromException(e);
+ console.error("Operation failed: " + summarizeTalerErrorDetail(ed));
+ console.error("Error details:", JSON.stringify(ed, undefined, 2));
+ processExit(1);
+ }
+}
+
+function writeObservabilityLog(notif: WalletNotification): void {
+ if (observabilityEventFile) {
+ switch (notif.type) {
+ case NotificationType.RequestObservabilityEvent:
+ case NotificationType.TaskObservabilityEvent:
+ fs.appendFileSync(observabilityEventFile, JSON.stringify(notif) + "\n");
+ break;
+ }
+ }
+}
+
+export interface WalletRunArgs {
+ lazyTaskLoop?: boolean;
+ noInit?: boolean;
+}
+
+async function withWallet<T>(
+ walletCliArgs: WalletCliArgsType,
+ args: WalletRunArgs = {},
+ f: (ctx: WalletContext) => Promise<T>,
+): Promise<T> {
+ const waiter = makeNotificationWaiter();
+
+ const onNotif = (notif: WalletNotification) => {
+ waiter.notify(notif);
+ writeObservabilityLog(notif);
+ };
+
+ if (walletCliArgs.wallet.walletConnection) {
+ logger.info("creating remote wallet");
+ const w = await createRemoteWallet({
+ name: "wallet",
+ notificationHandler: onNotif,
+ socketFilename: walletCliArgs.wallet.walletConnection,
+ });
+ const ctx: WalletContext = {
+ makeCoreApiRequest(operation, payload) {
+ return w.makeCoreApiRequest(operation, payload);
+ },
+ client: getClientFromRemoteWallet(w),
+ waitForNotificationCond: waiter.waitForNotificationCond,
+ };
+ const res = await f(ctx);
+ w.close();
+ return res;
+ } else {
+ const wh = await createLocalWallet(walletCliArgs, args, onNotif);
+ const ctx: WalletContext = {
+ client: wh.wallet.client,
+ waitForNotificationCond: waiter.waitForNotificationCond,
+ makeCoreApiRequest(operation, payload) {
+ return wh.wallet.handleCoreApiRequest(operation, "my-req", payload);
+ },
+ };
+ const result = await f(ctx);
+ await wh.wallet.client.call(WalletApiOperation.Shutdown, {});
+ if (process.env.TALER_WALLET_DBSTATS) {
+ console.log("database stats:");
+ console.log(j2s(wh.getStats()));
}
- process.exit(1);
- } finally {
- logger.info("operation with wallet finished, stopping");
- wallet.stop();
+ return result;
}
}
@@ -225,92 +376,229 @@ walletCli
help: "Show raw JSON.",
})
.action(async (args) => {
- await withWallet(args, async (wallet) => {
- const balance = await wallet.client.call(
- WalletApiOperation.GetBalances,
- {},
- );
- console.log(JSON.stringify(balance, undefined, 2));
- });
+ await withWallet(
+ args,
+ {
+ lazyTaskLoop: true,
+ },
+ async (wallet) => {
+ const balance = await wallet.client.call(
+ WalletApiOperation.GetBalances,
+ {},
+ );
+ console.log(JSON.stringify(balance, undefined, 2));
+ },
+ );
});
walletCli
.subcommand("api", "api", { help: "Call the wallet-core API directly." })
.requiredArgument("operation", clk.STRING)
.requiredArgument("request", clk.STRING)
+ .flag("expectSuccess", ["--expect-success"], {
+ help: "Exit with non-zero status code when request fails instead of returning error JSON.",
+ })
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, {}, async (wallet) => {
let requestJson;
+ logger.info(`handling 'api' request (${args.api.operation})`);
+ const jsonContent = args.api.request.startsWith("@")
+ ? readFile(args.api.request.substring(1))
+ : args.api.request;
try {
- requestJson = JSON.parse(args.api.request);
+ requestJson = JSON.parse(jsonContent);
} catch (e) {
console.error("Invalid JSON");
- process.exit(1);
+ processExit(1);
+ }
+ try {
+ const resp = await wallet.makeCoreApiRequest(
+ args.api.operation,
+ requestJson,
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ if (resp.type === "error") {
+ if (args.api.expectSuccess) {
+ processExit(EXIT_API_ERROR);
+ }
+ }
+ } catch (e) {
+ logger.error(`Got exception while handling API request ${e}`);
+ processExit(EXIT_EXCEPTION);
}
- const resp = await wallet.ws.handleCoreApiRequest(
- args.api.operation,
- "reqid-1",
- requestJson,
- );
- console.log(JSON.stringify(resp, undefined, 2));
});
+ logger.info("finished handling API request");
});
-walletCli
- .subcommand("", "pending", { help: "Show pending operations." })
- .action(async (args) => {
- await withWallet(args, async (wallet) => {
+const transactionsCli = walletCli
+ .subcommand("transactions", "transactions", { help: "Manage transactions." })
+ .maybeOption("currency", ["--currency"], clk.STRING, {
+ help: "Filter by currency.",
+ })
+ .maybeOption("search", ["--search"], clk.STRING, {
+ help: "Filter by search string",
+ })
+ .flag("includeRefreshes", ["--include-refreshes"]);
+
+// Default action
+transactionsCli.action(async (args) => {
+ await withWallet(
+ args,
+ {
+ lazyTaskLoop: true,
+ },
+ async (wallet) => {
const pending = await wallet.client.call(
- WalletApiOperation.GetPendingOperations,
- {},
+ WalletApiOperation.GetTransactions,
+ {
+ currency: args.transactions.currency,
+ search: args.transactions.search,
+ includeRefreshes: args.transactions.includeRefreshes,
+ sort: "stable-ascending",
+ },
);
console.log(JSON.stringify(pending, undefined, 2));
+ },
+ );
+});
+
+transactionsCli
+ .subcommand("deleteTransaction", "delete", {
+ help: "Permanently delete a transaction from the transaction list.",
+ })
+ .requiredArgument("transactionId", clk.STRING, {
+ help: "Identifier of the transaction to delete",
+ })
+ .action(async (args) => {
+ await withWallet(
+ args,
+ {
+ lazyTaskLoop: true,
+ },
+ async (wallet) => {
+ await wallet.client.call(WalletApiOperation.DeleteTransaction, {
+ transactionId: args.deleteTransaction
+ .transactionId as TransactionIdStr,
+ });
+ },
+ );
+ });
+
+transactionsCli
+ .subcommand("suspendTransaction", "suspend", {
+ help: "Suspend a transaction.",
+ })
+ .requiredArgument("transactionId", clk.STRING, {
+ help: "Identifier of the transaction to suspend.",
+ })
+ .action(async (args) => {
+ await withWallet(
+ args,
+ {
+ lazyTaskLoop: true,
+ },
+ async (wallet) => {
+ await wallet.client.call(WalletApiOperation.SuspendTransaction, {
+ transactionId: args.suspendTransaction
+ .transactionId as TransactionIdStr,
+ });
+ },
+ );
+ });
+
+transactionsCli
+ .subcommand("fail", "fail", {
+ help: "Fail a transaction (when it can't be aborted).",
+ })
+ .requiredArgument("transactionId", clk.STRING, {
+ help: "Identifier of the transaction to fail.",
+ })
+ .action(async (args) => {
+ await withWallet(
+ args,
+ {
+ lazyTaskLoop: true,
+ },
+ async (wallet) => {
+ await wallet.client.call(WalletApiOperation.FailTransaction, {
+ transactionId: args.fail.transactionId as TransactionIdStr,
+ });
+ },
+ );
+ });
+
+transactionsCli
+ .subcommand("resumeTransaction", "resume", {
+ help: "Resume a transaction.",
+ })
+ .requiredArgument("transactionId", clk.STRING, {
+ help: "Identifier of the transaction to suspend.",
+ })
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ await wallet.client.call(WalletApiOperation.ResumeTransaction, {
+ transactionId: args.resumeTransaction.transactionId as TransactionIdStr,
+ });
});
});
-walletCli
- .subcommand("transactions", "transactions", { help: "Show transactions." })
- .maybeOption("currency", ["--currency"], clk.STRING)
- .maybeOption("search", ["--search"], clk.STRING)
+transactionsCli
+ .subcommand("lookup", "lookup", {
+ help: "Look up a single transaction based on the transaction identifier.",
+ })
+ .requiredArgument("transactionId", clk.STRING, {
+ help: "Identifier of the transaction to delete",
+ })
.action(async (args) => {
- await withWallet(args, async (wallet) => {
- const pending = await wallet.client.call(
- WalletApiOperation.GetTransactions,
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const tx = await wallet.client.call(
+ WalletApiOperation.GetTransactionById,
{
- currency: args.transactions.currency,
- search: args.transactions.search,
+ transactionId: args.lookup.transactionId,
},
);
- console.log(JSON.stringify(pending, undefined, 2));
+ console.log(j2s(tx));
});
});
-async function asyncSleep(milliSeconds: number): Promise<void> {
- return new Promise<void>((resolve, reject) => {
- setTimeout(() => resolve(), milliSeconds);
+transactionsCli
+ .subcommand("abortTransaction", "abort", {
+ help: "Abort a transaction.",
+ })
+ .requiredArgument("transactionId", clk.STRING, {
+ help: "Identifier of the transaction to delete",
+ })
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ await wallet.client.call(WalletApiOperation.AbortTransaction, {
+ transactionId: args.abortTransaction.transactionId as TransactionIdStr,
+ });
+ });
});
-}
walletCli
- .subcommand("runPendingOpt", "run-pending", {
- help: "Run pending operations.",
+ .subcommand("version", "version", {
+ help: "Show version details.",
})
- .flag("forceNow", ["-f", "--force-now"])
.action(async (args) => {
- await withWallet(args, async (wallet) => {
- await wallet.ws.runPending(args.runPendingOpt.forceNow);
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const versionInfo = await wallet.client.call(
+ WalletApiOperation.GetVersion,
+ {},
+ );
+ console.log(j2s(versionInfo));
});
});
-walletCli
- .subcommand("retryTransaction", "retry-transaction", {
+transactionsCli
+ .subcommand("retryTransaction", "retry", {
help: "Retry a transaction.",
})
.requiredArgument("transactionId", clk.STRING)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
await wallet.client.call(WalletApiOperation.RetryTransaction, {
- transactionId: args.retryTransaction.transactionId,
+ transactionId: args.retryTransaction.transactionId as TransactionIdStr,
});
});
});
@@ -319,29 +607,76 @@ walletCli
.subcommand("finishPendingOpt", "run-until-done", {
help: "Run until no more work is left.",
})
- .maybeOption("maxRetries", ["--max-retries"], clk.INT)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
- await wallet.ws.runTaskLoop({
- maxRetries: args.finishPendingOpt.maxRetries,
- stopWhenDone: true,
- });
- wallet.ws.stop();
+ await withWallet(args, { lazyTaskLoop: false }, async (ctx) => {
+ await ctx.client.call(WalletApiOperation.TestingWaitTasksDone, {});
});
});
-walletCli
- .subcommand("deleteTransaction", "delete-transaction", {
- help: "Permanently delete a transaction from the transaction list.",
- })
- .requiredArgument("transactionId", clk.STRING, {
- help: "Identifier of the transaction to delete",
- })
+const withdrawCli = walletCli.subcommand("withdraw", "withdraw", {
+ help: "Withdraw with a taler://withdraw/ URI",
+});
+
+withdrawCli
+ .subcommand("withdrawCheckUri", "check-uri")
+ .requiredArgument("uri", clk.STRING)
+ .maybeOption("restrictAge", ["--restrict-age"], clk.INT)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
- await wallet.client.call(WalletApiOperation.DeleteTransaction, {
- transactionId: args.deleteTransaction.transactionId,
- });
+ const uri = args.withdrawCheckUri.uri;
+ const restrictAge = args.withdrawCheckUri.restrictAge;
+ console.log(`age restriction requested (${restrictAge})`);
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const withdrawInfo = await wallet.client.call(
+ WalletApiOperation.GetWithdrawalDetailsForUri,
+ {
+ talerWithdrawUri: uri,
+ restrictAge,
+ },
+ );
+ console.log("withdrawInfo", withdrawInfo);
+ });
+ });
+
+withdrawCli
+ .subcommand("withdrawCheckAmount", "check-amount")
+ .requiredArgument("exchange", clk.STRING)
+ .requiredArgument("amount", clk.AMOUNT)
+ .maybeOption("restrictAge", ["--restrict-age"], clk.INT)
+ .action(async (args) => {
+ const restrictAge = args.withdrawCheckAmount.restrictAge;
+ console.log(`age restriction requested (${restrictAge})`);
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const withdrawInfo = await wallet.client.call(
+ WalletApiOperation.GetWithdrawalDetailsForAmount,
+ {
+ amount: args.withdrawCheckAmount.amount,
+ exchangeBaseUrl: args.withdrawCheckAmount.exchange,
+ restrictAge,
+ },
+ );
+ console.log("withdrawInfo", withdrawInfo);
+ });
+ });
+
+withdrawCli
+ .subcommand("withdrawAcceptUri", "accept-uri")
+ .requiredArgument("uri", clk.STRING)
+ .requiredOption("exchange", ["--exchange"], clk.STRING)
+ .maybeOption("restrictAge", ["--restrict-age"], clk.INT)
+ .action(async (args) => {
+ const uri = args.withdrawAcceptUri.uri;
+ const restrictAge = args.withdrawAcceptUri.restrictAge;
+ console.log(`age restriction requested (${restrictAge})`);
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const res = await wallet.client.call(
+ WalletApiOperation.AcceptBankIntegratedWithdrawal,
+ {
+ exchangeBaseUrl: args.withdrawAcceptUri.exchange,
+ talerWithdrawUri: uri,
+ restrictAge,
+ },
+ );
+ console.log(j2s(res));
});
});
@@ -349,69 +684,125 @@ walletCli
.subcommand("handleUri", "handle-uri", {
help: "Handle a taler:// URI.",
})
- .requiredArgument("uri", clk.STRING)
+ .maybeArgument("uri", clk.STRING)
+ .maybeOption("withdrawalExchange", ["--withdrawal-exchange"], clk.STRING, {
+ help: "Exchange to use for withdrawal operations.",
+ })
+ .maybeOption("restrictAge", ["--restrict-age"], clk.INT)
.flag("autoYes", ["-y", "--yes"])
.action(async (args) => {
- await withWallet(args, async (wallet) => {
- const uri: string = args.handleUri.uri;
- const uriType = classifyTalerUri(uri);
- switch (uriType) {
- case TalerUriType.TalerPay:
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ let uri;
+ if (args.handleUri.uri) {
+ uri = args.handleUri.uri;
+ } else {
+ uri = await readlinePrompt("Taler URI: ");
+ }
+ const parsedTalerUri = parseTalerUri(uri);
+ if (!parsedTalerUri) {
+ throw Error("invalid taler URI");
+ }
+ switch (parsedTalerUri.type) {
+ case TalerUriAction.Pay:
await doPay(wallet.client, uri, {
alwaysYes: args.handleUri.autoYes,
});
break;
- case TalerUriType.TalerTip:
- {
- const res = await wallet.client.call(
- WalletApiOperation.PrepareTip,
- {
- talerTipUri: uri,
- },
- );
- console.log("tip status", res);
- await wallet.client.call(WalletApiOperation.AcceptTip, {
- walletTipId: res.walletTipId,
- });
- }
- break;
- case TalerUriType.TalerRefund:
- await wallet.client.call(WalletApiOperation.ApplyRefund, {
+ case TalerUriAction.Refund:
+ await wallet.client.call(WalletApiOperation.StartRefundQueryForUri, {
talerRefundUri: uri,
});
break;
- case TalerUriType.TalerWithdraw:
- {
- const withdrawInfo = await wallet.client.call(
- WalletApiOperation.GetWithdrawalDetailsForUri,
- {
- talerWithdrawUri: uri,
- },
- );
- console.log("withdrawInfo", withdrawInfo);
- const selectedExchange = withdrawInfo.defaultExchangeBaseUrl;
- if (!selectedExchange) {
- console.error("no suggested exchange!");
- process.exit(1);
- return;
- }
- const res = await wallet.client.call(
- WalletApiOperation.AcceptBankIntegratedWithdrawal,
- {
- exchangeBaseUrl: selectedExchange,
- talerWithdrawUri: uri,
- },
+ case TalerUriAction.Withdraw: {
+ const withdrawInfo = await wallet.client.call(
+ WalletApiOperation.GetWithdrawalDetailsForUri,
+ {
+ talerWithdrawUri: uri,
+ restrictAge: args.handleUri.restrictAge,
+ },
+ );
+ console.log("withdrawInfo", withdrawInfo);
+ const selectedExchange =
+ args.handleUri.withdrawalExchange ??
+ withdrawInfo.defaultExchangeBaseUrl;
+ if (!selectedExchange) {
+ console.error(
+ "no exchange specified for withdrawal (and no exchange suggested by the bank)",
);
+ processExit(1);
+ return;
}
+ const res = await wallet.client.call(
+ WalletApiOperation.AcceptBankIntegratedWithdrawal,
+ {
+ exchangeBaseUrl: selectedExchange,
+ talerWithdrawUri: uri,
+ },
+ );
+ console.log("accept withdrawal response", res);
+ break;
+ }
+ case TalerUriAction.DevExperiment: {
+ await wallet.client.call(WalletApiOperation.ApplyDevExperiment, {
+ devExperimentUri: uri,
+ });
break;
+ }
default:
- console.log(`URI type (${uriType}) not handled`);
+ console.log(`URI type (${parsedTalerUri.type}) not handled`);
break;
}
return;
});
});
+withdrawCli
+ .subcommand("withdrawManually", "manual", {
+ help: "Withdraw manually from an exchange.",
+ })
+ .requiredOption("exchange", ["--exchange"], clk.STRING, {
+ help: "Base URL of the exchange.",
+ })
+ .requiredOption("amount", ["--amount"], clk.AMOUNT, {
+ help: "Amount to withdraw",
+ })
+ .maybeOption("forcedReservePriv", ["--forced-reserve-priv"], clk.STRING, {})
+ .maybeOption("restrictAge", ["--restrict-age"], clk.INT)
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const exchangeBaseUrl = args.withdrawManually.exchange;
+ const amount = args.withdrawManually.amount;
+ const d = await wallet.client.call(
+ WalletApiOperation.GetWithdrawalDetailsForAmount,
+ {
+ amount: args.withdrawManually.amount,
+ exchangeBaseUrl: exchangeBaseUrl,
+ },
+ );
+ const acct = d.withdrawalAccountsList[0];
+ if (!acct) {
+ console.log("exchange has no accounts");
+ return;
+ }
+ const resp = await wallet.client.call(
+ WalletApiOperation.AcceptManualWithdrawal,
+ {
+ amount,
+ exchangeBaseUrl,
+ restrictAge: parseInt(String(args.withdrawManually.restrictAge), 10),
+ forceReservePriv: args.withdrawManually.forcedReservePriv,
+ },
+ );
+ const reservePub = resp.reservePub;
+ const completePaytoUri = addPaytoQueryParams(acct.paytoUri, {
+ amount: args.withdrawManually.amount,
+ message: `Taler top-up ${reservePub}`,
+ });
+ console.log("Created reserve", reservePub);
+ console.log("Payto URI", completePaytoUri);
+ });
+ });
+
const exchangesCli = walletCli.subcommand("exchangesCmd", "exchanges", {
help: "Manage exchanges.",
});
@@ -422,7 +813,7 @@ exchangesCli
})
.action(async (args) => {
console.log("Listing exchanges ...");
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
const exchanges = await wallet.client.call(
WalletApiOperation.ListExchanges,
{},
@@ -440,15 +831,34 @@ exchangesCli
})
.flag("force", ["-f", "--force"])
.action(async (args) => {
- await withWallet(args, async (wallet) => {
- await wallet.client.call(WalletApiOperation.AddExchange, {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ await wallet.client.call(WalletApiOperation.UpdateExchangeEntry, {
exchangeBaseUrl: args.exchangesUpdateCmd.url,
- forceUpdate: args.exchangesUpdateCmd.force,
+ force: args.exchangesUpdateCmd.force,
});
});
});
exchangesCli
+ .subcommand("exchangesShowCmd", "show", {
+ help: "Show exchange details",
+ })
+ .requiredArgument("url", clk.STRING, {
+ help: "Base URL of the exchange.",
+ })
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.GetExchangeDetailedInfo,
+ {
+ exchangeBaseUrl: args.exchangesShowCmd.url,
+ },
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ });
+ });
+
+exchangesCli
.subcommand("exchangesAddCmd", "add", {
help: "Add an exchange by base URL.",
})
@@ -456,7 +866,7 @@ exchangesCli
help: "Base URL of the exchange.",
})
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
await wallet.client.call(WalletApiOperation.AddExchange, {
exchangeBaseUrl: args.exchangesAddCmd.url,
});
@@ -464,19 +874,32 @@ exchangesCli
});
exchangesCli
+ .subcommand("exchangesAddCmd", "delete", {
+ help: "Delete an exchange by base URL.",
+ })
+ .requiredArgument("url", clk.STRING, {
+ help: "Base URL of the exchange.",
+ })
+ .flag("purge", ["--purge"])
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ await wallet.client.call(WalletApiOperation.DeleteExchange, {
+ exchangeBaseUrl: args.exchangesAddCmd.url,
+ purge: args.exchangesAddCmd.purge,
+ });
+ });
+ });
+
+exchangesCli
.subcommand("exchangesAcceptTosCmd", "accept-tos", {
help: "Accept terms of service.",
})
.requiredArgument("url", clk.STRING, {
help: "Base URL of the exchange.",
})
- .requiredArgument("etag", clk.STRING, {
- help: "ToS version tag to accept",
- })
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
await wallet.client.call(WalletApiOperation.SetExchangeTosAccepted, {
- etag: args.exchangesAcceptTosCmd.etag,
exchangeBaseUrl: args.exchangesAcceptTosCmd.url,
});
});
@@ -484,17 +907,26 @@ exchangesCli
exchangesCli
.subcommand("exchangesTosCmd", "tos", {
- help: "Show terms of service.",
+ help: "Show/request terms of service.",
})
.requiredArgument("url", clk.STRING, {
help: "Base URL of the exchange.",
})
+ .maybeOption("contentTypes", ["--content-type"], clk.STRING)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ let acceptedFormat: string[] | undefined = undefined;
+ if (args.exchangesTosCmd.contentTypes) {
+ const split = args.exchangesTosCmd.contentTypes
+ .split(",")
+ .map((x) => x.trim());
+ acceptedFormat = split;
+ }
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
const tosResult = await wallet.client.call(
WalletApiOperation.GetExchangeTos,
{
exchangeBaseUrl: args.exchangesTosCmd.url,
+ acceptedFormat,
},
);
console.log(JSON.stringify(tosResult, undefined, 2));
@@ -505,107 +937,83 @@ const backupCli = walletCli.subcommand("backupArgs", "backup", {
help: "Subcommands for backups",
});
-backupCli
- .subcommand("setDeviceId", "set-device-id")
- .requiredArgument("deviceId", clk.STRING, {
- help: "new device ID",
- })
- .action(async (args) => {
- await withWallet(args, async (wallet) => {
- await wallet.client.call(WalletApiOperation.SetWalletDeviceId, {
- walletDeviceId: args.setDeviceId.deviceId,
- });
- });
- });
-
-backupCli.subcommand("exportPlain", "export-plain").action(async (args) => {
- await withWallet(args, async (wallet) => {
- const backup = await wallet.client.call(
- WalletApiOperation.ExportBackupPlain,
- {},
- );
+backupCli.subcommand("exportDb", "export-db").action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const backup = await wallet.client.call(WalletApiOperation.ExportDb, {});
console.log(JSON.stringify(backup, undefined, 2));
});
});
-backupCli.subcommand("recoverySave", "save-recovery").action(async (args) => {
- await withWallet(args, async (wallet) => {
- const recoveryJson = await wallet.client.call(
- WalletApiOperation.ExportBackupRecovery,
+backupCli.subcommand("storeBackup", "store").action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.CreateStoredBackup,
{},
);
- console.log(JSON.stringify(recoveryJson, undefined, 2));
+ console.log(JSON.stringify(resp, undefined, 2));
});
});
-backupCli.subcommand("run", "run").action(async (args) => {
- await withWallet(args, async (wallet) => {
- await wallet.client.call(WalletApiOperation.RunBackupCycle, {});
- });
-});
-
-backupCli.subcommand("status", "status").action(async (args) => {
- await withWallet(args, async (wallet) => {
- const status = await wallet.client.call(
- WalletApiOperation.GetBackupInfo,
+backupCli.subcommand("storeBackup", "list-stored").action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.ListStoredBackups,
{},
);
- console.log(JSON.stringify(status, undefined, 2));
+ console.log(JSON.stringify(resp, undefined, 2));
});
});
backupCli
- .subcommand("recoveryLoad", "load-recovery")
- .maybeOption("strategy", ["--strategy"], clk.STRING, {
- help:
- "Strategy for resolving a conflict with the existing wallet key ('theirs' or 'ours')",
- })
+ .subcommand("storeBackup", "delete-stored")
+ .requiredArgument("name", clk.STRING)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
- const data = JSON.parse(await read(process.stdin));
- let strategy: RecoveryMergeStrategy | undefined;
- const stratStr = args.recoveryLoad.strategy;
- if (stratStr) {
- if (stratStr === "theirs") {
- strategy = RecoveryMergeStrategy.Theirs;
- } else if (stratStr === "ours") {
- strategy = RecoveryMergeStrategy.Theirs;
- } else {
- throw Error("invalid recovery strategy");
- }
- }
- await wallet.client.call(WalletApiOperation.ImportBackupRecovery, {
- recovery: data,
- strategy,
- });
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.DeleteStoredBackup,
+ {
+ name: args.storeBackup.name,
+ },
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
});
});
backupCli
- .subcommand("addProvider", "add-provider")
- .requiredArgument("url", clk.STRING)
- .maybeArgument("name", clk.STRING)
- .flag("activate", ["--activate"])
+ .subcommand("recoverBackup", "recover-stored")
+ .requiredArgument("name", clk.STRING)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
- await wallet.client.call(WalletApiOperation.AddBackupProvider, {
- backupProviderBaseUrl: args.addProvider.url,
- activate: args.addProvider.activate,
- name: args.addProvider.name || args.addProvider.url,
- });
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.RecoverStoredBackup,
+ {
+ name: args.recoverBackup.name,
+ },
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
});
});
+backupCli.subcommand("importDb", "import-db").action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const dumpRaw = await read(process.stdin);
+ const dump = JSON.parse(dumpRaw);
+ await wallet.client.call(WalletApiOperation.ImportDb, {
+ dump,
+ });
+ });
+});
+
const depositCli = walletCli.subcommand("depositArgs", "deposit", {
help: "Subcommands for depositing money to payto:// accounts",
});
depositCli
.subcommand("createDepositArgs", "create")
- .requiredArgument("amount", clk.STRING)
+ .requiredArgument("amount", clk.AMOUNT)
.requiredArgument("targetPayto", clk.STRING)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
const resp = await wallet.client.call(
WalletApiOperation.CreateDepositGroup,
{
@@ -614,19 +1022,190 @@ depositCli
},
);
console.log(`Created deposit ${resp.depositGroupId}`);
- await wallet.ws.runPending();
});
});
-depositCli
- .subcommand("trackDepositArgs", "track")
- .requiredArgument("depositGroupId", clk.STRING)
+const peerCli = walletCli.subcommand("peerArgs", "p2p", {
+ help: "Subcommands for peer-to-peer payments.",
+});
+
+peerCli
+ .subcommand("checkPayPush", "check-push-debit", {
+ help: "Check fees for starting a peer-push debit transaction.",
+ })
+ .requiredArgument("amount", clk.AMOUNT, {
+ help: "Amount to pay",
+ })
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
const resp = await wallet.client.call(
- WalletApiOperation.TrackDepositGroup,
+ WalletApiOperation.CheckPeerPushDebit,
{
- depositGroupId: args.trackDepositArgs.depositGroupId,
+ amount: args.checkPayPush.amount,
+ },
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ });
+ });
+
+peerCli
+ .subcommand("checkPayPull", "check-pull-credit", {
+ help: "Check fees for a starting peer-pull credit transaction.",
+ })
+ .requiredArgument("amount", clk.AMOUNT, {
+ help: "Amount to request",
+ })
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.CheckPeerPullCredit,
+ {
+ amount: args.checkPayPull.amount,
+ },
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ });
+ });
+
+peerCli
+ .subcommand("prepareIncomingPayPull", "prepare-pull-debit")
+ .requiredArgument("talerUri", clk.STRING)
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.PreparePeerPullDebit,
+ {
+ talerUri: args.prepareIncomingPayPull.talerUri,
+ },
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ });
+ });
+
+peerCli
+ .subcommand("confirmIncomingPayPull", "confirm-pull-debit")
+ .requiredArgument("transactionId", clk.STRING)
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.ConfirmPeerPullDebit,
+ {
+ transactionId: args.confirmIncomingPayPull
+ .transactionId as TransactionIdStr,
+ },
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ });
+ });
+
+peerCli
+ .subcommand("confirmIncomingPayPush", "confirm-push-credit")
+ .requiredArgument("transactionId", clk.STRING)
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.ConfirmPeerPushCredit,
+ {
+ transactionId: args.confirmIncomingPayPush.transactionId,
+ },
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ });
+ });
+
+peerCli
+ .subcommand("initiatePayPull", "initiate-pull-credit", {
+ help: "Initiate a peer-pull payment.",
+ })
+ .requiredArgument("amount", clk.AMOUNT, {
+ help: "Amount to request",
+ })
+ .maybeOption("summary", ["--summary"], clk.STRING, {
+ help: "Summary to use in the contract terms.",
+ })
+ .maybeOption("purseExpiration", ["--purse-expiration"], clk.STRING)
+ .maybeOption("exchangeBaseUrl", ["--exchange"], clk.STRING)
+ .action(async (args) => {
+ let purseExpiration: AbsoluteTime;
+
+ if (args.initiatePayPull.purseExpiration) {
+ purseExpiration = AbsoluteTime.addDuration(
+ AbsoluteTime.now(),
+ Duration.fromPrettyString(args.initiatePayPull.purseExpiration),
+ );
+ } else {
+ purseExpiration = AbsoluteTime.addDuration(
+ AbsoluteTime.now(),
+ Duration.fromSpec({ hours: 1 }),
+ );
+ }
+
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.InitiatePeerPullCredit,
+ {
+ exchangeBaseUrl: args.initiatePayPull.exchangeBaseUrl,
+ partialContractTerms: {
+ amount: args.initiatePayPull.amount,
+ summary: args.initiatePayPull.summary ?? "Invoice",
+ purse_expiration: AbsoluteTime.toProtocolTimestamp(purseExpiration),
+ },
+ },
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ });
+ });
+
+peerCli
+ .subcommand("preparePushCredit", "prepare-push-credit")
+ .requiredArgument("talerUri", clk.STRING)
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.PreparePeerPushCredit,
+ {
+ talerUri: args.preparePushCredit.talerUri,
+ },
+ );
+ console.log(JSON.stringify(resp, undefined, 2));
+ });
+ });
+
+peerCli
+ .subcommand("payPush", "initiate-push-debit", {
+ help: "Initiate a peer-push payment.",
+ })
+ .requiredArgument("amount", clk.AMOUNT, {
+ help: "Amount to pay",
+ })
+ .maybeOption("summary", ["--summary"], clk.STRING, {
+ help: "Summary to use in the contract terms.",
+ })
+ .maybeOption("purseExpiration", ["--purse-expiration"], clk.STRING)
+ .action(async (args) => {
+ let purseExpiration: AbsoluteTime;
+
+ if (args.payPush.purseExpiration) {
+ purseExpiration = AbsoluteTime.addDuration(
+ AbsoluteTime.now(),
+ Duration.fromPrettyString(args.payPush.purseExpiration),
+ );
+ } else {
+ purseExpiration = AbsoluteTime.addDuration(
+ AbsoluteTime.now(),
+ Duration.fromSpec({ hours: 1 }),
+ );
+ }
+
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const resp = await wallet.client.call(
+ WalletApiOperation.InitiatePeerPushDebit,
+ {
+ partialContractTerms: {
+ amount: args.payPush.amount,
+ summary: args.payPush.summary ?? "Payment",
+ purse_expiration: AbsoluteTime.toProtocolTimestamp(purseExpiration),
+ },
},
);
console.log(JSON.stringify(resp, undefined, 2));
@@ -634,154 +1213,333 @@ depositCli
});
const advancedCli = walletCli.subcommand("advancedArgs", "advanced", {
- help:
- "Subcommands for advanced operations (only use if you know what you're doing!).",
+ help: "Subcommands for advanced operations (only use if you know what you're doing!).",
});
advancedCli
- .subcommand("bench1", "bench1", {
- help: "Run the 'bench1' benchmark",
+ .subcommand("genReserve", "gen-reserve", {
+ help: "Generate a reserve key pair (not stored in the DB).",
})
- .requiredOption("configJson", ["--config-json"], clk.STRING)
.action(async (args) => {
- let config: any;
- try {
- config = JSON.parse(args.bench1.configJson);
- } catch (e) {
- console.log("Could not parse config JSON");
- }
- await runBench1(config);
+ const pair = await nativeCrypto.createEddsaKeypair({});
+ console.log(
+ j2s({
+ reservePub: pair.pub,
+ reservePriv: pair.priv,
+ }),
+ );
});
advancedCli
- .subcommand("env1", "env1", {
- help: "Run a test environment for bench1",
+ .subcommand("tasks", "tasks", {
+ help: "Show active wallet-core tasks.",
})
.action(async (args) => {
- const testDir = fs.mkdtempSync(path.join(os.tmpdir(), "taler-env1-"));
- const testState = new GlobalTestState({
- testDir,
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const tasks = await wallet.client.call(
+ WalletApiOperation.GetActiveTasks,
+ {},
+ );
+ console.log(j2s(tasks));
});
- await runTestWithState(testState, runEnv1, "env1", true);
});
advancedCli
- .subcommand("withdrawFakebank", "withdraw-fakebank", {
- help: "Withdraw via a fakebank.",
+ .subcommand("sampleTransactions", "sample-transactions", {
+ help: "Print sample wallet-core transactions",
})
- .requiredOption("exchange", ["--exchange"], clk.STRING, {
- help: "Base URL of the exchange to use",
+ .action(async (args) => {
+ console.log(JSON.stringify(sampleWalletCoreTransactions, undefined, 2));
+ });
+
+advancedCli
+ .subcommand("serve", "serve", {
+ help: "Serve the wallet API via a unix domain socket.",
})
- .requiredOption("amount", ["--amount"], clk.STRING, {
- help: "Amount to withdraw (before fees).",
+ .requiredOption("unixPath", ["--unix-path"], clk.STRING, {
+ default: defaultWalletCoreSocket,
})
- .requiredOption("bank", ["--bank"], clk.STRING, {
- help: "Base URL of the Taler fakebank service.",
+ .flag("noInit", ["--no-init"], {
+ help: "Do not initialize the wallet. The client must send the initWallet message.",
})
.action(async (args) => {
- await withWallet(args, async (wallet) => {
- await wallet.client.call(WalletApiOperation.WithdrawFakebank, {
- amount: args.withdrawFakebank.amount,
- bank: args.withdrawFakebank.bank,
- exchange: args.withdrawFakebank.exchange,
+ const socketPath = args.serve.unixPath;
+ logger.info(`serving at ${socketPath}`);
+ let cleanupCalled = false;
+
+ const cleanupSocket = (signal: string, code: number) => {
+ if (cleanupCalled) {
+ return;
+ }
+ cleanupCalled = true;
+ try {
+ logger.info("cleaning up socket");
+ fs.unlinkSync(socketPath);
+ } catch (e) {
+ logger.warn(`unable to clean up socket: ${e}`);
+ }
+ process.exit(128 + code);
+ };
+ process.on("SIGTERM", cleanupSocket);
+ process.on("SIGINT", cleanupSocket);
+
+ const onNotif = (notif: WalletNotification) => {
+ writeObservabilityLog(notif);
+ };
+ const wh = await createLocalWallet(
+ args,
+ { lazyTaskLoop: false, noInit: args.serve.noInit },
+ onNotif,
+ );
+ const w = wh.wallet;
+ let nextClientId = 1;
+ const notifyHandlers = new Map<number, (n: WalletNotification) => void>();
+ w.addNotificationListener((n) => {
+ notifyHandlers.forEach((v, k) => {
+ v(n);
});
});
+ await runRpcServer({
+ socketFilename: args.serve.unixPath,
+ onConnect(client) {
+ logger.info("connected");
+ const clientId = nextClientId++;
+ notifyHandlers.set(clientId, (n: WalletNotification) => {
+ client.sendResponse({
+ type: "notification",
+ payload: n as unknown as JsonMessage,
+ });
+ });
+ return {
+ onDisconnect() {
+ notifyHandlers.delete(clientId);
+ logger.info("disconnected");
+ },
+ onMessage(msg) {
+ logger.info(`message: ${j2s(msg)}`);
+ const op = (msg as any).operation;
+ const id = (msg as any).id;
+ const payload = (msg as any).args;
+ w.handleCoreApiRequest(op, id, payload)
+ .then((resp) => {
+ logger.info("sending response");
+ client.sendResponse(resp as unknown as JsonMessage);
+ })
+ .catch((e) => {
+ logger.error(`unexpected error: ${e}`);
+ });
+ },
+ };
+ },
+ });
});
advancedCli
- .subcommand("manualWithdrawalDetails", "manual-withdrawal-details", {
- help: "Query withdrawal fees.",
+ .subcommand("init", "init", {
+ help: "Initialize the wallet (with DB) and exit.",
})
- .requiredArgument("exchange", clk.STRING)
- .requiredArgument("amount", clk.STRING)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
- const details = await wallet.client.call(
- WalletApiOperation.GetWithdrawalDetailsForAmount,
- {
- amount: args.manualWithdrawalDetails.amount,
- exchangeBaseUrl: args.manualWithdrawalDetails.exchange,
- },
+ await withWallet(args, { lazyTaskLoop: true }, async () => {});
+ });
+
+advancedCli
+ .subcommand("runPendingOpt", "run-pending", {
+ help: "Run pending operations.",
+ })
+ .action(async (args) => {
+ logger.error(
+ "Subcommand run-pending not supported anymore. Please use run-until-done or the client/server wallet.",
+ );
+ });
+
+advancedCli
+ .subcommand("pending", "pending", { help: "Show pending operations." })
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const pending = await wallet.client.call(
+ WalletApiOperation.GetPendingOperations,
+ {},
);
- console.log(JSON.stringify(details, undefined, 2));
+ console.log(JSON.stringify(pending, undefined, 2));
});
});
advancedCli
- .subcommand("decode", "decode", {
- help: "Decode base32-crockford.",
+ .subcommand("benchInternal", "bench-internal", {
+ help: "Run the 'bench-internal' benchmark",
})
- .action((args) => {
- const enc = fs.readFileSync(0, "utf8");
- fs.writeFileSync(1, decodeCrock(enc.trim()));
+ .action(async (args) => {
+ const myHttpLib = createPlatformHttpLib();
+ const res = await createNativeWalletHost2({
+ // No persistent DB storage.
+ persistentStoragePath: undefined,
+ httpLib: myHttpLib,
+ });
+ const wallet = res.wallet;
+ await wallet.client.call(WalletApiOperation.InitWallet, {});
+ await wallet.client.call(WalletApiOperation.RunIntegrationTest, {
+ amountToSpend: "TESTKUDOS:1" as AmountString,
+ amountToWithdraw: "TESTKUDOS:3" as AmountString,
+ corebankApiBaseUrl: "http://localhost:8082/taler-bank-access/",
+ exchangeBaseUrl: "http://localhost:8081/",
+ merchantBaseUrl: "http://localhost:8083/",
+ });
+ await wallet.client.call(WalletApiOperation.TestingWaitTasksDone, {});
+ await wallet.client.call(WalletApiOperation.Shutdown, {});
});
advancedCli
- .subcommand("withdrawManually", "withdraw-manually", {
- help: "Withdraw manually from an exchange.",
+ .subcommand("genSegwit", "gen-segwit")
+ .requiredArgument("paytoUri", clk.STRING)
+ .requiredArgument("reservePub", clk.STRING)
+ .action(async (args) => {
+ const p = parsePaytoUri(args.genSegwit.paytoUri);
+ console.log(p);
+ });
+
+const currenciesCli = walletCli.subcommand("currencies", "currencies", {
+ help: "Manage currencies.",
+});
+
+currenciesCli
+ .subcommand("listGlobalAuditors", "list-global-auditors", {
+ help: "List global-currency auditors.",
})
- .requiredOption("exchange", ["--exchange"], clk.STRING, {
- help: "Base URL of the exchange.",
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const currencies = await wallet.client.call(
+ WalletApiOperation.ListGlobalCurrencyAuditors,
+ {},
+ );
+ console.log(JSON.stringify(currencies, undefined, 2));
+ });
+ });
+
+currenciesCli
+ .subcommand("listGlobalExchanges", "list-global-exchanges", {
+ help: "List global-currency exchanges.",
})
- .requiredOption("amount", ["--amount"], clk.STRING, {
- help: "Amount to withdraw",
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const currencies = await wallet.client.call(
+ WalletApiOperation.ListGlobalCurrencyExchanges,
+ {},
+ );
+ console.log(JSON.stringify(currencies, undefined, 2));
+ });
+ });
+
+currenciesCli
+ .subcommand("addGlobalExchange", "add-global-exchange", {
+ help: "Add a global-currency exchange.",
})
+ .requiredOption("currency", ["--currency"], clk.STRING)
+ .requiredOption("exchangeBaseUrl", ["--url"], clk.STRING)
+ .requiredOption("exchangePub", ["--pub"], clk.STRING)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
- const exchangeBaseUrl = args.withdrawManually.exchange;
- const amount = args.withdrawManually.amount;
- const d = await wallet.client.call(
- WalletApiOperation.GetWithdrawalDetailsForAmount,
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const currencies = await wallet.client.call(
+ WalletApiOperation.AddGlobalCurrencyExchange,
{
- amount: args.withdrawManually.amount,
- exchangeBaseUrl: exchangeBaseUrl,
+ currency: args.addGlobalExchange.currency,
+ exchangeBaseUrl: args.addGlobalExchange.exchangeBaseUrl,
+ exchangeMasterPub: args.addGlobalExchange.exchangePub,
},
);
- const acct = d.paytoUris[0];
- if (!acct) {
- console.log("exchange has no accounts");
- return;
- }
- const resp = await wallet.client.call(
- WalletApiOperation.AcceptManualWithdrawal,
+ console.log(JSON.stringify(currencies, undefined, 2));
+ });
+ });
+
+currenciesCli
+ .subcommand("removeGlobalExchange", "remove-global-exchange", {
+ help: "Remove a global-currency exchange.",
+ })
+ .requiredOption("currency", ["--currency"], clk.STRING)
+ .requiredOption("exchangeBaseUrl", ["--url"], clk.STRING)
+ .requiredOption("exchangePub", ["--pub"], clk.STRING)
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const currencies = await wallet.client.call(
+ WalletApiOperation.RemoveGlobalCurrencyExchange,
{
- amount,
- exchangeBaseUrl,
+ currency: args.removeGlobalExchange.currency,
+ exchangeBaseUrl: args.removeGlobalExchange.exchangeBaseUrl,
+ exchangeMasterPub: args.removeGlobalExchange.exchangePub,
},
);
- const reservePub = resp.reservePub;
- const completePaytoUri = addPaytoQueryParams(acct, {
- amount: args.withdrawManually.amount,
- message: `Taler top-up ${reservePub}`,
- });
- console.log("Created reserve", reservePub);
- console.log("Payto URI", completePaytoUri);
+ console.log(JSON.stringify(currencies, undefined, 2));
});
});
-const currenciesCli = walletCli.subcommand("currencies", "currencies", {
- help: "Manage currencies.",
-});
+currenciesCli
+ .subcommand("addGlobalAuditor", "add-global-auditor", {
+ help: "Add a global-currency auditor.",
+ })
+ .requiredOption("currency", ["--currency"], clk.STRING)
+ .requiredOption("auditorBaseUrl", ["--url"], clk.STRING)
+ .requiredOption("auditorPub", ["--pub"], clk.STRING)
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ const currencies = await wallet.client.call(
+ WalletApiOperation.AddGlobalCurrencyAuditor,
+ {
+ currency: args.addGlobalAuditor.currency,
+ auditorBaseUrl: args.addGlobalAuditor.auditorBaseUrl,
+ auditorPub: args.addGlobalAuditor.auditorPub,
+ },
+ );
+ console.log(JSON.stringify(currencies, undefined, 2));
+ });
+ });
currenciesCli
- .subcommand("show", "show", { help: "Show currencies." })
+ .subcommand("removeGlobalAuditor", "remove-global-auditor", {
+ help: "Remove a global-currency auditor.",
+ })
+ .requiredOption("currency", ["--currency"], clk.STRING)
+ .requiredOption("auditorBaseUrl", ["--url"], clk.STRING)
+ .requiredOption("auditorPub", ["--pub"], clk.STRING)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
const currencies = await wallet.client.call(
- WalletApiOperation.ListCurrencies,
- {},
+ WalletApiOperation.RemoveGlobalCurrencyAuditor,
+ {
+ currency: args.removeGlobalAuditor.currency,
+ auditorBaseUrl: args.removeGlobalAuditor.auditorBaseUrl,
+ auditorPub: args.removeGlobalAuditor.auditorPub,
+ },
);
console.log(JSON.stringify(currencies, undefined, 2));
});
});
advancedCli
+ .subcommand("clearDatabase", "clear-database", {
+ help: "Clear the database, irrevocable deleting all data in the wallet.",
+ })
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ await wallet.client.call(WalletApiOperation.ClearDb, {});
+ });
+ });
+
+advancedCli
+ .subcommand("recycle", "recycle", {
+ help: "Export, clear and re-import the database via the backup mechanism.",
+ })
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ await wallet.client.call(WalletApiOperation.Recycle, {});
+ });
+ });
+
+advancedCli
.subcommand("payPrepare", "pay-prepare", {
help: "Claim an order but don't pay yet.",
})
.requiredArgument("url", clk.STRING)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
const res = await wallet.client.call(
WalletApiOperation.PreparePayForUri,
{
@@ -809,13 +1567,26 @@ advancedCli
});
advancedCli
+ .subcommand("queryRefund", "query-refund", {
+ help: "Query refunds for a payment transaction.",
+ })
+ .requiredArgument("transactionId", clk.STRING)
+ .action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ await wallet.client.call(WalletApiOperation.StartRefundQuery, {
+ transactionId: args.queryRefund.transactionId as TransactionIdStr,
+ });
+ });
+ });
+
+advancedCli
.subcommand("payConfirm", "pay-confirm", {
help: "Confirm payment proposed by a merchant.",
})
.requiredArgument("proposalId", clk.STRING)
.maybeOption("sessionIdOverride", ["--session-id"], clk.STRING)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
await wallet.client.call(WalletApiOperation.ConfirmPay, {
proposalId: args.payConfirm.proposalId,
sessionId: args.payConfirm.sessionIdOverride,
@@ -829,9 +1600,13 @@ advancedCli
})
.requiredArgument("coinPub", clk.STRING)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
await wallet.client.call(WalletApiOperation.ForceRefresh, {
- coinPubList: [args.refresh.coinPub],
+ refreshCoinSpecs: [
+ {
+ coinPub: args.refresh.coinPub,
+ },
+ ],
});
});
});
@@ -841,7 +1616,7 @@ advancedCli
help: "Dump coins in an easy-to-process format.",
})
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
const coinDump = await wallet.client.call(
WalletApiOperation.DumpCoins,
{},
@@ -858,7 +1633,7 @@ advancedCli
})
.requiredArgument("coinPubSpec", clk.STRING)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
let coinPubList: string[];
try {
coinPubList = coinPubListCodec.decode(
@@ -866,7 +1641,7 @@ advancedCli
);
} catch (e: any) {
console.log("could not parse coin list:", e.message);
- process.exit(1);
+ processExit(1);
}
for (const c of coinPubList) {
await wallet.client.call(WalletApiOperation.SetCoinSuspended, {
@@ -883,7 +1658,7 @@ advancedCli
})
.requiredArgument("coinPubSpec", clk.STRING)
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
let coinPubList: string[];
try {
coinPubList = coinPubListCodec.decode(
@@ -891,7 +1666,7 @@ advancedCli
);
} catch (e: any) {
console.log("could not parse coin list:", e.message);
- process.exit(1);
+ processExit(1);
}
for (const c of coinPubList) {
await wallet.client.call(WalletApiOperation.SetCoinSuspended, {
@@ -907,216 +1682,170 @@ advancedCli
help: "List coins.",
})
.action(async (args) => {
- await withWallet(args, async (wallet) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
const coins = await wallet.client.call(WalletApiOperation.DumpCoins, {});
for (const coin of coins.coins) {
console.log(`coin ${coin.coin_pub}`);
console.log(` exchange ${coin.exchange_base_url}`);
console.log(` denomPubHash ${coin.denom_pub_hash}`);
- console.log(
- ` remaining amount ${Amounts.stringify(coin.remaining_value)}`,
- );
+ console.log(` status ${coin.coin_status}`);
}
});
});
-const deploymentCli = walletCli.subcommand("deploymentArgs", "deployment", {
- help: "Subcommands for handling GNU Taler deployments.",
-});
-
-deploymentCli
- .subcommand("lintExchange", "lint-exchange", {
- help: "Run checks on the exchange deployment.",
- })
- .flag("cont", ["--continue"], {
- help: "Continue after errors if possible",
- })
- .flag("debug", ["--debug"], {
- help: "Output extra debug info",
- })
- .action(async (args) => {
- await lintExchangeDeployment(
- args.lintExchange.debug,
- args.lintExchange.cont,
- );
- });
-
-deploymentCli
- .subcommand("coincfg", "gen-coin-config", {
- help: "Generate a coin/denomination configuration for the exchange.",
- })
- .requiredOption("minAmount", ["--min-amount"], clk.STRING, {
- help: "Smallest denomination",
- })
- .requiredOption("maxAmount", ["--max-amount"], clk.STRING, {
- help: "Largest denomination",
- })
- .action(async (args) => {
- let out = "";
-
- const stamp = Math.floor(new Date().getTime() / 1000);
-
- const min = Amounts.parseOrThrow(args.coincfg.minAmount);
- const max = Amounts.parseOrThrow(args.coincfg.maxAmount);
- if (min.currency != max.currency) {
- console.error("currency mismatch");
- process.exit(1);
- }
- const currency = min.currency;
- let x = min;
- let n = 1;
-
- out += "# Coin configuration for the exchange.\n";
- out += '# Should be placed in "/etc/taler/conf.d/exchange-coins.conf".\n';
- out += "\n";
-
- while (Amounts.cmp(x, max) < 0) {
- out += `[COIN-${currency}-n${n}-t${stamp}]\n`;
- out += `VALUE = ${Amounts.stringify(x)}\n`;
- out += `DURATION_WITHDRAW = 7 days\n`;
- out += `DURATION_SPEND = 2 years\n`;
- out += `DURATION_LEGAL = 6 years\n`;
- out += `FEE_WITHDRAW = ${currency}:0\n`;
- out += `FEE_DEPOSIT = ${Amounts.stringify(min)}\n`;
- out += `FEE_REFRESH = ${currency}:0\n`;
- out += `FEE_REFUND = ${currency}:0\n`;
- out += `RSA_KEYSIZE = 2048\n`;
- out += "\n";
- x = Amounts.add(x, x).amount;
- n++;
- }
-
- console.log(out);
- });
-
-const deploymentConfigCli = deploymentCli.subcommand("configArgs", "config", {
- help: "Subcommands the Taler configuration.",
-});
-
-deploymentConfigCli
- .subcommand("show", "show")
- .flag("diagnostics", ["-d", "--diagnostics"])
- .maybeArgument("cfgfile", clk.STRING, {})
- .action(async (args) => {
- const cfg = Configuration.load(args.show.cfgfile);
- console.log(
- cfg.stringify({
- diagnostics: args.show.diagnostics,
- }),
- );
- });
-
const testCli = walletCli.subcommand("testingArgs", "testing", {
help: "Subcommands for testing.",
});
testCli
- .subcommand("listIntegrationtests", "list-integrationtests")
+ .subcommand("withdrawTestkudos", "withdraw-testkudos")
.action(async (args) => {
- for (const t of getTestInfo()) {
- let s = t.name;
- if (t.suites.length > 0) {
- s += ` (suites: ${t.suites.join(",")})`;
- }
- if (t.excludeByDefault) {
- s += ` [excluded by default]`;
- }
- console.log(s);
- }
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ await wallet.client.call(WalletApiOperation.WithdrawTestkudos, {});
+ });
});
-testCli
- .subcommand("runIntegrationtests", "run-integrationtests")
- .maybeArgument("pattern", clk.STRING, {
- help: "Glob pattern to select which tests to run",
- })
- .maybeOption("suites", ["--suites"], clk.STRING, {
- help: "Only run selected suites (comma-separated list)",
- })
- .flag("dryRun", ["--dry"], {
- help: "Only print tests that will be selected to run.",
- })
- .flag("quiet", ["--quiet"], {
- help: "Produce less output.",
- })
- .action(async (args) => {
- await runTests({
- includePattern: args.runIntegrationtests.pattern,
- suiteSpec: args.runIntegrationtests.suites,
- dryRun: args.runIntegrationtests.dryRun,
- verbosity: args.runIntegrationtests.quiet ? 0 : 1,
+testCli.subcommand("withdrawKudos", "withdraw-kudos").action(async (args) => {
+ await withWallet(args, { lazyTaskLoop: true }, async (wallet) => {
+ await wallet.client.call(WalletApiOperation.WithdrawTestBalance, {
+ amount: "KUDOS:50" as AmountString,
+ corebankApiBaseUrl: "https://bank.demo.taler.net/",
+ exchangeBaseUrl: "https://exchange.demo.taler.net/",
});
});
+});
-async function read(stream: NodeJS.ReadStream) {
- const chunks = [];
- for await (const chunk of stream) chunks.push(chunk);
- return Buffer.concat(chunks).toString("utf8");
-}
+class PerfTimer {
+ tStarted: bigint | undefined;
+ tSum = BigInt(0);
+ tSumSq = BigInt(0);
-testCli.subcommand("tvgcheck", "tvgcheck").action(async (args) => {
- const data = await read(process.stdin);
+ start() {
+ this.tStarted = process.hrtime.bigint();
+ }
- const lines = data.match(/[^\r\n]+/g);
+ stop() {
+ const now = process.hrtime.bigint();
+ const s = this.tStarted;
+ if (s == null) {
+ throw Error();
+ }
+ this.tSum = this.tSum + (now - s);
+ this.tSumSq = this.tSumSq + (now - s) * (now - s);
+ }
- if (!lines) {
- throw Error("can't split lines");
+ mean(nRuns: number): number {
+ return Number(this.tSum / BigInt(nRuns));
}
- const vals: Record<string, string> = {};
+ stdev(nRuns: number) {
+ const m = this.tSum / BigInt(nRuns);
+ const x = this.tSumSq / BigInt(nRuns) - m * m;
+ return Math.floor(Math.sqrt(Number(x)));
+ }
+}
- let inBlindSigningSection = false;
+testCli
+ .subcommand("benchmarkAgeRestrictions", "benchmark-age-restrictions")
+ .requiredOption("reps", ["--reps"], clk.INT, {
+ default: 100,
+ help: "repetitions (default: 100)",
+ })
+ .action(async (args) => {
+ const numReps = args.benchmarkAgeRestrictions.reps ?? 100;
+ let tCommit = new PerfTimer();
+ let tAttest = new PerfTimer();
+ let tVerify = new PerfTimer();
+ let tDerive = new PerfTimer();
+ let tCompare = new PerfTimer();
+
+ console.log("starting benchmark");
+
+ for (let i = 0; i < numReps; i++) {
+ console.log(`doing iteration ${i}`);
+ tCommit.start();
+ const commitProof = await AgeRestriction.restrictionCommit(
+ 0b1000001010101010101001,
+ 21,
+ );
+ tCommit.stop();
- for (const line of lines) {
- if (line === "blind signing:") {
- inBlindSigningSection = true;
- continue;
- }
- if (line[0] !== " ") {
- inBlindSigningSection = false;
- continue;
- }
- if (inBlindSigningSection) {
- const m = line.match(/ (\w+) (\w+)/);
- if (!m) {
- console.log("bad format");
- process.exit(2);
+ tAttest.start();
+ const attest = AgeRestriction.commitmentAttest(commitProof, 18);
+ tAttest.stop();
+
+ tVerify.start();
+ const attestRes = AgeRestriction.commitmentVerify(
+ commitProof.commitment,
+ encodeCrock(attest),
+ 18,
+ );
+ tVerify.stop();
+ if (!attestRes) {
+ throw Error();
}
- vals[m[1]] = m[2];
- }
- }
- console.log(vals);
+ const salt = getRandomBytes(32);
+ tDerive.start();
+ const deriv = await AgeRestriction.commitmentDerive(commitProof, salt);
+ tDerive.stop();
- const req = (k: string) => {
- if (!vals[k]) {
- throw Error(`no value for ${k}`);
+ tCompare.start();
+ const res2 = await AgeRestriction.commitCompare(
+ deriv.commitment,
+ commitProof.commitment,
+ salt,
+ );
+ tCompare.stop();
+ if (!res2) {
+ throw Error();
+ }
}
- return decodeCrock(vals[k]);
- };
- const myBm = rsaBlind(
- req("message_hash"),
- req("blinding_key_secret"),
- req("rsa_public_key"),
- );
-
- deepStrictEqual(req("blinded_message"), myBm);
+ console.log(
+ `edx25519-commit (ns): ${tCommit.mean(numReps)} (stdev ${tCommit.stdev(
+ numReps,
+ )})`,
+ );
+ console.log(
+ `edx25519-attest (ns): ${tAttest.mean(numReps)} (stdev ${tAttest.stdev(
+ numReps,
+ )})`,
+ );
+ console.log(
+ `edx25519-verify (ns): ${tVerify.mean(numReps)} (stdev ${tVerify.stdev(
+ numReps,
+ )})`,
+ );
+ console.log(
+ `edx25519-derive (ns): ${tDerive.mean(numReps)} (stdev ${tDerive.stdev(
+ numReps,
+ )})`,
+ );
+ console.log(
+ `edx25519-compare (ns): ${tCompare.mean(numReps)} (stdev ${tCompare.stdev(
+ numReps,
+ )})`,
+ );
+ });
- console.log("check passed!");
+testCli.subcommand("logtest", "logtest").action(async (args) => {
+ logger.trace("This is a trace message.");
+ logger.info("This is an info message.");
+ logger.warn("This is an warning message.");
+ logger.error("This is an error message.");
});
-testCli.subcommand("cryptoworker", "cryptoworker").action(async (args) => {
- const workerFactory = new NodeThreadCryptoWorkerFactory();
- const cryptoApi = new CryptoApi(workerFactory);
- const res = await cryptoApi.hashString("foo");
- console.log(res);
-});
+async function read(stream: NodeJS.ReadStream) {
+ const chunks = [];
+ for await (const chunk of stream) chunks.push(chunk);
+ return Buffer.concat(chunks).toString("utf8");
+}
export function main() {
- if (process.env["TALER_WALLET_DEBUG_DENOMSEL_ALLOW_LATE"]) {
- logger.warn("Allowing withdrawal of late denominations for debugging");
- walletCoreDebugFlags.denomselAllowLate = true;
+ const maybeFilename = getenv("TALER_WALLET_DEBUG_OBSERVE");
+ if (!!maybeFilename) {
+ observabilityEventFile = maybeFilename;
}
walletCli.run();
}
diff --git a/packages/taler-wallet-cli/src/integrationtests/scenario-prompt-payment.ts b/packages/taler-wallet-cli/src/integrationtests/scenario-prompt-payment.ts
deleted file mode 100644
index ea05de8e9..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/scenario-prompt-payment.ts
+++ /dev/null
@@ -1,60 +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, MerchantPrivateApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runPromptPaymentScenario(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",
- },
- });
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- console.log(orderStatus);
-
- // Wait "forever"
- await new Promise(() => {});
-}
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts b/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts
deleted file mode 100644
index 0f8af05e5..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-bank-api.ts
+++ /dev/null
@@ -1,136 +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,
- WalletCli,
- ExchangeService,
- setupDb,
- BankService,
- MerchantService,
- BankApi,
- BankAccessApi,
- CreditDebitIndicator,
-} from "../harness/harness.js";
-import { createEddsaKeyPair, encodeCrock } from "@gnu-taler/taler-util";
-import { defaultCoinConfig } from "../harness/denomStructures";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runBankApiTest(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.addDefaultInstance();
- 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 balResp = await BankAccessApi.getAccountBalance(bank, bankUser);
-
- console.log(balResp);
-
- // Check that we got the sign-up bonus.
- t.assertAmountEquals(balResp.balance.amount, "TESTKUDOS:100");
- t.assertTrue(
- balResp.balance.credit_debit_indicator === CreditDebitIndicator.Credit,
- );
-
- const res = createEddsaKeyPair();
-
- await BankApi.adminAddIncoming(bank, {
- amount: "TESTKUDOS:115",
- debitAccountPayto: bankUser.accountPaytoUri,
- exchangeBankAccount: exchangeBankAccount,
- reservePub: encodeCrock(res.eddsaPub),
- });
-
- balResp = await BankAccessApi.getAccountBalance(bank, bankUser);
- t.assertAmountEquals(balResp.balance.amount, "TESTKUDOS:15");
- t.assertTrue(
- balResp.balance.credit_debit_indicator === CreditDebitIndicator.Debit,
- );
-}
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-claim-loop.ts b/packages/taler-wallet-cli/src/integrationtests/test-claim-loop.ts
deleted file mode 100644
index a509e3b19..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-claim-loop.ts
+++ /dev/null
@@ -1,79 +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, MerchantPrivateApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
-import { URL } from "url";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Run test for the merchant's order lifecycle.
- *
- * FIXME: Is this test still necessary? We initially wrote if to confirm/document
- * assumptions about how the merchant should work.
- */
-export async function runClaimLoopTest(t: GlobalTestState) {
- // Set up test environment
-
- const {
- wallet,
- bank,
- exchange,
- merchant,
- } = await createSimpleTestkudosEnvironment(t);
-
- 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",
- },
- });
-
- // Query private order status before claiming it.
- let orderStatusBefore = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- },
- );
- t.assertTrue(orderStatusBefore.order_status === "unpaid");
- let statusUrlBefore = new URL(orderStatusBefore.order_status_url);
-
- // Make wallet claim the unpaid order.
- t.assertTrue(orderStatusBefore.order_status === "unpaid");
- const talerPayUri = orderStatusBefore.taler_pay_uri;
- await wallet.client.call(WalletApiOperation.PreparePayForUri, {
- talerPayUri,
- });
-
- // Query private order status after claiming it.
- let orderStatusAfter = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- },
- );
- t.assertTrue(orderStatusAfter.order_status === "claimed");
-
- await t.shutdown();
-}
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts b/packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts
deleted file mode 100644
index 28cca0758..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-denom-unoffered.ts
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- 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 {
- PreparePayResultType,
- TalerErrorCode,
- TalerErrorDetails,
- TransactionType,
-} from "@gnu-taler/taler-util";
-import {
- WalletApiOperation,
-} from "@gnu-taler/taler-wallet-core";
-import { makeEventId } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
-
-export async function runDenomUnofferedTest(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" });
-
- // Make the exchange forget the denomination.
- // Effectively we completely reset the exchange,
- // but keep the exchange master public key.
-
- await exchange.stop();
- await exchange.purgeDatabase();
- await exchange.purgeSecmodKeys();
- await exchange.start();
- await exchange.pingUntilAvailable();
-
- await merchant.stop();
- await merchant.start();
- await merchant.pingUntilAvailable();
-
- const order = {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "taler://fulfillment-success/thx",
- };
-
- {
- const orderResp = await MerchantPrivateApi.createOrder(
- merchant,
- "default",
- {
- order: order,
- },
- );
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- },
- );
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- // Make wallet pay for the order
-
- const preparePayResult = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: orderStatus.taler_pay_uri,
- },
- );
-
- t.assertTrue(
- preparePayResult.status === PreparePayResultType.PaymentPossible,
- );
-
- const exc = await t.assertThrowsAsync(async () => {
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: preparePayResult.proposalId,
- });
- });
-
- const errorDetails: TalerErrorDetails = exc.operationError;
- // FIXME: We might want a more specific error code here!
- t.assertDeepEqual(
- errorDetails.code,
- TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
- );
- const merchantErrorCode = (errorDetails.details as any).errorResponse.code;
- t.assertDeepEqual(
- merchantErrorCode,
- TalerErrorCode.MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND,
- );
- }
-
- await wallet.client.call(WalletApiOperation.AddExchange, {
- exchangeBaseUrl: exchange.baseUrl,
- forceUpdate: true,
- });
-
- // Now withdrawal should work again.
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
-
- await wallet.runUntilDone();
-
- const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {});
- console.log(JSON.stringify(txs, undefined, 2));
-}
-
-runDenomUnofferedTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-deposit.ts b/packages/taler-wallet-cli/src/integrationtests/test-deposit.ts
deleted file mode 100644
index f33c8338b..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-deposit.ts
+++ /dev/null
@@ -1,71 +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 { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
-
-/**
- * Run test for basic, bank-integrated withdrawal and payment.
- */
-export async function runDepositTest(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" });
-
- await wallet.runUntilDone();
-
- const { depositGroupId } = await wallet.client.call(
- WalletApiOperation.CreateDepositGroup,
- {
- amount: "TESTKUDOS:10",
- depositPaytoUri: "payto://x-taler-bank/localhost/foo",
- },
- );
-
- await wallet.runUntilDone();
-
- const transactions = await wallet.client.call(
- WalletApiOperation.GetTransactions,
- {},
- );
- console.log("transactions", JSON.stringify(transactions, undefined, 2));
- t.assertDeepEqual(transactions.transactions[0].type, "withdrawal");
- t.assertTrue(!transactions.transactions[0].pending);
- t.assertDeepEqual(transactions.transactions[1].type, "deposit");
- t.assertTrue(!transactions.transactions[1].pending);
- // The raw amount is what ends up on the bank account, which includes
- // deposit and wire fees.
- t.assertDeepEqual(transactions.transactions[1].amountRaw, "TESTKUDOS:9.79");
-
- const trackResult = wallet.client.call(WalletApiOperation.TrackDepositGroup, {
- depositGroupId,
- });
-
- console.log(JSON.stringify(trackResult, undefined, 2));
-}
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-exchange-management.ts b/packages/taler-wallet-cli/src/integrationtests/test-exchange-management.ts
deleted file mode 100644
index 8a5d563ce..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-exchange-management.ts
+++ /dev/null
@@ -1,272 +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,
- WalletCli,
- setupDb,
- BankService,
- ExchangeService,
- MerchantService,
- BankApi,
- BankAccessApi,
-} from "../harness/harness.js";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import {
- ExchangesListRespose,
- URL,
- TalerErrorCode,
-} from "@gnu-taler/taler-util";
-import {
- FaultInjectedExchangeService,
- FaultInjectionResponseContext,
-} from "../harness/faultInjection";
-import { defaultCoinConfig } from "../harness/denomStructures";
-
-/**
- * Test if the wallet handles outdated exchange versions correct.y
- */
-export async function runExchangeManagementTest(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);
-
- const faultyExchange = new FaultInjectedExchangeService(t, exchange, 8091);
-
- bank.setSuggestedExchange(
- faultyExchange,
- 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: "default",
- name: "Default Instance",
- paytoUris: [`payto://x-taler-bank/merchant-default`],
- });
-
- await merchant.addInstance({
- id: "minst1",
- name: "minst1",
- paytoUris: ["payto://x-taler-bank/minst1"],
- });
-
- console.log("setup done!");
-
- /*
- * =========================================================================
- * Check that the exchange can be added to the wallet
- * (without any faults active).
- * =========================================================================
- */
-
- const wallet = new WalletCli(t);
-
- let exchangesList: ExchangesListRespose;
-
- exchangesList = await wallet.client.call(
- WalletApiOperation.ListExchanges,
- {},
- );
- t.assertTrue(exchangesList.exchanges.length === 0);
-
- // Try before fault is injected
- await wallet.client.call(WalletApiOperation.AddExchange, {
- exchangeBaseUrl: faultyExchange.baseUrl,
- });
-
- exchangesList = await wallet.client.call(
- WalletApiOperation.ListExchanges,
- {},
- );
- t.assertTrue(exchangesList.exchanges.length === 1);
-
- await wallet.client.call(WalletApiOperation.ListExchanges, {
- exchangeBaseUrl: faultyExchange.baseUrl,
- });
-
- console.log("listing exchanges");
-
- exchangesList = await wallet.client.call(
- WalletApiOperation.ListExchanges,
- {},
- );
- t.assertTrue(exchangesList.exchanges.length === 1);
-
- console.log("got list", exchangesList);
-
- /*
- * =========================================================================
- * Check what happens if the exchange returns something totally
- * bogus for /keys.
- * =========================================================================
- */
-
- wallet.deleteDatabase();
-
- exchangesList = await wallet.client.call(
- WalletApiOperation.ListExchanges,
- {},
- );
- t.assertTrue(exchangesList.exchanges.length === 0);
-
- faultyExchange.faultProxy.addFault({
- async modifyResponse(ctx: FaultInjectionResponseContext) {
- const url = new URL(ctx.request.requestUrl);
- if (url.pathname === "/keys") {
- const body = {
- version: "whaaat",
- };
- ctx.responseBody = Buffer.from(JSON.stringify(body), "utf-8");
- }
- },
- });
-
- const err1 = await t.assertThrowsOperationErrorAsync(async () => {
- await wallet.client.call(WalletApiOperation.AddExchange, {
- exchangeBaseUrl: faultyExchange.baseUrl,
- });
- });
-
- // Response is malformed, since it didn't even contain a version code
- // in a format the wallet can understand.
- t.assertTrue(
- err1.operationError.code ===
- TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
- );
-
- exchangesList = await wallet.client.call(
- WalletApiOperation.ListExchanges,
- {},
- );
- t.assertTrue(exchangesList.exchanges.length === 0);
-
- /*
- * =========================================================================
- * Check what happens if the exchange returns an old, unsupported
- * version for /keys
- * =========================================================================
- */
-
- wallet.deleteDatabase();
- faultyExchange.faultProxy.clearAllFaults();
-
- faultyExchange.faultProxy.addFault({
- async modifyResponse(ctx: FaultInjectionResponseContext) {
- const url = new URL(ctx.request.requestUrl);
- if (url.pathname === "/keys") {
- const keys = ctx.responseBody?.toString("utf-8");
- t.assertTrue(keys != null);
- const keysJson = JSON.parse(keys);
- keysJson["version"] = "2:0:0";
- ctx.responseBody = Buffer.from(JSON.stringify(keysJson), "utf-8");
- }
- },
- });
-
- const err2 = await t.assertThrowsOperationErrorAsync(async () => {
- await wallet.client.call(WalletApiOperation.AddExchange, {
- exchangeBaseUrl: faultyExchange.baseUrl,
- });
- });
-
- t.assertTrue(
- err2.operationError.code ===
- TalerErrorCode.WALLET_EXCHANGE_PROTOCOL_VERSION_INCOMPATIBLE,
- );
-
- exchangesList = await wallet.client.call(
- WalletApiOperation.ListExchanges,
- {},
- );
- t.assertTrue(exchangesList.exchanges.length === 0);
-
- /*
- * =========================================================================
- * Check that the exchange version is also checked when
- * the exchange is implicitly added via the suggested
- * exchange of a bank-integrated withdrawal.
- * =========================================================================
- */
-
- // Fault from above is still active!
-
- // Create withdrawal operation
-
- const user = await BankApi.createRandomBankUser(bank);
- const wop = await BankAccessApi.createWithdrawalOperation(
- bank,
- user,
- "TESTKUDOS:10",
- );
-
- // Hand it to the wallet
-
- const wd = await wallet.client.call(
- WalletApiOperation.GetWithdrawalDetailsForUri,
- {
- talerWithdrawUri: wop.taler_withdraw_uri,
- },
- );
-
- // Make sure the faulty exchange isn't used for the suggestion.
- t.assertTrue(wd.possibleExchanges.length === 0);
-}
-
-runExchangeManagementTest.suites = ["exchange"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts b/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts
deleted file mode 100644
index 56684f70a..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-exchange-timetravel.ts
+++ /dev/null
@@ -1,221 +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 {
- codecForExchangeKeysJson,
- ConfirmPayResultType,
- Duration,
- durationFromSpec,
- PreparePayResultType,
- stringifyTimestamp,
-} from "@gnu-taler/taler-util";
-import {
- NodeHttpLib,
- PendingOperationsResponse,
- readSuccessResponseJsonOrThrow,
- WalletApiOperation,
-} from "@gnu-taler/taler-wallet-core";
-import { makeNoFeeCoinConfig } from "../harness/denomStructures";
-import {
- BankService,
- ExchangeService,
- GlobalTestState,
- MerchantPrivateApi,
- MerchantService,
- setupDb,
- WalletCli,
-} from "../harness/harness.js";
-import { startWithdrawViaBank, withdrawViaBank } from "../harness/helpers.js";
-
-async function applyTimeTravel(
- timetravelDuration: Duration,
- s: {
- exchange?: ExchangeService;
- merchant?: MerchantService;
- wallet?: WalletCli;
- },
-): Promise<void> {
- if (s.exchange) {
- await s.exchange.stop();
- s.exchange.setTimetravel(timetravelDuration);
- await s.exchange.start();
- await s.exchange.pingUntilAvailable();
- }
-
- if (s.merchant) {
- await s.merchant.stop();
- s.merchant.setTimetravel(timetravelDuration);
- await s.merchant.start();
- await s.merchant.pingUntilAvailable();
- }
-
- if (s.wallet) {
- console.log("setting wallet time travel to", timetravelDuration);
- s.wallet.setTimetravel(timetravelDuration);
- }
-}
-
-const http = new NodeHttpLib();
-
-/**
- * Basic time travel test.
- */
-export async function runExchangeTimetravelTest(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.addCoinConfigList(makeNoFeeCoinConfig("TESTKUDOS"));
-
- 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://x-taler-bank/merchant-default`],
- });
-
- await merchant.addInstance({
- id: "minst1",
- name: "minst1",
- paytoUris: ["payto://x-taler-bank/minst1"],
- });
-
- console.log("setup done!");
-
- const wallet = new WalletCli(t);
-
- // Withdraw digital cash into the wallet.
-
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" });
-
- const keysResp1 = await http.get(exchange.baseUrl + "keys");
- const keys1 = await readSuccessResponseJsonOrThrow(
- keysResp1,
- codecForExchangeKeysJson(),
- );
- console.log(
- "keys 1 (before time travel):",
- JSON.stringify(keys1, undefined, 2),
- );
-
- // Travel into the future, the deposit expiration is two years
- // into the future.
- console.log("applying first time travel");
- await applyTimeTravel(durationFromSpec({ days: 400 }), {
- wallet,
- exchange,
- merchant,
- });
-
- const keysResp2 = await http.get(exchange.baseUrl + "keys");
- const keys2 = await readSuccessResponseJsonOrThrow(
- keysResp2,
- codecForExchangeKeysJson(),
- );
- console.log(
- "keys 2 (after time travel):",
- JSON.stringify(keys2, undefined, 2),
- );
-
- const denomPubs1 = keys1.denoms.map((x) => {
- return {
- denomPub: x.denom_pub,
- expireDeposit: stringifyTimestamp(x.stamp_expire_deposit),
- };
- });
-
- const denomPubs2 = keys2.denoms.map((x) => {
- return {
- denomPub: x.denom_pub,
- expireDeposit: stringifyTimestamp(x.stamp_expire_deposit),
- };
- });
- const dps2 = new Set(denomPubs2.map((x) => x.denomPub));
-
- console.log("=== KEYS RESPONSE 1 ===");
-
- console.log("list issue date", stringifyTimestamp(keys1.list_issue_date));
- console.log("num denoms", keys1.denoms.length)
- console.log("denoms", JSON.stringify(denomPubs1, undefined, 2));
-
- console.log("=== KEYS RESPONSE 2 ===");
-
- console.log("list issue date", stringifyTimestamp(keys2.list_issue_date));
- console.log("num denoms", keys2.denoms.length)
- console.log("denoms", JSON.stringify(denomPubs2, undefined, 2));
-
- for (const da of denomPubs1) {
- if (!dps2.has(da.denomPub)) {
- console.log("=== ERROR ===");
- console.log(`denomination with public key ${da.denomPub} is not present in new /keys response`);
- console.log(
- `the new /keys response was issued ${stringifyTimestamp(
- keys2.list_issue_date,
- )}`,
- );
- console.log(
- `however, the missing denomination has stamp_expire_deposit ${da.expireDeposit}`,
- );
- console.log("see above for the verbatim /keys responses");
- t.assertTrue(false);
- }
- }
-}
-
-runExchangeTimetravelTest.suites = ["exchange"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-fee-regression.ts b/packages/taler-wallet-cli/src/integrationtests/test-fee-regression.ts
deleted file mode 100644
index 025e12226..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-fee-regression.ts
+++ /dev/null
@@ -1,200 +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 { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import {
- GlobalTestState,
- BankService,
- ExchangeService,
- MerchantService,
- setupDb,
- WalletCli,
-} from "../harness/harness.js";
-import {
- withdrawViaBank,
- makeTestPayment,
- SimpleTestEnvironment,
-} from "../harness/helpers.js";
-
-/**
- * Run a test case with a simple TESTKUDOS Taler environment, consisting
- * of one exchange, one bank and one merchant.
- */
-export async function createMyTestkudosEnvironment(
- t: GlobalTestState,
-): Promise<SimpleTestEnvironment> {
- 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();
-
- const coinCommon = {
- durationLegal: "3 years",
- durationSpend: "2 years",
- durationWithdraw: "7 days",
- rsaKeySize: 1024,
- feeDeposit: "TESTKUDOS:0.0025",
- feeWithdraw: "TESTKUDOS:0",
- feeRefresh: "TESTKUDOS:0",
- feeRefund: "TESTKUDOS:0",
- };
-
- exchange.addCoinConfigList([
- {
- ...coinCommon,
- name: "c1",
- value: "TESTKUDOS:1.28",
- },
- {
- ...coinCommon,
- name: "c2",
- value: "TESTKUDOS:0.64",
- },
- {
- ...coinCommon,
- name: "c3",
- value: "TESTKUDOS:0.32",
- },
- {
- ...coinCommon,
- name: "c4",
- value: "TESTKUDOS:0.16",
- },
- {
- ...coinCommon,
- name: "c5",
- value: "TESTKUDOS:0.08",
- },
- {
- ...coinCommon,
- name: "c5",
- value: "TESTKUDOS:0.04",
- },
- {
- ...coinCommon,
- name: "c6",
- value: "TESTKUDOS:0.02",
- },
- {
- ...coinCommon,
- name: "c7",
- value: "TESTKUDOS:0.01",
- },
- ]);
-
- await exchange.start();
- await exchange.pingUntilAvailable();
-
- merchant.addExchange(exchange);
-
- await merchant.start();
- await merchant.pingUntilAvailable();
-
- await merchant.addDefaultInstance();
- await merchant.addInstance({
- id: "minst1",
- name: "minst1",
- paytoUris: ["payto://x-taler-bank/minst1"],
- });
-
- console.log("setup done!");
-
- const wallet = new WalletCli(t);
-
- return {
- commonDb: db,
- exchange,
- merchant,
- wallet,
- bank,
- exchangeBankAccount,
- };
-}
-
-/**
- * Run test for basic, bank-integrated withdrawal and payment.
- */
-export async function runFeeRegressionTest(t: GlobalTestState) {
- // Set up test environment
-
- const {
- wallet,
- bank,
- exchange,
- merchant,
- } = await createMyTestkudosEnvironment(t);
-
- // Withdraw digital cash into the wallet.
-
- await withdrawViaBank(t, {
- wallet,
- bank,
- exchange,
- amount: "TESTKUDOS:1.92",
- });
-
- const coins = await wallet.client.call(WalletApiOperation.DumpCoins, {});
-
- // Make sure we really withdraw one 0.64 and one 1.28 coin.
- t.assertTrue(coins.coins.length === 2);
-
- const order = {
- summary: "Buy me!",
- amount: "TESTKUDOS:1.30",
- fulfillment_url: "taler://fulfillment-success/thx",
- };
-
- await makeTestPayment(t, { wallet, merchant, order });
-
- await wallet.runUntilDone();
-
- const txs = await wallet.client.call(WalletApiOperation.GetTransactions, {});
- t.assertAmountEquals(txs.transactions[1].amountEffective, "TESTKUDOS:1.30");
- console.log(txs);
-}
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankaccount.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankaccount.ts
deleted file mode 100644
index 839ad5fa7..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankaccount.ts
+++ /dev/null
@@ -1,110 +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 } from "../harness/harness.js";
-import {
- NexusUserBundle,
- LibeufinNexusApi,
- LibeufinNexusService,
- LibeufinSandboxService,
- LibeufinSandboxApi,
- findNexusPayment,
-} from "../harness/libeufin";
-
-/**
- * Run basic test with LibEuFin.
- */
-export async function runLibeufinApiBankaccountTest(t: GlobalTestState) {
- const nexus = await LibeufinNexusService.create(t, {
- httpPort: 5011,
- databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`,
- });
- await nexus.start();
- await nexus.pingUntilAvailable();
-
- await LibeufinNexusApi.createUser(nexus, {
- username: "one",
- password: "testing-the-bankaccount-api",
- });
- const sandbox = await LibeufinSandboxService.create(t, {
- httpPort: 5012,
- databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`,
- });
- await sandbox.start();
- await sandbox.pingUntilAvailable();
- await LibeufinSandboxApi.createEbicsHost(sandbox, "mock");
- await LibeufinSandboxApi.createEbicsSubscriber(sandbox, {
- hostID: "mock",
- userID: "mock",
- partnerID: "mock",
- });
- await LibeufinSandboxApi.createEbicsBankAccount(sandbox, {
- subscriber: {
- hostID: "mock",
- partnerID: "mock",
- userID: "mock",
- },
- iban: "DE71500105179674997361",
- bic: "BELADEBEXXX",
- name: "mock",
- currency: "EUR",
- label: "mock",
- });
- await LibeufinNexusApi.createEbicsBankConnection(nexus, {
- name: "bankaccount-api-test-connection",
- ebicsURL: "http://localhost:5012/ebicsweb",
- hostID: "mock",
- userID: "mock",
- partnerID: "mock",
- });
- await LibeufinNexusApi.connectBankConnection(
- nexus,
- "bankaccount-api-test-connection",
- );
- await LibeufinNexusApi.fetchAccounts(
- nexus,
- "bankaccount-api-test-connection",
- );
-
- await LibeufinNexusApi.importConnectionAccount(
- nexus,
- "bankaccount-api-test-connection",
- "mock",
- "local-mock",
- );
- await LibeufinSandboxApi.simulateIncomingTransaction(
- sandbox,
- "mock", // creditor bankaccount label
- {
- debtorIban: "DE84500105176881385584",
- debtorBic: "BELADEBEXXX",
- debtorName: "mock2",
- amount: "1",
- subject: "mock subject",
- }
- );
- await LibeufinNexusApi.fetchTransactions(nexus, "local-mock");
- let transactions = await LibeufinNexusApi.getAccountTransactions(
- nexus,
- "local-mock",
- );
- let el = findNexusPayment("mock subject", transactions.data);
- t.assertTrue(el instanceof Object);
-}
-runLibeufinApiBankaccountTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankconnection.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankconnection.ts
deleted file mode 100644
index f1d507c03..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-bankconnection.ts
+++ /dev/null
@@ -1,63 +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 } from "../harness/harness.js";
-import {
- NexusUserBundle,
- LibeufinNexusApi,
- LibeufinNexusService,
- LibeufinSandboxService,
- LibeufinSandboxApi,
- findNexusPayment,
-} from "../harness/libeufin";
-
-/**
- * Run basic test with LibEuFin.
- */
-export async function runLibeufinApiBankconnectionTest(t: GlobalTestState) {
- const nexus = await LibeufinNexusService.create(t, {
- httpPort: 5011,
- databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`,
- });
- await nexus.start();
- await nexus.pingUntilAvailable();
-
- await LibeufinNexusApi.createUser(nexus, {
- username: "one",
- password: "testing-the-bankconnection-api",
- });
-
- await LibeufinNexusApi.createEbicsBankConnection(nexus, {
- name: "bankconnection-api-test-connection",
- ebicsURL: "http://localhost:5012/ebicsweb",
- hostID: "mock",
- userID: "mock",
- partnerID: "mock",
- });
-
- let connections = await LibeufinNexusApi.getAllConnections(nexus);
- t.assertTrue(connections.data["bankConnections"].length == 1);
-
- await LibeufinNexusApi.deleteBankConnection(nexus, {
- bankConnectionId: "bankconnection-api-test-connection",
- });
- connections = await LibeufinNexusApi.getAllConnections(nexus);
- t.assertTrue(connections.data["bankConnections"].length == 0);
-}
-runLibeufinApiBankconnectionTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade-bad-request.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade-bad-request.ts
deleted file mode 100644
index b106cf304..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade-bad-request.ts
+++ /dev/null
@@ -1,71 +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 axios from "axios";
-import { URL } from "@gnu-taler/taler-util";
-import { GlobalTestState } from "../harness/harness.js";
-import {
- SandboxUserBundle,
- NexusUserBundle,
- launchLibeufinServices,
- LibeufinNexusApi,
-} from "../harness/libeufin";
-
-export async function runLibeufinApiFacadeBadRequestTest(t: GlobalTestState) {
-
- /**
- * User saltetd "01"
- */
- const user01nexus = new NexusUserBundle(
- "01",
- "http://localhost:5010/ebicsweb",
- );
- const user01sandbox = new SandboxUserBundle("01");
-
- /**
- * Launch Sandbox and Nexus.
- */
- const libeufinServices = await launchLibeufinServices(
- t,
- [user01nexus],
- [user01sandbox],
- ["twg"],
- );
- console.log("malformed facade");
- const baseUrl = libeufinServices.libeufinNexus.baseUrl;
- let url = new URL("facades", baseUrl);
- let resp = await axios.post(
- url.href,
- {
- name: "malformed-facade",
- type: "taler-wire-gateway",
- config: {}, // malformation here.
- },
- {
- auth: {
- username: "admin",
- password: "test",
- },
- validateStatus: () => true,
- },
- );
- t.assertTrue(resp.status == 400);
-}
-
-runLibeufinApiFacadeBadRequestTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade.ts
deleted file mode 100644
index c49d49712..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-facade.ts
+++ /dev/null
@@ -1,70 +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 } from "../harness/harness.js";
-import {
- SandboxUserBundle,
- NexusUserBundle,
- launchLibeufinServices,
- LibeufinNexusApi,
-} from "../harness/libeufin";
-
-/**
- * Run basic test with LibEuFin.
- */
-export async function runLibeufinApiFacadeTest(t: GlobalTestState) {
- /**
- * User saltetd "01"
- */
- const user01nexus = new NexusUserBundle(
- "01",
- "http://localhost:5010/ebicsweb",
- );
- const user01sandbox = new SandboxUserBundle("01");
-
- /**
- * Launch Sandbox and Nexus.
- */
- const libeufinServices = await launchLibeufinServices(
- t,
- [user01nexus],
- [user01sandbox],
- ["twg"],
- );
- let resp = await LibeufinNexusApi.getAllFacades(
- libeufinServices.libeufinNexus,
- );
- // check that original facade shows up.
- t.assertTrue(resp.data["facades"][0]["name"] == user01nexus.twgReq["name"]);
-
- const twgBaseUrl: string = resp.data["facades"][0]["baseUrl"];
- t.assertTrue(typeof twgBaseUrl === "string");
- t.assertTrue(twgBaseUrl.startsWith("http://"));
- t.assertTrue(twgBaseUrl.endsWith("/"));
-
- // delete it.
- resp = await LibeufinNexusApi.deleteFacade(
- libeufinServices.libeufinNexus,
- user01nexus.twgReq["name"],
- );
- // check that no facades show up.
- t.assertTrue(!resp.data.hasOwnProperty("facades"));
-}
-
-runLibeufinApiFacadeTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-permissions.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-permissions.ts
deleted file mode 100644
index e64f459a0..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-permissions.ts
+++ /dev/null
@@ -1,64 +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 } from "../harness/harness.js";
-import {
- NexusUserBundle,
- LibeufinNexusApi,
- LibeufinNexusService,
-} from "../harness/libeufin";
-
-/**
- * Run basic test with LibEuFin.
- */
-export async function runLibeufinApiPermissionsTest(t: GlobalTestState) {
- const nexus = await LibeufinNexusService.create(t, {
- httpPort: 5011,
- databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`,
- });
- await nexus.start();
- await nexus.pingUntilAvailable();
-
- const user01nexus = new NexusUserBundle(
- "01",
- "http://localhost:5010/ebicsweb",
- );
-
- await LibeufinNexusApi.createUser(nexus, user01nexus.userReq);
- await LibeufinNexusApi.postPermission(
- nexus,
- user01nexus.twgTransferPermission,
- );
- let transferPermission = await LibeufinNexusApi.getAllPermissions(nexus);
- let element = transferPermission.data["permissions"].pop();
- t.assertTrue(
- element["permissionName"] == "facade.talerwiregateway.transfer" &&
- element["subjectId"] == "username-01",
- );
- let denyTransfer = user01nexus.twgTransferPermission;
-
- // Now revoke permission.
- denyTransfer["action"] = "revoke";
- await LibeufinNexusApi.postPermission(nexus, denyTransfer);
-
- transferPermission = await LibeufinNexusApi.getAllPermissions(nexus);
- t.assertTrue(transferPermission.data["permissions"].length == 0);
-}
-
-runLibeufinApiPermissionsTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-camt.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-camt.ts
deleted file mode 100644
index f5df4cfa3..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-camt.ts
+++ /dev/null
@@ -1,64 +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 } from "../harness/harness.js";
-import {
- NexusUserBundle,
- LibeufinNexusApi,
- LibeufinNexusService,
- LibeufinSandboxService,
- LibeufinSandboxApi,
- findNexusPayment,
-} from "../harness/libeufin";
-
-// This test only checks that LibEuFin doesn't fail when
-// it generates Camt statements - no assertions take place.
-// Furthermore, it prints the Camt.053 being generated.
-export async function runLibeufinApiSandboxCamtTest(t: GlobalTestState) {
-
- const sandbox = await LibeufinSandboxService.create(t, {
- httpPort: 5012,
- databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`,
- });
- await sandbox.start();
- await sandbox.pingUntilAvailable();
- await LibeufinSandboxApi.createBankAccount(sandbox, {
- iban: "DE71500105179674997361",
- bic: "BELADEBEXXX",
- name: "Mock Name",
- label: "mock-account-0",
- currency: "EUR"
- });
- await LibeufinSandboxApi.createBankAccount(sandbox, {
- iban: "DE71500105179674997361",
- bic: "BELADEBEXXX",
- name: "Mock Name",
- label: "mock-account-1",
- currency: "EUR"
- });
- await sandbox.makeTransaction("mock-account-0", "mock-account-1", "EUR:1", "+1");
- await sandbox.makeTransaction("mock-account-0", "mock-account-1", "EUR:1", "+1");
- await sandbox.makeTransaction("mock-account-0", "mock-account-1", "EUR:1", "+1");
- await sandbox.makeTransaction("mock-account-1", "mock-account-0", "EUR:5", "minus 5");
- await sandbox.c53tick();
- let ret = await LibeufinSandboxApi.getCamt053(sandbox, "mock-account-1");
- console.log(ret);
-}
-runLibeufinApiSandboxCamtTest.excludeByDefault = true;
-runLibeufinApiSandboxCamtTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-transactions.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-transactions.ts
deleted file mode 100644
index a90644926..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-sandbox-transactions.ts
+++ /dev/null
@@ -1,72 +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 } from "../harness/harness.js";
-import {
- NexusUserBundle,
- LibeufinNexusApi,
- LibeufinNexusService,
- LibeufinSandboxService,
- LibeufinSandboxApi,
- findNexusPayment,
-} from "../harness/libeufin";
-
-export async function runLibeufinApiSandboxTransactionsTest(t: GlobalTestState) {
-
- const sandbox = await LibeufinSandboxService.create(t, {
- httpPort: 5012,
- databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`,
- });
- await sandbox.start();
- await sandbox.pingUntilAvailable();
- await LibeufinSandboxApi.createBankAccount(sandbox, {
- iban: "DE71500105179674997361",
- bic: "BELADEBEXXX",
- name: "Mock Name",
- label: "mock-account",
- currency: "EUR"
- });
- await LibeufinSandboxApi.simulateIncomingTransaction(
- sandbox,
- "mock-account",
- {
-
- debtorIban: "DE84500105176881385584",
- debtorBic: "BELADEBEXXX",
- debtorName: "mock2",
- subject: "mock subject",
- amount: "1" // EUR is default.
- }
- )
- await LibeufinSandboxApi.simulateIncomingTransaction(
- sandbox,
- "mock-account",
- {
-
- debtorIban: "DE84500105176881385584",
- debtorBic: "BELADEBEXXX",
- debtorName: "mock2",
- subject: "mock subject 2",
- amount: "1.1" // EUR is default.
- }
- )
- let ret = await LibeufinSandboxApi.getAccountInfoWithBalance(sandbox, "mock-account");
- t.assertAmountEquals(ret.data.balance, "EUR:2.1");
-}
-runLibeufinApiSandboxTransactionsTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-scheduling.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-scheduling.ts
deleted file mode 100644
index 3863c5711..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-scheduling.ts
+++ /dev/null
@@ -1,107 +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, setupDb } from "../harness/harness.js";
-import {
- SandboxUserBundle,
- NexusUserBundle,
- launchLibeufinServices,
- LibeufinSandboxApi,
- LibeufinNexusApi,
- LibeufinNexusService,
-} from "../harness/libeufin";
-
-/**
- * Test Nexus scheduling API. It creates a task, check whether it shows
- * up, then deletes it, and check if it's gone. Ideally, a check over the
- * _liveliness_ of a scheduled task should happen.
- */
-export async function runLibeufinApiSchedulingTest(t: GlobalTestState) {
- const nexus = await LibeufinNexusService.create(t, {
- httpPort: 5011,
- databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`,
- });
- await nexus.start();
- await nexus.pingUntilAvailable();
-
- const user01nexus = new NexusUserBundle(
- "01",
- "http://localhost:5010/ebicsweb",
- );
- const user01sandbox = new SandboxUserBundle("01");
- await launchLibeufinServices(t, [user01nexus], [user01sandbox]);
- await LibeufinNexusApi.postTask(nexus, user01nexus.localAccountName, {
- name: "test-task",
- cronspec: "* * *",
- type: "fetch",
- params: {
- level: "all",
- rangeType: "all",
- },
- });
- let resp = await LibeufinNexusApi.getTasks(
- nexus,
- user01nexus.localAccountName,
- "test-task",
- );
- t.assertTrue(resp.data["taskName"] == "test-task");
- await LibeufinNexusApi.deleteTask(
- nexus,
- user01nexus.localAccountName,
- "test-task",
- );
- try {
- await LibeufinNexusApi.getTasks(
- nexus,
- user01nexus.localAccountName,
- "test-task",
- );
- } catch (err: any) {
- t.assertTrue(err.response.status == 404);
- }
-
- // Same with submit task.
- await LibeufinNexusApi.postTask(nexus, user01nexus.localAccountName, {
- name: "test-task",
- cronspec: "* * *",
- type: "submit",
- params: {},
- });
- resp = await LibeufinNexusApi.getTasks(
- nexus,
- user01nexus.localAccountName,
- "test-task",
- );
- t.assertTrue(resp.data["taskName"] == "test-task");
- await LibeufinNexusApi.deleteTask(
- nexus,
- user01nexus.localAccountName,
- "test-task",
- );
- try {
- await LibeufinNexusApi.getTasks(
- nexus,
- user01nexus.localAccountName,
- "test-task",
- );
- } catch (err: any) {
- t.assertTrue(err.response.status == 404);
- }
-}
-runLibeufinApiSchedulingTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-users.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-users.ts
deleted file mode 100644
index edf66690b..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-api-users.ts
+++ /dev/null
@@ -1,63 +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 } from "../harness/harness.js";
-import { LibeufinNexusApi, LibeufinNexusService } from "../harness/libeufin";
-
-/**
- * Run basic test with LibEuFin.
- */
-export async function runLibeufinApiUsersTest(t: GlobalTestState) {
- const nexus = await LibeufinNexusService.create(t, {
- httpPort: 5011,
- databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`,
- });
- await nexus.start();
- await nexus.pingUntilAvailable();
-
- await LibeufinNexusApi.createUser(nexus, {
- username: "one",
- password: "will-be-changed",
- });
-
- await LibeufinNexusApi.changePassword(
- nexus,
- "one",
- {
- newPassword: "got-changed",
- },
- {
- auth: {
- username: "admin",
- password: "test",
- },
- },
- );
-
- let resp = await LibeufinNexusApi.getUser(nexus, {
- auth: {
- username: "one",
- password: "got-changed",
- },
- });
- console.log(resp.data);
- t.assertTrue(resp.data["username"] == "one" && !resp.data["superuser"]);
-}
-
-runLibeufinApiUsersTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-bad-gateway.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-bad-gateway.ts
deleted file mode 100644
index 786e61832..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-bad-gateway.ts
+++ /dev/null
@@ -1,74 +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, delayMs } from "../harness/harness.js";
-import {
- NexusUserBundle,
- LibeufinNexusApi,
- LibeufinNexusService,
- LibeufinSandboxService,
-} from "../harness/libeufin";
-
-/**
- * Testing how Nexus reacts when the Sandbox is unreachable.
- * Typically, because the user specified a wrong EBICS endpoint.
- */
-export async function runLibeufinBadGatewayTest(t: GlobalTestState) {
- /**
- * User saltetd "01"
- */
- const user01nexus = new NexusUserBundle(
- "01", "http://localhost:5010/not-found", // the EBICS endpoint at Sandbox
- );
-
- // Start Nexus
- const libeufinNexus = await LibeufinNexusService.create(t, {
- httpPort: 5011,
- databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`,
- });
- await libeufinNexus.start();
- await libeufinNexus.pingUntilAvailable();
-
- // Start Sandbox
- const libeufinSandbox = await LibeufinSandboxService.create(t, {
- httpPort: 5010,
- databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`,
- });
- await libeufinSandbox.start();
- await libeufinSandbox.pingUntilAvailable();
-
- // Connecting to a non-existent Sandbox endpoint.
- await LibeufinNexusApi.createEbicsBankConnection(
- libeufinNexus,
- user01nexus.connReq
- );
-
- // 502 Bad Gateway expected.
- try {
- await LibeufinNexusApi.connectBankConnection(
- libeufinNexus,
- user01nexus.connReq.name,
- );
- } catch(e: any) {
- t.assertTrue(e.response.status == 502);
- return;
- }
- t.assertTrue(false);
-}
-runLibeufinBadGatewayTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts
deleted file mode 100644
index 9e1842d03..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-basic.ts
+++ /dev/null
@@ -1,314 +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 { CoreApiResponse } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures";
-import {
- DbInfo,
- HarnessExchangeBankAccount,
- ExchangeService,
- GlobalTestState,
- MerchantService,
- setupDb,
- WalletCli,
-} from "../harness/harness.js";
-import { makeTestPayment } from "../harness/helpers.js";
-import {
- LibeufinNexusApi,
- LibeufinNexusService,
- LibeufinSandboxApi,
- LibeufinSandboxService,
-} from "../harness/libeufin";
-
-const exchangeIban = "DE71500105179674997361";
-const customerIban = "DE84500105176881385584";
-const customerBic = "BELADEBEXXX";
-const merchantIban = "DE42500105171245624648";
-
-export interface LibeufinTestEnvironment {
- commonDb: DbInfo;
- exchange: ExchangeService;
- exchangeBankAccount: HarnessExchangeBankAccount;
- 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: "AUTOATW1XXX",
- 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",
- });
-
- await LibeufinNexusApi.createUser(libeufinNexus, {
- username: "twguser",
- password: "twgpw",
- });
-
- await LibeufinNexusApi.postPermission(libeufinNexus, {
- action: "grant",
- permission: {
- subjectType: "user",
- subjectId: "twguser",
- resourceType: "facade",
- resourceId: "twg1",
- permissionName: "facade.talerWireGateway.history",
- },
- });
-
- await LibeufinNexusApi.postPermission(libeufinNexus, {
- action: "grant",
- permission: {
- subjectType: "user",
- subjectId: "twguser",
- resourceType: "facade",
- resourceId: "twg1",
- permissionName: "facade.talerWireGateway.transfer",
- },
- });
-
- 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: HarnessExchangeBankAccount = {
- accountName: "twguser",
- accountPassword: "twgpw",
- 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);
-
- await wallet.client.call(WalletApiOperation.AddExchange, {
- exchangeBaseUrl: exchange.baseUrl,
- });
-
- const wr = await wallet.client.call(
- WalletApiOperation.AcceptManualWithdrawal,
- {
- exchangeBaseUrl: exchange.baseUrl,
- amount: "EUR:10",
- },
- );
-
- const reservePub: string = wr.reservePub;
-
- await LibeufinSandboxApi.simulateIncomingTransaction(
- libeufinSandbox,
- "exchangeacct",
- {
- amount: "15.00",
- debtorBic: customerBic,
- debtorIban: customerIban,
- debtorName: "Jane Customer",
- subject: `Taler Top-up ${reservePub}`,
- },
- );
-
- await LibeufinNexusApi.fetchTransactions(libeufinNexus, "myacct");
-
- await exchange.runWirewatchOnce();
-
- await wallet.runUntilDone();
-
- const bal = await wallet.client.call(WalletApiOperation.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,
- );
-}
-runLibeufinBasicTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-c5x.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-c5x.ts
deleted file mode 100644
index 5a995fb69..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-c5x.ts
+++ /dev/null
@@ -1,138 +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, delayMs } from "../harness/harness.js";
-import {
- SandboxUserBundle,
- NexusUserBundle,
- launchLibeufinServices,
- LibeufinSandboxApi,
- LibeufinNexusApi,
-} from "../harness/libeufin";
-
-/**
- * This test checks how the C52 and C53 coordinate. It'll test
- * whether fresh transactions stop showing as C52 after they get
- * included in a bank statement.
- */
-export async function runLibeufinC5xTest(t: GlobalTestState) {
- /**
- * User saltetd "01"
- */
- const user01nexus = new NexusUserBundle(
- "01",
- "http://localhost:5010/ebicsweb",
- );
- const user01sandbox = new SandboxUserBundle("01");
-
- /**
- * User saltetd "02".
- */
- const user02nexus = new NexusUserBundle(
- "02",
- "http://localhost:5010/ebicsweb",
- );
- const user02sandbox = new SandboxUserBundle("02");
-
- /**
- * Launch Sandbox and Nexus.
- */
- const libeufinServices = await launchLibeufinServices(
- t,
- [user01nexus, user02nexus],
- [user01sandbox, user02sandbox],
- ["twg"],
- );
-
- // Check that C52 and C53 have zero entries.
-
- // C52
- await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- "all", // range
- "report", // level
- );
- // C53
- await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- "all", // range
- "statement", // level
- );
- const nexusTxs = await LibeufinNexusApi.getAccountTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- );
- t.assertTrue(nexusTxs.data["transactions"].length == 0);
-
- // Addressing one payment to user 01
- await libeufinServices.libeufinSandbox.makeTransaction(
- user02sandbox.ebicsBankAccount.label, // debit
- user01sandbox.ebicsBankAccount.label, // credit
- "EUR:10",
- "first payment",
- );
-
- // Checking that C52 has one and C53 has zero.
-
- let expectOne = await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- "all", // range
- "report", // C52
- );
- t.assertTrue(expectOne.data.newTransactions == 1);
- t.assertTrue(expectOne.data.downloadedTransactions == 1);
-
- let expectZero = await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- "all", // range
- "statement", // C53
- );
- t.assertTrue(expectZero.data.newTransactions == 0);
- t.assertTrue(expectZero.data.downloadedTransactions == 0);
-
- // Ticking now: the one payment should be downloaded
- // in a C53 but not in a C52. In any case, the payment
- // is not new anymore, because it was already ingested
- // when it was downloaded for the first time along the
- // c52 above.
- await libeufinServices.libeufinSandbox.c53tick();
-
- expectOne = await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- "all", // range
- "statement", // C53
- );
- t.assertTrue(expectOne.data.downloadedTransactions == 1);
- t.assertTrue(expectOne.data.newTransactions == 0);
-
- expectZero = await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- "all", // range
- "report", // C52
- );
- t.assertTrue(expectZero.data.downloadedTransactions == 0);
- t.assertTrue(expectZero.data.newTransactions == 0);
-}
-runLibeufinC5xTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-facade-anastasis.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-facade-anastasis.ts
deleted file mode 100644
index 0bbd4fd28..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-facade-anastasis.ts
+++ /dev/null
@@ -1,169 +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 } from "../harness/harness.js";
-import {
- SandboxUserBundle,
- NexusUserBundle,
- launchLibeufinServices,
- LibeufinNexusApi,
- LibeufinSandboxApi,
-} from "../harness/libeufin";
-
-/**
- * Testing the Anastasis API, offered by the Anastasis facade.
- */
-export async function runLibeufinAnastasisFacadeTest(t: GlobalTestState) {
- /**
- * User saltetd "01"
- */
- const user01nexus = new NexusUserBundle(
- "01",
- "http://localhost:5010/ebicsweb",
- );
- const user01sandbox = new SandboxUserBundle("01");
-
- /**
- * Launch Sandbox and Nexus.
- */
- const libeufinServices = await launchLibeufinServices(
- t,
- [user01nexus],
- [user01sandbox],
- ["anastasis"], // create only one Anastasis facade.
- );
- let resp = await LibeufinNexusApi.getAllFacades(
- libeufinServices.libeufinNexus,
- );
- // check that original facade shows up.
- t.assertTrue(resp.data["facades"][0]["name"] == user01nexus.anastasisReq["name"]);
- const anastasisBaseUrl: string = resp.data["facades"][0]["baseUrl"];
- t.assertTrue(typeof anastasisBaseUrl === "string");
- t.assertTrue(anastasisBaseUrl.startsWith("http://"));
- t.assertTrue(anastasisBaseUrl.endsWith("/"));
-
- await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- );
-
- await LibeufinNexusApi.postPermission(
- libeufinServices.libeufinNexus, {
- action: "grant",
- permission: {
- subjectId: user01nexus.userReq.username,
- subjectType: "user",
- resourceType: "facade",
- resourceId: user01nexus.anastasisReq.name,
- permissionName: "facade.anastasis.history",
- },
- }
- );
-
- // check if empty.
- let txsEmpty = await LibeufinNexusApi.getAnastasisTransactions(
- libeufinServices.libeufinNexus,
- anastasisBaseUrl, {delta: 5})
-
- t.assertTrue(txsEmpty.data.incoming_transactions.length == 0);
-
- LibeufinSandboxApi.simulateIncomingTransaction(
- libeufinServices.libeufinSandbox,
- user01sandbox.ebicsBankAccount.label,
- {
- debtorIban: "ES3314655813489414469157",
- debtorBic: "BCMAESM1XXX",
- debtorName: "Mock Donor",
- subject: "Anastasis donation",
- amount: "3", // Sandbox takes currency from its 'config'
- },
- )
-
- LibeufinSandboxApi.simulateIncomingTransaction(
- libeufinServices.libeufinSandbox,
- user01sandbox.ebicsBankAccount.label,
- {
- debtorIban: "ES3314655813489414469157",
- debtorBic: "BCMAESM1XXX",
- debtorName: "Mock Donor",
- subject: "another Anastasis donation",
- amount: "1", // Sandbox takes currency from its "config"
- },
- )
-
- await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- );
-
- let txs = await LibeufinNexusApi.getAnastasisTransactions(
- libeufinServices.libeufinNexus,
- anastasisBaseUrl,
- {delta: 5},
- user01nexus.userReq.username,
- user01nexus.userReq.password,
- );
-
- // check the two payments show up
- let txsList = txs.data.incoming_transactions
- t.assertTrue(txsList.length == 2);
- t.assertTrue([txsList[0].subject, txsList[1].subject].includes("Anastasis donation"));
- t.assertTrue([txsList[0].subject, txsList[1].subject].includes("another Anastasis donation"));
- t.assertTrue(txsList[0].row_id == 1)
- t.assertTrue(txsList[1].row_id == 2)
-
- LibeufinSandboxApi.simulateIncomingTransaction(
- libeufinServices.libeufinSandbox,
- user01sandbox.ebicsBankAccount.label,
- {
- debtorIban: "ES3314655813489414469157",
- debtorBic: "BCMAESM1XXX",
- debtorName: "Mock Donor",
- subject: "last Anastasis donation",
- amount: "10.10", // Sandbox takes currency from its "config"
- },
- )
-
- await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- );
-
- let txsLast = await LibeufinNexusApi.getAnastasisTransactions(
- libeufinServices.libeufinNexus,
- anastasisBaseUrl,
- {delta: 5, start: 2},
- user01nexus.userReq.username,
- user01nexus.userReq.password,
- );
- console.log(txsLast.data.incoming_transactions[0].subject == "last Anastasis donation");
-
- let txsReverse = await LibeufinNexusApi.getAnastasisTransactions(
- libeufinServices.libeufinNexus,
- anastasisBaseUrl,
- {delta: -5, start: 4},
- user01nexus.userReq.username,
- user01nexus.userReq.password,
- );
- t.assertTrue(txsReverse.data.incoming_transactions[0].row_id == 3);
- t.assertTrue(txsReverse.data.incoming_transactions[1].row_id == 2);
- t.assertTrue(txsReverse.data.incoming_transactions[2].row_id == 1);
-}
-
-runLibeufinAnastasisFacadeTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-keyrotation.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-keyrotation.ts
deleted file mode 100644
index 5dc31f0bf..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-keyrotation.ts
+++ /dev/null
@@ -1,79 +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 } from "../harness/harness.js";
-import {
- SandboxUserBundle,
- NexusUserBundle,
- launchLibeufinServices,
- LibeufinSandboxApi,
- LibeufinNexusApi,
-} from "../harness/libeufin";
-
-/**
- * Run basic test with LibEuFin.
- */
-export async function runLibeufinKeyrotationTest(t: GlobalTestState) {
- /**
- * User saltetd "01"
- */
- const user01nexus = new NexusUserBundle(
- "01",
- "http://localhost:5010/ebicsweb",
- );
- const user01sandbox = new SandboxUserBundle("01");
-
- /**
- * Launch Sandbox and Nexus.
- */
- const libeufinServices = await launchLibeufinServices(
- t, [user01nexus], [user01sandbox],
- );
-
- await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- );
-
- /* Rotate the Sandbox keys, and fetch the transactions again */
- await LibeufinSandboxApi.rotateKeys(
- libeufinServices.libeufinSandbox,
- user01sandbox.ebicsBankAccount.subscriber.hostID,
- );
-
- try {
- await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- );
- } catch (e: any) {
- /**
- * Asserting that Nexus responded with a 500 Internal server
- * error, because the bank signed the last response with a new
- * key pair that was never downloaded by Nexus.
- *
- * NOTE: the bank accepted the request addressed to the old
- * public key. Should it in this case reject the request even
- * before trying to verify it?
- */
- t.assertTrue(e.response.status == 500);
- t.assertTrue(e.response.data.code == 9000);
- }
-}
-runLibeufinKeyrotationTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-nexus-balance.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-nexus-balance.ts
deleted file mode 100644
index 23d76081f..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-nexus-balance.ts
+++ /dev/null
@@ -1,114 +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, delayMs } from "../harness/harness.js";
-import {
- SandboxUserBundle,
- NexusUserBundle,
- launchLibeufinServices,
- LibeufinSandboxApi,
- LibeufinNexusApi,
-} from "../harness/libeufin";
-
-/**
- * This test checks how the C52 and C53 coordinate. It'll test
- * whether fresh transactions stop showing as C52 after they get
- * included in a bank statement.
- */
-export async function runLibeufinNexusBalanceTest(t: GlobalTestState) {
- /**
- * User saltetd "01"
- */
- const user01nexus = new NexusUserBundle(
- "01",
- "http://localhost:5010/ebicsweb",
- );
- const user01sandbox = new SandboxUserBundle("01");
-
- /**
- * User saltetd "02".
- */
- const user02nexus = new NexusUserBundle(
- "02",
- "http://localhost:5010/ebicsweb",
- );
- const user02sandbox = new SandboxUserBundle("02");
-
- /**
- * Launch Sandbox and Nexus.
- */
- const libeufinServices = await launchLibeufinServices(
- t,
- [user01nexus, user02nexus],
- [user01sandbox, user02sandbox],
- ["twg"],
- );
-
- // user 01 gets 10
- await libeufinServices.libeufinSandbox.makeTransaction(
- user02sandbox.ebicsBankAccount.label, // debit
- user01sandbox.ebicsBankAccount.label, // credit
- "EUR:10",
- "first payment",
- );
-
- // user 01 gets another 10
- await libeufinServices.libeufinSandbox.makeTransaction(
- user02sandbox.ebicsBankAccount.label, // debit
- user01sandbox.ebicsBankAccount.label, // credit
- "EUR:10",
- "first payment",
- );
-
- await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- "all", // range
- "report", // level
- );
-
- // Check that user 01 has 20, via Nexus.
- let accountInfo = await LibeufinNexusApi.getBankAccount(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName
- );
- t.assertTrue(accountInfo.data.lastSeenBalance == "EUR:20");
-
- // user 01 gives 30
- await libeufinServices.libeufinSandbox.makeTransaction(
- user01sandbox.ebicsBankAccount.label, // credit
- user02sandbox.ebicsBankAccount.label, // debit
- "EUR:30",
- "third payment",
- );
-
- await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- "all", // range
- "report", // level
- );
-
- let accountInfoDebit = await LibeufinNexusApi.getBankAccount(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName
- );
- t.assertTrue(accountInfoDebit.data.lastSeenBalance == "-EUR:10");
-}
-runLibeufinNexusBalanceTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund-multiple-users.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund-multiple-users.ts
deleted file mode 100644
index 39517f247..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund-multiple-users.ts
+++ /dev/null
@@ -1,104 +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, delayMs } from "../harness/harness.js";
-import {
- SandboxUserBundle,
- NexusUserBundle,
- launchLibeufinServices,
- LibeufinSandboxApi,
- LibeufinNexusApi,
-} from "../harness/libeufin";
-
-/**
- * User 01 expects a refund from user 02, and expectedly user 03
- * should not be involved in the process.
- */
-export async function runLibeufinRefundMultipleUsersTest(t: GlobalTestState) {
- /**
- * User saltetd "01"
- */
- const user01nexus = new NexusUserBundle(
- "01",
- "http://localhost:5010/ebicsweb",
- );
- const user01sandbox = new SandboxUserBundle("01");
-
- /**
- * User saltetd "02"
- */
- const user02nexus = new NexusUserBundle(
- "02",
- "http://localhost:5010/ebicsweb",
- );
- const user02sandbox = new SandboxUserBundle("02");
-
- /**
- * User saltetd "03"
- */
- const user03nexus = new NexusUserBundle(
- "03",
- "http://localhost:5010/ebicsweb",
- );
- const user03sandbox = new SandboxUserBundle("03");
-
- /**
- * Launch Sandbox and Nexus.
- */
- const libeufinServices = await launchLibeufinServices(
- t,
- [user01nexus, user02nexus],
- [user01sandbox, user02sandbox],
- ["twg"],
- );
-
- // user 01 gets the payment
- await libeufinServices.libeufinSandbox.makeTransaction(
- user02sandbox.ebicsBankAccount.label, // debit
- user01sandbox.ebicsBankAccount.label, // credit
- "EUR:1",
- "not a public key",
- );
-
- // user 01 fetches the payments
- await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- );
-
- // user 01 tries to submit the reimbursement, as
- // the payment didn't have a valid public key in
- // the subject.
- await LibeufinNexusApi.submitInitiatedPayment(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- "1", // so far the only one that can exist.
- );
-
- // user 02 checks whether a reimbursement arrived.
- let history = await LibeufinSandboxApi.getAccountTransactions(
- libeufinServices.libeufinSandbox,
- user02sandbox.ebicsBankAccount["label"],
- );
- // reimbursement arrived IFF the total payments are 2:
- // 1 the original (faulty) transaction + 1 the reimbursement.
- t.assertTrue(history["payments"].length == 2);
-}
-
-runLibeufinRefundMultipleUsersTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund.ts
deleted file mode 100644
index d91ae88bb..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-refund.ts
+++ /dev/null
@@ -1,101 +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, delayMs } from "../harness/harness.js";
-import {
- SandboxUserBundle,
- NexusUserBundle,
- launchLibeufinServices,
- LibeufinSandboxApi,
- LibeufinNexusApi,
-} from "../harness/libeufin";
-
-/**
- * Run basic test with LibEuFin.
- */
-export async function runLibeufinRefundTest(t: GlobalTestState) {
- /**
- * User saltetd "01"
- */
- const user01nexus = new NexusUserBundle(
- "01",
- "http://localhost:5010/ebicsweb",
- );
- const user01sandbox = new SandboxUserBundle("01");
-
- /**
- * User saltetd "02"
- */
- const user02nexus = new NexusUserBundle(
- "02",
- "http://localhost:5010/ebicsweb",
- );
- const user02sandbox = new SandboxUserBundle("02");
-
- /**
- * Launch Sandbox and Nexus.
- */
- const libeufinServices = await launchLibeufinServices(
- t,
- [user01nexus, user02nexus],
- [user01sandbox, user02sandbox],
- ["twg"],
- );
-
- // user 02 pays user 01 with a faulty (non Taler) subject.
- await libeufinServices.libeufinSandbox.makeTransaction(
- user02sandbox.ebicsBankAccount.label, // debit
- user01sandbox.ebicsBankAccount.label, // credit
- "EUR:1",
- "not a public key",
- );
-
- // The bad payment should be now ingested and prepared as
- // a reimbursement.
- await LibeufinNexusApi.fetchTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- );
- // Check that the payment arrived at the Nexus.
- const nexusTxs = await LibeufinNexusApi.getAccountTransactions(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- );
- t.assertTrue(nexusTxs.data["transactions"].length == 1);
-
- // Submit the reimbursement
- await LibeufinNexusApi.submitInitiatedPayment(
- libeufinServices.libeufinNexus,
- user01nexus.localAccountName,
- // The initiated payment (= the reimbursement) ID below
- // got set by the Taler facade; at this point only one must
- // exist. If "1" is not found, a 404 will make this test fail.
- "1",
- );
-
- // user 02 checks whether the reimbursement arrived.
- let history = await LibeufinSandboxApi.getAccountTransactions(
- libeufinServices.libeufinSandbox,
- user02sandbox.ebicsBankAccount["label"],
- );
- // 2 payments must exist: 1 the original (faulty) payment +
- // 1 the reimbursement.
- t.assertTrue(history["payments"].length == 2);
-}
-runLibeufinRefundTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-sandbox-wire-transfer-cli.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-sandbox-wire-transfer-cli.ts
deleted file mode 100644
index 5560f091a..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-sandbox-wire-transfer-cli.ts
+++ /dev/null
@@ -1,72 +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 } from "../harness/harness.js";
-import {
- NexusUserBundle,
- LibeufinNexusApi,
- LibeufinNexusService,
- LibeufinSandboxService,
- LibeufinSandboxApi,
- findNexusPayment,
-} from "../harness/libeufin";
-
-export async function runLibeufinSandboxWireTransferCliTest(t: GlobalTestState) {
-
- const sandbox = await LibeufinSandboxService.create(t, {
- httpPort: 5012,
- databaseJdbcUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`,
- });
- await sandbox.start();
- await sandbox.pingUntilAvailable();
- await LibeufinSandboxApi.createBankAccount(sandbox, {
- iban: "DE71500105179674997361",
- bic: "BELADEBEXXX",
- name: "Mock Name",
- label: "mock-account",
- currency: "EUR"
- });
-
- await LibeufinSandboxApi.createBankAccount(sandbox, {
- iban: "DE71500105179674997364",
- bic: "BELADEBEXXX",
- name: "Mock Name 2",
- label: "mock-account-2",
- currency: "EUR"
- });
- await sandbox.makeTransaction(
- "mock-account", "mock-account-2", "EUR:1", "one!"
- );
- await sandbox.makeTransaction(
- "mock-account", "mock-account-2", "EUR:1", "two!"
- );
- await sandbox.makeTransaction(
- "mock-account", "mock-account-2", "EUR:1", "three!"
- );
- await sandbox.makeTransaction(
- "mock-account-2", "mock-account", "EUR:1", "Give one back."
- );
- await sandbox.makeTransaction(
- "mock-account-2", "mock-account", "EUR:0.11", "Give fraction back."
- );
- let ret = await LibeufinSandboxApi.getAccountInfoWithBalance(sandbox, "mock-account-2");
- console.log(ret.data.balance)
- t.assertTrue(ret.data.balance == "EUR:1.89")
-}
-runLibeufinSandboxWireTransferCliTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-tutorial.ts b/packages/taler-wallet-cli/src/integrationtests/test-libeufin-tutorial.ts
deleted file mode 100644
index 71a1e8c4b..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-libeufin-tutorial.ts
+++ /dev/null
@@ -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 } from "../harness/harness.js";
-import {
- LibeufinNexusService,
- LibeufinSandboxService,
- LibeufinCli,
-} from "../harness/libeufin";
-
-/**
- * Run basic test with LibEuFin.
- */
-export async function runLibeufinTutorialTest(t: GlobalTestState) {
- // Set up test environment
-
- 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`,
- });
-
- const nexusUser = { username: "foo", password: "secret" };
- const libeufinCli = new LibeufinCli(t, {
- sandboxUrl: libeufinSandbox.baseUrl,
- nexusUrl: libeufinNexus.baseUrl,
- sandboxDatabaseUri: `jdbc:sqlite:${t.testDir}/libeufin-sandbox.sqlite3`,
- nexusDatabaseUri: `jdbc:sqlite:${t.testDir}/libeufin-nexus.sqlite3`,
- user: nexusUser,
- });
-
- const ebicsDetails = {
- hostId: "testhost",
- partnerId: "partner01",
- userId: "user01",
- };
- const bankAccountDetails = {
- currency: "EUR",
- iban: "DE18500105172929531888",
- bic: "INGDDEFFXXX",
- personName: "Jane Normal",
- accountName: "testacct01",
- };
-
- await libeufinCli.checkSandbox();
- await libeufinCli.createEbicsHost("testhost");
- await libeufinCli.createEbicsSubscriber(ebicsDetails);
- await libeufinCli.createEbicsBankAccount(ebicsDetails, bankAccountDetails);
- await libeufinCli.generateTransactions(bankAccountDetails.accountName);
-
- await libeufinNexus.start();
- await libeufinNexus.pingUntilAvailable();
-
- await libeufinNexus.createNexusSuperuser(nexusUser);
- const connectionDetails = {
- subscriberDetails: ebicsDetails,
- ebicsUrl: `${libeufinSandbox.baseUrl}ebicsweb`, // FIXME: need appropriate URL concatenation
- connectionName: "my-ebics-conn",
- };
- await libeufinCli.createEbicsConnection(connectionDetails);
- await libeufinCli.createBackupFile({
- passphrase: "secret",
- outputFile: `${t.testDir}/connection-backup.json`,
- connectionName: connectionDetails.connectionName,
- });
- await libeufinCli.createKeyLetter({
- outputFile: `${t.testDir}/letter.pdf`,
- connectionName: connectionDetails.connectionName,
- });
- await libeufinCli.connect(connectionDetails.connectionName);
- await libeufinCli.downloadBankAccounts(connectionDetails.connectionName);
- await libeufinCli.listOfferedBankAccounts(connectionDetails.connectionName);
-
- const bankAccountImportDetails = {
- offeredBankAccountName: bankAccountDetails.accountName,
- nexusBankAccountName: "at-nexus-testacct01",
- connectionName: connectionDetails.connectionName,
- };
-
- await libeufinCli.importBankAccount(bankAccountImportDetails);
- await libeufinSandbox.c53tick()
- await libeufinCli.fetchTransactions(bankAccountImportDetails.nexusBankAccountName);
- await libeufinCli.transactions(bankAccountImportDetails.nexusBankAccountName);
-
- const paymentDetails = {
- creditorIban: "DE42500105171245624648",
- creditorBic: "BELADEBEXXX",
- creditorName: "Mina Musterfrau",
- subject: "Purchase 01234",
- amount: "1.0",
- currency: "EUR",
- nexusBankAccountName: bankAccountImportDetails.nexusBankAccountName,
- };
- await libeufinCli.preparePayment(paymentDetails);
- await libeufinCli.submitPayment(paymentDetails, "1");
-
- await libeufinCli.newTalerWireGatewayFacade({
- accountName: bankAccountImportDetails.nexusBankAccountName,
- connectionName: "my-ebics-conn",
- currency: "EUR",
- facadeName: "my-twg",
- });
- await libeufinCli.listFacades();
-}
-runLibeufinTutorialTest.suites = ["libeufin"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-exchange-confusion.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-exchange-confusion.ts
deleted file mode 100644
index 8e8f966b9..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-exchange-confusion.ts
+++ /dev/null
@@ -1,244 +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 {
- BankService,
- ExchangeService,
- GlobalTestState,
- MerchantPrivateApi,
- MerchantService,
- setupDb,
- WalletCli,
-} from "../harness/harness.js";
-import {
- withdrawViaBank,
- createFaultInjectedMerchantTestkudosEnvironment,
- FaultyMerchantTestEnvironment,
-} from "../harness/helpers.js";
-import {
- PreparePayResultType,
- codecForMerchantOrderStatusUnpaid,
- ConfirmPayResultType,
-} from "@gnu-taler/taler-util";
-import axios from "axios";
-import {
- FaultInjectedExchangeService,
- FaultInjectedMerchantService,
- FaultInjectionRequestContext,
-} from "../harness/faultInjection";
-import { defaultCoinConfig } from "../harness/denomStructures";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { URL } from "url";
-
-/**
- * Run a test case with a simple TESTKUDOS Taler environment, consisting
- * of one exchange, one bank and one merchant.
- */
-export async function createConfusedMerchantTestkudosEnvironment(
- t: GlobalTestState,
-): Promise<FaultyMerchantTestEnvironment> {
- 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 faultyMerchant = new FaultInjectedMerchantService(t, merchant, 9083);
- const faultyExchange = new FaultInjectedExchangeService(t, exchange, 9081);
-
- const exchangeBankAccount = await bank.createExchangeAccount(
- "MyExchange",
- "x",
- );
- exchange.addBankAccount("1", exchangeBankAccount);
-
- bank.setSuggestedExchange(
- faultyExchange,
- exchangeBankAccount.accountPaytoUri,
- );
-
- await bank.start();
-
- await bank.pingUntilAvailable();
-
- exchange.addOfferedCoins(defaultCoinConfig);
-
- await exchange.start();
- await exchange.pingUntilAvailable();
-
- // Confuse the merchant by adding the non-proxied exchange.
- merchant.addExchange(exchange);
-
- await merchant.start();
- await merchant.pingUntilAvailable();
-
- await merchant.addInstance({
- id: "default",
- name: "Default Instance",
- paytoUris: [`payto://x-taler-bank/merchant-default`],
- });
-
- await merchant.addInstance({
- id: "minst1",
- name: "minst1",
- paytoUris: ["payto://x-taler-bank/minst1"],
- });
-
- console.log("setup done!");
-
- const wallet = new WalletCli(t);
-
- return {
- commonDb: db,
- exchange,
- merchant,
- wallet,
- bank,
- exchangeBankAccount,
- faultyMerchant,
- faultyExchange,
- };
-}
-
-/**
- * Confuse the merchant by having one URL for the same exchange in the config,
- * but sending coins from the same exchange with a different URL.
- */
-export async function runMerchantExchangeConfusionTest(t: GlobalTestState) {
- // Set up test environment
-
- const {
- wallet,
- bank,
- faultyExchange,
- faultyMerchant,
- } = await createConfusedMerchantTestkudosEnvironment(t);
-
- // Withdraw digital cash into the wallet.
-
- await withdrawViaBank(t, {
- wallet,
- bank,
- exchange: faultyExchange,
- amount: "TESTKUDOS:20",
- });
-
- /**
- * =========================================================================
- * Create an order and let the wallet pay under a session ID
- *
- * We check along the way that the JSON response to /orders/{order_id}
- * returns the right thing.
- * =========================================================================
- */
-
- const merchant = faultyMerchant;
-
- let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- },
- });
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- sessionId: "mysession-one",
- });
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- t.assertTrue(orderStatus.already_paid_order_id === undefined);
- let publicOrderStatusUrl = orderStatus.order_status_url;
-
- let publicOrderStatusResp = await axios.get(publicOrderStatusUrl, {
- validateStatus: () => true,
- });
-
- if (publicOrderStatusResp.status != 402) {
- throw Error(
- `expected status 402 (before claiming), but got ${publicOrderStatusResp.status}`,
- );
- }
-
- let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
- publicOrderStatusResp.data,
- );
-
- console.log(pubUnpaidStatus);
-
- let preparePayResp = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: pubUnpaidStatus.taler_pay_uri,
- },
- );
-
- t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible);
-
- const proposalId = preparePayResp.proposalId;
-
- const orderUrlWithHash = new URL(publicOrderStatusUrl);
- orderUrlWithHash.searchParams.set("h_contract", preparePayResp.contractTermsHash);
-
- console.log("requesting", orderUrlWithHash.href);
-
- publicOrderStatusResp = await axios.get(orderUrlWithHash.href, {
- validateStatus: () => true,
- });
-
- if (publicOrderStatusResp.status != 402) {
- throw Error(
- `expected status 402 (after claiming), but got ${publicOrderStatusResp.status}`,
- );
- }
-
- pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
- publicOrderStatusResp.data,
- );
-
- const confirmPayRes = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId: proposalId,
- },
- );
-
- t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
-}
-
-runMerchantExchangeConfusionTest.suites = ["merchant"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-delete.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-delete.ts
deleted file mode 100644
index 589c79120..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-delete.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- 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 { URL } from "@gnu-taler/taler-util";
-import axios from "axios";
-import {
- ExchangeService,
- GlobalTestState,
- MerchantApiClient,
- MerchantService,
- setupDb,
-} from "../harness/harness.js";
-
-/**
- * Test instance deletion and authentication for it
- */
-export async function runMerchantInstancesDeleteTest(t: GlobalTestState) {
- // Set up test environment
-
- const db = await setupDb(t);
-
- 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,
- });
-
- // We add the exchange to the config, but note that the exchange won't be started.
- merchant.addExchange(exchange);
-
- await merchant.start();
- await merchant.pingUntilAvailable();
-
- // Base URL for the default instance.
- const baseUrl = merchant.makeInstanceBaseUrl();
-
- {
- const r = await axios.get(new URL("config", baseUrl).href);
- console.log(r.data);
- t.assertDeepEqual(r.data.currency, "TESTKUDOS");
- }
-
- // Instances should initially be empty
- {
- const r = await axios.get(new URL("management/instances", baseUrl).href);
- t.assertDeepEqual(r.data.instances, []);
- }
-
- // Add an instance, no auth!
- await merchant.addInstance({
- id: "default",
- name: "Default Instance",
- paytoUris: [`payto://x-taler-bank/merchant-default`],
- auth: {
- method: "external",
- },
- });
-
- // Add an instance, no auth!
- await merchant.addInstance({
- id: "myinst",
- name: "Second Instance",
- paytoUris: [`payto://x-taler-bank/merchant-default`],
- auth: {
- method: "external",
- },
- });
-
- let merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl(), {
- method: "external",
- });
-
- await merchantClient.changeAuth({
- method: "token",
- token: "secret-token:foobar",
- });
-
- merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl(), {
- method: "token",
- token: "secret-token:foobar",
- });
-
- // Check that deleting an instance checks the auth
- // of the default instance.
- {
- const unauthMerchantClient = new MerchantApiClient(
- merchant.makeInstanceBaseUrl(),
- {
- method: "token",
- token: "secret-token:invalid",
- },
- );
-
- const exc = await t.assertThrowsAsync(async () => {
- await unauthMerchantClient.deleteInstance("myinst");
- });
- console.log("Got expected exception", exc);
- t.assertAxiosError(exc);
- t.assertDeepEqual(exc.response?.status, 401);
- }
-}
-
-runMerchantInstancesDeleteTest.suites = ["merchant"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-urls.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-urls.ts
deleted file mode 100644
index fc5e7305a..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances-urls.ts
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- 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 {
- ExchangeService,
- GlobalTestState,
- MerchantApiClient,
- MerchantService,
- setupDb,
-} from "../harness/harness.js";
-
-/**
- * Do basic checks on instance management and authentication.
- */
-export async function runMerchantInstancesUrlsTest(t: GlobalTestState) {
- // Set up test environment
-
- const db = await setupDb(t);
-
- 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,
- });
-
- merchant.addExchange(exchange);
-
- await merchant.start();
- await merchant.pingUntilAvailable();
-
- const clientForDefault = new MerchantApiClient(
- merchant.makeInstanceBaseUrl(),
- {
- method: "token",
- token: "secret-token:i-am-default",
- },
- );
-
- await clientForDefault.createInstance({
- id: "default",
- address: {},
- default_max_deposit_fee: "TESTKUDOS:1",
- default_max_wire_fee: "TESTKUDOS:1",
- default_pay_delay: { d_ms: 60000 },
- default_wire_fee_amortization: 1,
- default_wire_transfer_delay: { d_ms: 60000 },
- jurisdiction: {},
- name: "My Default Instance",
- payto_uris: ["payto://x-taler-bank/foo/bar"],
- auth: {
- method: "token",
- token: "secret-token:i-am-default",
- },
- });
-
- await clientForDefault.createInstance({
- id: "myinst",
- address: {},
- default_max_deposit_fee: "TESTKUDOS:1",
- default_max_wire_fee: "TESTKUDOS:1",
- default_pay_delay: { d_ms: 60000 },
- default_wire_fee_amortization: 1,
- default_wire_transfer_delay: { d_ms: 60000 },
- jurisdiction: {},
- name: "My Second Instance",
- payto_uris: ["payto://x-taler-bank/foo/bar"],
- auth: {
- method: "token",
- token: "secret-token:i-am-myinst",
- },
- });
-
- async function check(url: string, token: string, expectedStatus: number) {
- const resp = await axios.get(url, {
- headers: {
- Authorization: `Bearer ${token}`,
- },
- validateStatus: () => true,
- });
- console.log(
- `checking ${url}, expected ${expectedStatus}, got ${resp.status}`,
- );
- t.assertDeepEqual(resp.status, expectedStatus);
- }
-
- const tokDefault = "secret-token:i-am-default";
-
- const defaultBaseUrl = merchant.makeInstanceBaseUrl();
-
- await check(
- `${defaultBaseUrl}private/instances/default/instances/default/config`,
- tokDefault,
- 404,
- );
-
- // Instance management is only available when accessing the default instance
- // directly.
- await check(
- `${defaultBaseUrl}instances/default/private/instances`,
- "foo",
- 404,
- );
-
- // Non-default instances don't allow instance management.
- await check(`${defaultBaseUrl}instances/foo/private/instances`, "foo", 404);
- await check(
- `${defaultBaseUrl}instances/myinst/private/instances`,
- "foo",
- 404,
- );
-
- await check(`${defaultBaseUrl}config`, "foo", 200);
- await check(`${defaultBaseUrl}instances/default/config`, "foo", 200);
- await check(`${defaultBaseUrl}instances/myinst/config`, "foo", 200);
- await check(`${defaultBaseUrl}instances/foo/config`, "foo", 404);
- await check(
- `${defaultBaseUrl}instances/default/instances/config`,
- "foo",
- 404,
- );
-
- await check(
- `${defaultBaseUrl}private/instances/myinst/config`,
- tokDefault,
- 404,
- );
-
- await check(
- `${defaultBaseUrl}instances/myinst/private/orders`,
- tokDefault,
- 401,
- );
-
- await check(
- `${defaultBaseUrl}instances/myinst/private/orders`,
- tokDefault,
- 401,
- );
-
- await check(
- `${defaultBaseUrl}instances/myinst/private/orders`,
- "secret-token:i-am-myinst",
- 200,
- );
-
- await check(
- `${defaultBaseUrl}private/instances/myinst/orders`,
- tokDefault,
- 404,
- );
-}
-
-runMerchantInstancesUrlsTest.suites = ["merchant"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances.ts
deleted file mode 100644
index 46af87922..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-instances.ts
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- 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 { URL } from "@gnu-taler/taler-util";
-import axios from "axios";
-import {
- ExchangeService,
- GlobalTestState,
- MerchantApiClient,
- MerchantService,
- setupDb,
-} from "../harness/harness.js";
-
-/**
- * Do basic checks on instance management and authentication.
- */
-export async function runMerchantInstancesTest(t: GlobalTestState) {
- // Set up test environment
-
- const db = await setupDb(t);
-
- 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,
- });
-
- // We add the exchange to the config, but note that the exchange won't be started.
- merchant.addExchange(exchange);
-
- await merchant.start();
- await merchant.pingUntilAvailable();
-
- // Base URL for the default instance.
- const baseUrl = merchant.makeInstanceBaseUrl();
-
- {
- const r = await axios.get(new URL("config", baseUrl).href);
- console.log(r.data);
- t.assertDeepEqual(r.data.currency, "TESTKUDOS");
- }
-
- // Instances should initially be empty
- {
- const r = await axios.get(new URL("management/instances", baseUrl).href);
- t.assertDeepEqual(r.data.instances, []);
- }
-
- // Add an instance, no auth!
- await merchant.addInstance({
- id: "default",
- name: "Default Instance",
- paytoUris: [`payto://x-taler-bank/merchant-default`],
- auth: {
- method: "external",
- },
- });
-
- // Add an instance, no auth!
- await merchant.addInstance({
- id: "myinst",
- name: "Second Instance",
- paytoUris: [`payto://x-taler-bank/merchant-default`],
- auth: {
- method: "external",
- },
- });
-
- let merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl(), {
- method: "external",
- });
-
- {
- const r = await merchantClient.getInstances();
- t.assertDeepEqual(r.instances.length, 2);
- }
-
- // Check that a "malformed" bearer Authorization header gets ignored
- {
- const url = merchant.makeInstanceBaseUrl();
- const resp = await axios.get(new URL("management/instances", url).href, {
- headers: {
- Authorization: "foo bar-baz",
- },
- });
- t.assertDeepEqual(resp.status, 200);
- }
-
- {
- const fullDetails = await merchantClient.getInstanceFullDetails("default");
- t.assertDeepEqual(fullDetails.auth.method, "external");
- }
-
- await merchantClient.changeAuth({
- method: "token",
- token: "secret-token:foobar",
- });
-
- // Now this should fail, as we didn't change the auth of the client yet.
- const exc = await t.assertThrowsAsync(async () => {
- console.log("requesting instances with auth", merchantClient.auth);
- const resp = await merchantClient.getInstances();
- console.log("instances result:", resp);
- });
-
- console.log(exc);
-
- t.assertAxiosError(exc);
- t.assertTrue(exc.response?.status === 401);
-
- merchantClient = new MerchantApiClient(merchant.makeInstanceBaseUrl(), {
- method: "token",
- token: "secret-token:foobar",
- });
-
- // With the new client auth settings, request should work again.
- await merchantClient.getInstances();
-
- // Now, try some variations.
- {
- const url = merchant.makeInstanceBaseUrl();
- const resp = await axios.get(new URL("management/instances", url).href, {
- headers: {
- // Note the spaces
- Authorization: "Bearer secret-token:foobar",
- },
- });
- t.assertDeepEqual(resp.status, 200);
- }
-
- // Check that auth is reported properly
- {
- const fullDetails = await merchantClient.getInstanceFullDetails("default");
- t.assertDeepEqual(fullDetails.auth.method, "token");
- // Token should *not* be reported back.
- t.assertDeepEqual(fullDetails.auth.token, undefined);
- }
-
- // Check that deleting an instance checks the auth
- // of the default instance.
- {
- const unauthMerchantClient = new MerchantApiClient(
- merchant.makeInstanceBaseUrl(),
- {
- method: "external",
- },
- );
-
- const exc = await t.assertThrowsAsync(async () => {
- await unauthMerchantClient.deleteInstance("myinst");
- });
- console.log(exc);
- t.assertAxiosError(exc);
- t.assertDeepEqual(exc.response?.status, 401);
- }
-}
-
-runMerchantInstancesTest.suites = ["merchant"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-longpolling.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-longpolling.ts
deleted file mode 100644
index 556d9074e..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-longpolling.ts
+++ /dev/null
@@ -1,161 +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, MerchantPrivateApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
-import {
- PreparePayResultType,
- codecForMerchantOrderStatusUnpaid,
- ConfirmPayResultType,
- URL,
-} from "@gnu-taler/taler-util";
-import axios from "axios";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runMerchantLongpollingTest(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" });
-
- /**
- * =========================================================================
- * Create an order and let the wallet pay under a session ID
- *
- * We check along the way that the JSON response to /orders/{order_id}
- * returns the right thing.
- * =========================================================================
- */
-
- let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- },
- create_token: false,
- });
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- sessionId: "mysession-one",
- });
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- t.assertTrue(orderStatus.already_paid_order_id === undefined);
- let publicOrderStatusUrl = new URL(orderStatus.order_status_url);
-
- // First, request order status without longpolling
- {
- console.log("requesting", publicOrderStatusUrl.href);
- let publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, {
- validateStatus: () => true,
- });
-
- if (publicOrderStatusResp.status != 402) {
- throw Error(
- `expected status 402 (before claiming, no long polling), but got ${publicOrderStatusResp.status}`,
- );
- }
- }
-
- // Now do long-polling for half a second!
- publicOrderStatusUrl.searchParams.set("timeout_ms", "500");
-
- console.log("requesting", publicOrderStatusUrl.href);
- let publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, {
- validateStatus: () => true,
- });
-
- if (publicOrderStatusResp.status != 402) {
- throw Error(
- `expected status 402 (before claiming, with long-polling), but got ${publicOrderStatusResp.status}`,
- );
- }
-
- let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
- publicOrderStatusResp.data,
- );
-
- console.log(pubUnpaidStatus);
-
- /**
- * =========================================================================
- * Now actually pay, but WHILE a long poll is active!
- * =========================================================================
- */
-
- let preparePayResp = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: pubUnpaidStatus.taler_pay_uri,
- },
- );
-
- t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible);
-
- publicOrderStatusUrl.searchParams.set("timeout_ms", "5000");
- publicOrderStatusUrl.searchParams.set(
- "h_contract",
- preparePayResp.contractTermsHash,
- );
-
- let publicOrderStatusPromise = axios.get(publicOrderStatusUrl.href, {
- validateStatus: () => true,
- });
-
- t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible);
-
- const proposalId = preparePayResp.proposalId;
-
- publicOrderStatusResp = await publicOrderStatusPromise;
-
- if (publicOrderStatusResp.status != 402) {
- throw Error(
- `expected status 402 (after claiming), but got ${publicOrderStatusResp.status}`,
- );
- }
-
- pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
- publicOrderStatusResp.data,
- );
-
- const confirmPayRes = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId: proposalId,
- },
- );
-
- t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
-}
-
-runMerchantLongpollingTest.suites = ["merchant"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-refund-api.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-refund-api.ts
deleted file mode 100644
index 466b1efbd..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-refund-api.ts
+++ /dev/null
@@ -1,296 +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,
- MerchantPrivateApi,
- BankServiceInterface,
- MerchantServiceInterface,
- WalletCli,
- ExchangeServiceInterface,
-} from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
-import {
- URL,
- durationFromSpec,
- PreparePayResultType,
-} from "@gnu-taler/taler-util";
-import axios from "axios";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-async function testRefundApiWithFulfillmentUrl(
- t: GlobalTestState,
- env: {
- merchant: MerchantServiceInterface;
- bank: BankServiceInterface;
- wallet: WalletCli;
- exchange: ExchangeServiceInterface;
- },
-): Promise<void> {
- const { wallet, bank, exchange, merchant } = env;
-
- // Set up order.
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/fulfillment",
- },
- refund_delay: durationFromSpec({ minutes: 5 }),
- });
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- const talerPayUri = orderStatus.taler_pay_uri;
- const orderId = orderResp.order_id;
-
- // Make wallet pay for the order
-
- let preparePayResult = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri,
- },
- );
-
- t.assertTrue(
- preparePayResult.status === PreparePayResultType.PaymentPossible,
- );
-
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: preparePayResult.proposalId,
- });
-
- // Check if payment was successful.
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "paid");
-
- preparePayResult = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri,
- },
- );
-
- t.assertTrue(
- preparePayResult.status === PreparePayResultType.AlreadyConfirmed,
- );
-
- await MerchantPrivateApi.giveRefund(merchant, {
- amount: "TESTKUDOS:5",
- instance: "default",
- justification: "foo",
- orderId: orderResp.order_id,
- });
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "paid");
-
- t.assertAmountEquals(orderStatus.refund_amount, "TESTKUDOS:5");
-
- // Now test what the merchant gives as a response for various requests to the
- // public order status URL!
-
- let publicOrderStatusUrl = new URL(
- `orders/${orderId}`,
- merchant.makeInstanceBaseUrl(),
- );
- publicOrderStatusUrl.searchParams.set(
- "h_contract",
- preparePayResult.contractTermsHash,
- );
-
- let publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, {
- validateStatus: () => true,
- });
- console.log(publicOrderStatusResp.data);
- t.assertTrue(publicOrderStatusResp.status === 200);
- t.assertAmountEquals(publicOrderStatusResp.data.refund_amount, "TESTKUDOS:5");
-
- publicOrderStatusUrl = new URL(
- `orders/${orderId}`,
- merchant.makeInstanceBaseUrl(),
- );
- console.log(`requesting order status via '${publicOrderStatusUrl.href}'`);
- publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, {
- validateStatus: () => true,
- });
- console.log(publicOrderStatusResp.status);
- console.log(publicOrderStatusResp.data);
- // We didn't give any authentication, so we should get a fulfillment URL back
- t.assertTrue(publicOrderStatusResp.status === 403);
-}
-
-async function testRefundApiWithFulfillmentMessage(
- t: GlobalTestState,
- env: {
- merchant: MerchantServiceInterface;
- bank: BankServiceInterface;
- wallet: WalletCli;
- exchange: ExchangeServiceInterface;
- },
-): Promise<void> {
- const { wallet, bank, exchange, merchant } = env;
-
- // Set up order.
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_message: "Thank you for buying foobar",
- },
- refund_delay: durationFromSpec({ minutes: 5 }),
- });
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- const talerPayUri = orderStatus.taler_pay_uri;
- const orderId = orderResp.order_id;
-
- // Make wallet pay for the order
-
- let preparePayResult = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri,
- },
- );
-
- t.assertTrue(
- preparePayResult.status === PreparePayResultType.PaymentPossible,
- );
-
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: preparePayResult.proposalId,
- });
-
- // Check if payment was successful.
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "paid");
-
- preparePayResult = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri,
- },
- );
-
- t.assertTrue(
- preparePayResult.status === PreparePayResultType.AlreadyConfirmed,
- );
-
- await MerchantPrivateApi.giveRefund(merchant, {
- amount: "TESTKUDOS:5",
- instance: "default",
- justification: "foo",
- orderId: orderResp.order_id,
- });
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "paid");
-
- t.assertAmountEquals(orderStatus.refund_amount, "TESTKUDOS:5");
-
- // Now test what the merchant gives as a response for various requests to the
- // public order status URL!
-
- let publicOrderStatusUrl = new URL(
- `orders/${orderId}`,
- merchant.makeInstanceBaseUrl(),
- );
- publicOrderStatusUrl.searchParams.set(
- "h_contract",
- preparePayResult.contractTermsHash,
- );
-
- let publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, {
- validateStatus: () => true,
- });
- console.log(publicOrderStatusResp.data);
- t.assertTrue(publicOrderStatusResp.status === 200);
- t.assertAmountEquals(publicOrderStatusResp.data.refund_amount, "TESTKUDOS:5");
-
- publicOrderStatusUrl = new URL(
- `orders/${orderId}`,
- merchant.makeInstanceBaseUrl(),
- );
-
- publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, {
- validateStatus: () => true,
- });
- console.log(publicOrderStatusResp.data);
- // We didn't give any authentication, so we should get a fulfillment URL back
- t.assertTrue(publicOrderStatusResp.status === 403);
-}
-
-/**
- * Test case for the refund API of the merchant backend.
- */
-export async function runMerchantRefundApiTest(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" });
-
- await testRefundApiWithFulfillmentUrl(t, {
- wallet,
- bank,
- exchange,
- merchant,
- });
-
- await testRefundApiWithFulfillmentMessage(t, {
- wallet,
- bank,
- exchange,
- merchant,
- });
-}
-
-runMerchantRefundApiTest.suites = ["merchant"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-merchant-spec-public-orders.ts b/packages/taler-wallet-cli/src/integrationtests/test-merchant-spec-public-orders.ts
deleted file mode 100644
index 70edaaf0c..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-merchant-spec-public-orders.ts
+++ /dev/null
@@ -1,620 +0,0 @@
-/*
- 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 {
- ConfirmPayResultType,
- PreparePayResultType,
- URL,
- encodeCrock,
- getRandomBytes,
-} from "@gnu-taler/taler-util";
-import { NodeHttpLib, WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import {
- BankService,
- ExchangeService,
- GlobalTestState,
- MerchantPrivateApi,
- MerchantService,
- WalletCli,
-} from "../harness/harness.js";
-import {
- createSimpleTestkudosEnvironment,
- withdrawViaBank,
-} from "../harness/helpers.js";
-
-const httpLib = new NodeHttpLib();
-
-interface Context {
- merchant: MerchantService;
- merchantBaseUrl: string;
- bank: BankService;
- exchange: ExchangeService;
-}
-
-async function testWithClaimToken(
- t: GlobalTestState,
- c: Context,
-): Promise<void> {
- const wallet = new WalletCli(t, "withclaimtoken");
- const { bank, exchange } = c;
- const { merchant, merchantBaseUrl } = c;
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
- const sessionId = "mysession";
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
- });
-
- const claimToken = orderResp.token;
- const orderId = orderResp.order_id;
- t.assertTrue(!!claimToken);
- let talerPayUri: string;
-
- {
- const httpResp = await httpLib.get(
- new URL(`orders/${orderId}`, merchantBaseUrl).href,
- );
- const r = await httpResp.json();
- t.assertDeepEqual(httpResp.status, 202);
- console.log(r);
- }
-
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("token", claimToken);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- t.assertDeepEqual(httpResp.status, 402);
- console.log(r);
- talerPayUri = r.taler_pay_uri;
- t.assertTrue(!!talerPayUri);
- }
-
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("token", claimToken);
- const httpResp = await httpLib.get(url.href, {
- headers: {
- Accept: "text/html",
- },
- });
- const r = await httpResp.text();
- t.assertDeepEqual(httpResp.status, 402);
- console.log(r);
- }
-
- const preparePayResp = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri,
- },
- );
-
- t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible);
- const contractTermsHash = preparePayResp.contractTermsHash;
- const proposalId = preparePayResp.proposalId;
-
- // claimed, unpaid, access with wrong h_contract
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const hcWrong = encodeCrock(getRandomBytes(64));
- url.searchParams.set("h_contract", hcWrong);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // claimed, unpaid, access with wrong claim token
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const ctWrong = encodeCrock(getRandomBytes(16));
- url.searchParams.set("token", ctWrong);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // claimed, unpaid, access with correct claim token
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("token", claimToken);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- // claimed, unpaid, access with correct contract terms hash
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("h_contract", contractTermsHash);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- // claimed, unpaid, access without credentials
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 202);
- }
-
- const confirmPayRes = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId: proposalId,
- },
- );
-
- t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
-
- // paid, access without credentials
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 202);
- }
-
- // paid, access with wrong h_contract
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const hcWrong = encodeCrock(getRandomBytes(64));
- url.searchParams.set("h_contract", hcWrong);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // paid, access with wrong claim token
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const ctWrong = encodeCrock(getRandomBytes(16));
- url.searchParams.set("token", ctWrong);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // paid, access with correct h_contract
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("h_contract", contractTermsHash);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 200);
- }
-
- // paid, access with correct claim token, JSON
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("token", claimToken);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 200);
- const respFulfillmentUrl = r.fulfillment_url;
- t.assertDeepEqual(respFulfillmentUrl, "https://example.com/article42");
- }
-
- // paid, access with correct claim token, HTML
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("token", claimToken);
- const httpResp = await httpLib.get(url.href, {
- headers: { Accept: "text/html" },
- });
- t.assertDeepEqual(httpResp.status, 200);
- }
-
- const confirmPayRes2 = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId: proposalId,
- sessionId: sessionId,
- },
- );
-
- t.assertTrue(confirmPayRes2.type === ConfirmPayResultType.Done);
-
- // Create another order with identical fulfillment URL to test the "already paid" flow
- const alreadyPaidOrderResp = await MerchantPrivateApi.createOrder(
- merchant,
- "default",
- {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
- },
- );
-
- const apOrderId = alreadyPaidOrderResp.order_id;
- const apToken = alreadyPaidOrderResp.token;
- t.assertTrue(!!apToken);
-
- {
- const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);
- url.searchParams.set("token", apToken);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- // Check for already paid session ID, JSON
- {
- const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);
- url.searchParams.set("token", apToken);
- url.searchParams.set("session_id", sessionId);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- const alreadyPaidOrderId = r.already_paid_order_id;
- t.assertDeepEqual(alreadyPaidOrderId, orderId);
- }
-
- // Check for already paid session ID, HTML
- {
- const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);
- url.searchParams.set("token", apToken);
- url.searchParams.set("session_id", sessionId);
- const httpResp = await httpLib.get(url.href, {
- headers: { Accept: "text/html" },
- });
- t.assertDeepEqual(httpResp.status, 302);
- const location = httpResp.headers.get("Location");
- console.log("location header:", location);
- t.assertDeepEqual(location, "https://example.com/article42");
- }
-}
-
-async function testWithoutClaimToken(
- t: GlobalTestState,
- c: Context,
-): Promise<void> {
- const wallet = new WalletCli(t, "withoutct");
- const sessionId = "mysession2";
- const { bank, exchange } = c;
- const { merchant, merchantBaseUrl } = c;
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
- create_token: false,
- });
-
- const orderId = orderResp.order_id;
- let talerPayUri: string;
-
- {
- const httpResp = await httpLib.get(
- new URL(`orders/${orderId}`, merchantBaseUrl).href,
- );
- const r = await httpResp.json();
- t.assertDeepEqual(httpResp.status, 402);
- console.log(r);
- }
-
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- t.assertDeepEqual(httpResp.status, 402);
- console.log(r);
- talerPayUri = r.taler_pay_uri;
- t.assertTrue(!!talerPayUri);
- }
-
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.get(url.href, {
- headers: {
- Accept: "text/html",
- },
- });
- const r = await httpResp.text();
- t.assertDeepEqual(httpResp.status, 402);
- console.log(r);
- }
-
- const preparePayResp = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri,
- },
- );
-
- console.log(preparePayResp);
-
- t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible);
- const contractTermsHash = preparePayResp.contractTermsHash;
- const proposalId = preparePayResp.proposalId;
-
- // claimed, unpaid, access with wrong h_contract
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const hcWrong = encodeCrock(getRandomBytes(64));
- url.searchParams.set("h_contract", hcWrong);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // claimed, unpaid, access with wrong claim token
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const ctWrong = encodeCrock(getRandomBytes(16));
- url.searchParams.set("token", ctWrong);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // claimed, unpaid, no claim token
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- // claimed, unpaid, access with correct contract terms hash
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("h_contract", contractTermsHash);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- // claimed, unpaid, access without credentials
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- // No credentials, but the order doesn't require a claim token.
- // This effectively means that the order ID is already considered
- // enough authentication, at least to check for the basic order status
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- const confirmPayRes = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId: proposalId,
- },
- );
-
- t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
-
- // paid, access without credentials
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 200);
- }
-
- // paid, access with wrong h_contract
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const hcWrong = encodeCrock(getRandomBytes(64));
- url.searchParams.set("h_contract", hcWrong);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // paid, access with wrong claim token
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const ctWrong = encodeCrock(getRandomBytes(16));
- url.searchParams.set("token", ctWrong);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 403);
- }
-
- // paid, access with correct h_contract
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- url.searchParams.set("h_contract", contractTermsHash);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 200);
- }
-
- // paid, JSON
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 200);
- const respFulfillmentUrl = r.fulfillment_url;
- t.assertDeepEqual(respFulfillmentUrl, "https://example.com/article42");
- }
-
- // paid, HTML
- {
- const url = new URL(`orders/${orderId}`, merchantBaseUrl);
- const httpResp = await httpLib.get(url.href, {
- headers: { Accept: "text/html" },
- });
- t.assertDeepEqual(httpResp.status, 200);
- }
-
- const confirmPayRes2 = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId: proposalId,
- sessionId: sessionId,
- },
- );
-
- t.assertTrue(confirmPayRes2.type === ConfirmPayResultType.Done);
-
- // Create another order with identical fulfillment URL to test the "already paid" flow
- const alreadyPaidOrderResp = await MerchantPrivateApi.createOrder(
- merchant,
- "default",
- {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
- },
- );
-
- const apOrderId = alreadyPaidOrderResp.order_id;
- const apToken = alreadyPaidOrderResp.token;
- t.assertTrue(!!apToken);
-
- {
- const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);
- url.searchParams.set("token", apToken);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- }
-
- // Check for already paid session ID, JSON
- {
- const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);
- url.searchParams.set("token", apToken);
- url.searchParams.set("session_id", sessionId);
- const httpResp = await httpLib.get(url.href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 402);
- const alreadyPaidOrderId = r.already_paid_order_id;
- t.assertDeepEqual(alreadyPaidOrderId, orderId);
- }
-
- // Check for already paid session ID, HTML
- {
- const url = new URL(`orders/${apOrderId}`, merchantBaseUrl);
- url.searchParams.set("token", apToken);
- url.searchParams.set("session_id", sessionId);
- const httpResp = await httpLib.get(url.href, {
- headers: { Accept: "text/html" },
- });
- t.assertDeepEqual(httpResp.status, 302);
- const location = httpResp.headers.get("Location");
- console.log("location header:", location);
- t.assertDeepEqual(location, "https://example.com/article42");
- }
-}
-
-/**
- * Checks for the /orders/{id} endpoint of the merchant.
- *
- * The tests here should exercise all code paths in the executable
- * specification of the endpoint.
- */
-export async function runMerchantSpecPublicOrdersTest(t: GlobalTestState) {
- const { bank, exchange, merchant } = await createSimpleTestkudosEnvironment(
- t,
- );
-
- // Base URL for the default instance.
- const merchantBaseUrl = merchant.makeInstanceBaseUrl();
-
- {
- const httpResp = await httpLib.get(new URL("config", merchantBaseUrl).href);
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(r.currency, "TESTKUDOS");
- }
-
- {
- const httpResp = await httpLib.get(
- new URL("orders/foo", merchantBaseUrl).href,
- );
- const r = await httpResp.json();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 404);
- // FIXME: also check Taler error code
- }
-
- {
- const httpResp = await httpLib.get(
- new URL("orders/foo", merchantBaseUrl).href,
- {
- headers: {
- Accept: "text/html",
- },
- },
- );
- const r = await httpResp.text();
- console.log(r);
- t.assertDeepEqual(httpResp.status, 404);
- // FIXME: also check Taler error code
- }
-
- await testWithClaimToken(t, {
- merchant,
- merchantBaseUrl,
- exchange,
- bank,
- });
-
- await testWithoutClaimToken(t, {
- merchant,
- merchantBaseUrl,
- exchange,
- bank,
- });
-}
-
-runMerchantSpecPublicOrdersTest.suites = ["merchant"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-pay-abort.ts b/packages/taler-wallet-cli/src/integrationtests/test-pay-abort.ts
deleted file mode 100644
index 0fa9ec81d..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-pay-abort.ts
+++ /dev/null
@@ -1,156 +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/>
- */
-
-/**
- * Fault injection test to check aborting partial payment
- * via refunds.
- */
-
-/**
- * Imports.
- */
-import { URL, PreparePayResultType, TalerErrorCode } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import {
- FaultInjectionRequestContext,
- FaultInjectionResponseContext,
-} from "../harness/faultInjection";
-import { GlobalTestState, MerchantPrivateApi, setupDb } from "../harness/harness.js";
-import {
- createFaultInjectedMerchantTestkudosEnvironment,
- withdrawViaBank,
-} from "../harness/helpers.js";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runPayAbortTest(t: GlobalTestState) {
- const {
- bank,
- faultyExchange,
- wallet,
- faultyMerchant,
- } = await createFaultInjectedMerchantTestkudosEnvironment(t);
- // Set up test environment
-
- await withdrawViaBank(t, {
- wallet,
- exchange: faultyExchange,
- amount: "TESTKUDOS:20",
- bank,
- });
-
- const orderResp = await MerchantPrivateApi.createOrder(
- faultyMerchant,
- "default",
- {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:15",
- fulfillment_url: "taler://fulfillment-success/thx",
- },
- },
- );
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- faultyMerchant,
- {
- orderId: orderResp.order_id,
- },
- );
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- // Make wallet pay for the order
-
- const preparePayResult = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: orderStatus.taler_pay_uri,
- },
- );
-
- t.assertTrue(
- preparePayResult.status === PreparePayResultType.PaymentPossible,
- );
-
- // We let only the first deposit through!
- let firstDepositUrl: string | undefined;
-
- faultyExchange.faultProxy.addFault({
- async modifyRequest(ctx: FaultInjectionRequestContext) {
- const url = new URL(ctx.requestUrl);
- if (url.pathname.endsWith("/deposit")) {
- if (!firstDepositUrl) {
- firstDepositUrl = url.href;
- return;
- }
- if (url.href != firstDepositUrl) {
- url.pathname = "/doesntexist";
- ctx.requestUrl = url.href;
- }
- }
- },
- async modifyResponse(ctx: FaultInjectionResponseContext) {
- const url = new URL(ctx.request.requestUrl);
- if (url.pathname.endsWith("/deposit") && url.href != firstDepositUrl) {
- ctx.responseBody = Buffer.from("{}");
- ctx.statusCode = 500;
- }
- },
- });
-
- faultyMerchant.faultProxy.addFault({
- async modifyResponse(ctx: FaultInjectionResponseContext) {
- const url = new URL(ctx.request.requestUrl);
- if (url.pathname.endsWith("/pay") && url.href != firstDepositUrl) {
- ctx.responseBody = Buffer.from("{}");
- ctx.statusCode = 400;
- }
- },
- });
-
- await t.assertThrowsOperationErrorAsync(async () => {
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: preparePayResult.proposalId,
- });
- });
-
- let txr = await wallet.client.call(WalletApiOperation.GetTransactions, {});
- console.log(JSON.stringify(txr, undefined, 2));
-
- t.assertDeepEqual(txr.transactions[1].type, "payment");
- t.assertDeepEqual(txr.transactions[1].pending, true);
- t.assertDeepEqual(
- txr.transactions[1].error?.code,
- TalerErrorCode.WALLET_RECEIVED_MALFORMED_RESPONSE,
- );
-
- await wallet.client.call(WalletApiOperation.AbortFailedPayWithRefund, {
- proposalId: preparePayResult.proposalId,
- });
-
- await wallet.runUntilDone();
-
- txr = await wallet.client.call(WalletApiOperation.GetTransactions, {});
- console.log(JSON.stringify(txr, undefined, 2));
-
- const txTypes = txr.transactions.map((x) => x.type);
-
- t.assertDeepEqual(txTypes, ["withdrawal", "payment", "refund"]);
-}
-
-runPayAbortTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-pay-paid.ts b/packages/taler-wallet-cli/src/integrationtests/test-pay-paid.ts
deleted file mode 100644
index 2d291ddd3..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-pay-paid.ts
+++ /dev/null
@@ -1,225 +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, MerchantPrivateApi } from "../harness/harness.js";
-import {
- withdrawViaBank,
- createFaultInjectedMerchantTestkudosEnvironment,
-} from "../harness/helpers.js";
-import {
- PreparePayResultType,
- codecForMerchantOrderStatusUnpaid,
- ConfirmPayResultType,
- URL,
-} from "@gnu-taler/taler-util";
-import axios from "axios";
-import { FaultInjectionRequestContext } from "../harness/faultInjection";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Run test for the wallets repurchase detection mechanism
- * based on the fulfillment URL.
- *
- * FIXME: This test is now almost the same as test-paywall-flow,
- * since we can't initiate payment via a "claimed" private order status
- * response.
- */
-export async function runPayPaidTest(t: GlobalTestState) {
- // Set up test environment
-
- const {
- wallet,
- bank,
- faultyExchange,
- faultyMerchant,
- } = await createFaultInjectedMerchantTestkudosEnvironment(t);
-
- // Withdraw digital cash into the wallet.
-
- await withdrawViaBank(t, {
- wallet,
- bank,
- exchange: faultyExchange,
- amount: "TESTKUDOS:20",
- });
-
- /**
- * =========================================================================
- * Create an order and let the wallet pay under a session ID
- *
- * We check along the way that the JSON response to /orders/{order_id}
- * returns the right thing.
- * =========================================================================
- */
-
- const merchant = faultyMerchant;
-
- let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
- });
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- sessionId: "mysession-one",
- });
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- t.assertTrue(orderStatus.already_paid_order_id === undefined);
- let publicOrderStatusUrl = orderStatus.order_status_url;
-
- let publicOrderStatusResp = await axios.get(publicOrderStatusUrl, {
- validateStatus: () => true,
- });
-
- if (publicOrderStatusResp.status != 402) {
- throw Error(
- `expected status 402 (before claiming), but got ${publicOrderStatusResp.status}`,
- );
- }
-
- let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
- publicOrderStatusResp.data,
- );
-
- console.log(pubUnpaidStatus);
-
- let preparePayResp = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: pubUnpaidStatus.taler_pay_uri,
- },
- );
-
- t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible);
-
- const proposalId = preparePayResp.proposalId;
-
- publicOrderStatusResp = await axios.get(publicOrderStatusUrl, {
- validateStatus: () => true,
- });
-
- if (publicOrderStatusResp.status != 402) {
- throw Error(
- `expected status 402 (after claiming), but got ${publicOrderStatusResp.status}`,
- );
- }
-
- pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
- publicOrderStatusResp.data,
- );
-
- const confirmPayRes = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId: proposalId,
- },
- );
-
- t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
-
- publicOrderStatusResp = await axios.get(publicOrderStatusUrl, {
- validateStatus: () => true,
- });
-
- console.log(publicOrderStatusResp.data);
-
- if (publicOrderStatusResp.status != 200) {
- console.log(publicOrderStatusResp.data);
- throw Error(
- `expected status 200 (after paying), but got ${publicOrderStatusResp.status}`,
- );
- }
-
- /**
- * =========================================================================
- * Now change up the session ID and do payment re-play!
- * =========================================================================
- */
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- sessionId: "mysession-two",
- });
-
- console.log(
- "order status under mysession-two:",
- JSON.stringify(orderStatus, undefined, 2),
- );
-
- // Should be claimed (not paid!) because of a new session ID
- t.assertTrue(orderStatus.order_status === "claimed");
-
- let numPayRequested = 0;
- let numPaidRequested = 0;
-
- faultyMerchant.faultProxy.addFault({
- async modifyRequest(ctx: FaultInjectionRequestContext) {
- const url = new URL(ctx.requestUrl);
- if (url.pathname.endsWith("/pay")) {
- numPayRequested++;
- } else if (url.pathname.endsWith("/paid")) {
- numPaidRequested++;
- }
- },
- });
-
- let orderRespTwo = await MerchantPrivateApi.createOrder(merchant, "default", {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
- });
-
- let orderStatusTwo = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderRespTwo.order_id,
- sessionId: "mysession-two",
- },
- );
-
- t.assertTrue(orderStatusTwo.order_status === "unpaid");
-
- // Pay with new taler://pay URI, which should
- // have the new session ID!
- // Wallet should now automatically re-play payment.
- preparePayResp = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: orderStatusTwo.taler_pay_uri,
- },
- );
-
- t.assertTrue(preparePayResp.status === PreparePayResultType.AlreadyConfirmed);
- t.assertTrue(preparePayResp.paid);
-
- // Make sure the wallet is actually doing the replay properly.
- t.assertTrue(numPaidRequested == 1);
- t.assertTrue(numPayRequested == 0);
-}
-
-runPayPaidTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-claim.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-claim.ts
deleted file mode 100644
index ba3bd8e0a..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-payment-claim.ts
+++ /dev/null
@@ -1,109 +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, MerchantPrivateApi, WalletCli } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
-import { PreparePayResultType } from "@gnu-taler/taler-util";
-import { TalerErrorCode } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runPaymentClaimTest(t: GlobalTestState) {
- // Set up test environment
-
- const {
- wallet,
- bank,
- exchange,
- merchant,
- } = await createSimpleTestkudosEnvironment(t);
-
- const walletTwo = new WalletCli(t, "two");
-
- // 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",
- },
- });
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- const talerPayUri = orderStatus.taler_pay_uri;
-
- // Make wallet pay for the order
-
- const preparePayResult = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri,
- },
- );
-
- t.assertTrue(
- preparePayResult.status === PreparePayResultType.PaymentPossible,
- );
-
- t.assertThrowsOperationErrorAsync(async () => {
- await walletTwo.client.call(WalletApiOperation.PreparePayForUri, {
- talerPayUri,
- });
- });
-
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: preparePayResult.proposalId,
- });
-
- // Check if payment was successful.
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "paid");
-
- walletTwo.deleteDatabase();
-
- const err = await t.assertThrowsOperationErrorAsync(async () => {
- await walletTwo.client.call(WalletApiOperation.PreparePayForUri, {
- talerPayUri,
- });
- });
-
- t.assertTrue(
- err.operationError.code === TalerErrorCode.WALLET_ORDER_ALREADY_CLAIMED,
- );
-
- await t.shutdown();
-}
-
-runPaymentClaimTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-fault.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-fault.ts
deleted file mode 100644
index 2be01d919..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-payment-fault.ts
+++ /dev/null
@@ -1,217 +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/>
- */
-
-/**
- * Sample fault injection test.
- */
-
-/**
- * Imports.
- */
-import {
- GlobalTestState,
- MerchantService,
- ExchangeService,
- setupDb,
- BankService,
- WalletCli,
- MerchantPrivateApi,
- BankApi,
- BankAccessApi,
-} from "../harness/harness.js";
-import {
- FaultInjectedExchangeService,
- FaultInjectionRequestContext,
- FaultInjectionResponseContext,
-} from "../harness/faultInjection";
-import { CoreApiResponse } from "@gnu-taler/taler-util";
-import { defaultCoinConfig } from "../harness/denomStructures";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runPaymentFaultTest(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 exchangeBankAccount = await bank.createExchangeAccount(
- "MyExchange",
- "x",
- );
-
- bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri);
-
- await bank.start();
-
- await bank.pingUntilAvailable();
-
- await exchange.addBankAccount("1", exchangeBankAccount);
- exchange.addOfferedCoins(defaultCoinConfig);
-
- await exchange.start();
- await exchange.pingUntilAvailable();
-
- const faultyExchange = new FaultInjectedExchangeService(t, exchange, 8091);
-
- // Print all requests to the exchange
- faultyExchange.faultProxy.addFault({
- async modifyRequest(ctx: FaultInjectionRequestContext) {
- console.log("got request", ctx);
- },
- async modifyResponse(ctx: FaultInjectionResponseContext) {
- console.log("got response", ctx);
- },
- });
-
- const merchant = await MerchantService.create(t, {
- name: "testmerchant-1",
- currency: "TESTKUDOS",
- httpPort: 8083,
- database: db.connStr,
- });
-
- merchant.addExchange(faultyExchange);
-
- await merchant.start();
- await merchant.pingUntilAvailable();
-
- await merchant.addInstance({
- id: "default",
- name: "Default Instance",
- paytoUris: [`payto://x-taler-bank/merchant-default`],
- });
-
- console.log("setup done!");
-
- const wallet = new WalletCli(t);
-
- // Create withdrawal operation
-
- const user = await BankApi.createRandomBankUser(bank);
- const wop = await BankAccessApi.createWithdrawalOperation(
- bank,
- user,
- "TESTKUDOS:20",
- );
-
- // Hand it to the wallet
-
- await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, {
- talerWithdrawUri: wop.taler_withdraw_uri,
- });
-
- await wallet.runPending();
-
- // Confirm it
-
- await BankApi.confirmWithdrawalOperation(bank, user, wop);
-
- // Withdraw
-
- await wallet.client.call(WalletApiOperation.AcceptBankIntegratedWithdrawal, {
- exchangeBaseUrl: faultyExchange.baseUrl,
- talerWithdrawUri: wop.taler_withdraw_uri,
- });
- await wallet.runUntilDone();
-
- // Check balance
-
- await wallet.client.call(WalletApiOperation.GetBalances, {});
-
- // Set up order.
-
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- 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
-
- let apiResp: CoreApiResponse;
-
- const prepResp = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: orderStatus.taler_pay_uri,
- },
- );
-
- const proposalId = prepResp.proposalId;
-
- await wallet.runPending();
-
- // Drop 3 responses from the exchange.
- let faultCount = 0;
- faultyExchange.faultProxy.addFault({
- async modifyResponse(ctx: FaultInjectionResponseContext) {
- if (!ctx.request.requestUrl.endsWith("/deposit")) {
- return;
- }
- if (faultCount < 3) {
- console.log(`blocking /deposit request #${faultCount}`);
- faultCount++;
- ctx.dropResponse = true;
- } else {
- console.log(`letting through /deposit request #${faultCount}`);
- }
- },
- });
-
- // confirmPay won't work, as the exchange is unreachable
-
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
- // FIXME: should be validated, don't cast!
- proposalId: proposalId,
- });
-
- await wallet.runUntilDone();
-
- // Check if payment was successful.
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "paid");
-}
-
-runPaymentFaultTest.suites = ["wallet"];
-runPaymentFaultTest.timeoutMs = 120000;
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-forgettable.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-forgettable.ts
deleted file mode 100644
index 3bdd6bef3..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-payment-forgettable.ts
+++ /dev/null
@@ -1,81 +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 } from "../harness/harness.js";
-import {
- createSimpleTestkudosEnvironment,
- withdrawViaBank,
- makeTestPayment,
-} from "../harness/helpers.js";
-
-/**
- * Run test for payment with a contract that has forgettable fields.
- */
-export async function runPaymentForgettableTest(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" });
-
- {
- const order = {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "taler://fulfillment-success/thx",
- extra: {
- foo: { bar: "baz" },
- $forgettable: {
- foo: "gnu",
- },
- },
- };
-
- await makeTestPayment(t, { wallet, merchant, order });
- }
-
- console.log("testing with forgettable field without hash");
-
- {
- const order = {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "taler://fulfillment-success/thx",
- extra: {
- foo: { bar: "baz" },
- $forgettable: {
- foo: true,
- },
- },
- };
-
- await makeTestPayment(t, { wallet, merchant, order });
- }
-
- await wallet.runUntilDone();
-}
-
-runPaymentForgettableTest.suites = ["wallet", "merchant"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-idempotency.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-idempotency.ts
deleted file mode 100644
index 9378465a0..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-payment-idempotency.ts
+++ /dev/null
@@ -1,114 +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, MerchantPrivateApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
-import { PreparePayResultType } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Test the wallet-core payment API, especially that repeated operations
- * return the expected result.
- */
-export async function runPaymentIdempotencyTest(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",
- },
- });
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- const talerPayUri = orderStatus.taler_pay_uri;
-
- // Make wallet pay for the order
-
- const preparePayResult = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: orderStatus.taler_pay_uri,
- },
- );
-
- const preparePayResultRep = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: orderStatus.taler_pay_uri,
- },
- );
-
- t.assertTrue(
- preparePayResult.status === PreparePayResultType.PaymentPossible,
- );
- t.assertTrue(
- preparePayResultRep.status === PreparePayResultType.PaymentPossible,
- );
-
- const proposalId = preparePayResult.proposalId;
-
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
- // FIXME: should be validated, don't cast!
- proposalId: proposalId,
- });
-
- // Check if payment was successful.
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "paid");
-
- const preparePayResultAfter = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri,
- },
- );
-
- t.assertTrue(
- preparePayResultAfter.status === PreparePayResultType.AlreadyConfirmed,
- );
- t.assertTrue(preparePayResultAfter.paid === true);
-
- await t.shutdown();
-}
-
-runPaymentIdempotencyTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-multiple.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-multiple.ts
deleted file mode 100644
index 754c3a0e8..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-payment-multiple.ts
+++ /dev/null
@@ -1,162 +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,
- setupDb,
- BankService,
- ExchangeService,
- MerchantService,
- WalletCli,
- MerchantPrivateApi,
-} from "../harness/harness.js";
-import { withdrawViaBank } from "../harness/helpers.js";
-import { coin_ct10, coin_u1 } from "../harness/denomStructures";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-async function setupTest(
- t: GlobalTestState,
-): Promise<{
- merchant: MerchantService;
- exchange: ExchangeService;
- bank: BankService;
-}> {
- 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 exchangeBankAccount = await bank.createExchangeAccount(
- "MyExchange",
- "x",
- );
-
- exchange.addOfferedCoins([coin_ct10, coin_u1]);
-
- bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri);
-
- await bank.start();
-
- await bank.pingUntilAvailable();
-
- await exchange.addBankAccount("1", exchangeBankAccount);
-
- await exchange.start();
- await exchange.pingUntilAvailable();
-
- const merchant = await MerchantService.create(t, {
- name: "testmerchant-1",
- currency: "TESTKUDOS",
- httpPort: 8083,
- database: db.connStr,
- });
-
- merchant.addExchange(exchange);
-
- await merchant.start();
- await merchant.pingUntilAvailable();
-
- await merchant.addInstance({
- id: "default",
- name: "Default Instance",
- paytoUris: [`payto://x-taler-bank/merchant-default`],
- });
-
- await merchant.addInstance({
- id: "minst1",
- name: "minst1",
- paytoUris: ["payto://x-taler-bank/minst1"],
- });
-
- console.log("setup done!");
-
- return {
- merchant,
- bank,
- exchange,
- };
-}
-
-/**
- * Run test.
- *
- * This test uses a very sub-optimal denomination structure.
- */
-export async function runPaymentMultipleTest(t: GlobalTestState) {
- // Set up test environment
-
- const { merchant, bank, exchange } = await setupTest(t);
-
- const wallet = new WalletCli(t);
-
- // Withdraw digital cash into the wallet.
-
- 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.client.call(WalletApiOperation.PreparePayForUri, {
- talerPayUri: orderStatus.taler_pay_uri,
- });
-
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
- // FIXME: should be validated, don't cast!
- proposalId: r1.proposalId,
- });
-
- // Check if payment was successful.
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "paid");
-
- await t.shutdown();
-}
-
-runPaymentMultipleTest.suites = ["wallet"];
-runPaymentMultipleTest.timeoutMs = 120000;
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-on-demo.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-on-demo.ts
deleted file mode 100644
index 1d419fd9a..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-payment-on-demo.ts
+++ /dev/null
@@ -1,99 +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,
- BankApi,
- WalletCli,
- BankAccessApi
-} from "../harness/harness.js";
-import {
- makeTestPayment,
-} from "../harness/helpers.js";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Run test for basic, bank-integrated withdrawal and payment.
- */
-export async function runPaymentDemoTest(t: GlobalTestState) {
-
- // Withdraw digital cash into the wallet.
- let bankInterface = {
- baseUrl: "https://bank.demo.taler.net/",
- port: 0 // unused.
- };
- let user = await BankApi.createRandomBankUser(bankInterface);
- let wop = await BankAccessApi.createWithdrawalOperation(bankInterface, user, "KUDOS:20");
-
- let wallet = new WalletCli(t);
- await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, {
- talerWithdrawUri: wop.taler_withdraw_uri,
- });
-
- await wallet.runPending();
-
- // Confirm it
-
- await BankApi.confirmWithdrawalOperation(bankInterface, user, wop);
-
- // Withdraw
-
- await wallet.client.call(WalletApiOperation.AcceptBankIntegratedWithdrawal, {
- exchangeBaseUrl: "https://exchange.demo.taler.net/",
- talerWithdrawUri: wop.taler_withdraw_uri,
- });
- await wallet.runUntilDone();
-
- let balanceBefore = await wallet.client.call(WalletApiOperation.GetBalances, {});
- t.assertTrue(balanceBefore["balances"].length == 1);
-
- const order = {
- summary: "Buy me!",
- amount: "KUDOS:5",
- fulfillment_url: "taler://fulfillment-success/thx",
- };
-
- let merchant = {
- makeInstanceBaseUrl: function(instanceName?: string) {
- return "https://backend.demo.taler.net/instances/donations/";
- },
- port: 0,
- name: "donations",
- };
-
- t.assertTrue("TALER_ENV_FRONTENDS_APITOKEN" in process.env);
-
- await makeTestPayment(
- t,
- {
- merchant, wallet, order
- },
- {
- "Authorization": `Bearer ${process.env["TALER_ENV_FRONTENDS_APITOKEN"]}`,
- });
-
- await wallet.runUntilDone();
-
- let balanceAfter = await wallet.client.call(WalletApiOperation.GetBalances, {});
- t.assertTrue(balanceAfter["balances"].length == 1);
- t.assertTrue(balanceBefore["balances"][0]["available"] > balanceAfter["balances"][0]["available"]);
-}
-
-runPaymentDemoTest.excludeByDefault = true;
-runPaymentDemoTest.suites = ["buildbot"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-transient.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-transient.ts
deleted file mode 100644
index 75d44d495..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-payment-transient.ts
+++ /dev/null
@@ -1,187 +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, MerchantPrivateApi } from "../harness/harness.js";
-import {
- withdrawViaBank,
- createFaultInjectedMerchantTestkudosEnvironment,
-} from "../harness/helpers.js";
-import axios from "axios";
-import {
- FaultInjectionRequestContext,
- FaultInjectionResponseContext,
-} from "../harness/faultInjection";
-import {
- codecForMerchantOrderStatusUnpaid,
- ConfirmPayResultType,
- PreparePayResultType,
- TalerErrorCode,
- TalerErrorDetails,
- URL,
-} from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Run test for a payment where the merchant has a transient
- * failure in /pay
- */
-export async function runPaymentTransientTest(t: GlobalTestState) {
- // Set up test environment
-
- const {
- wallet,
- bank,
- exchange,
- faultyMerchant,
- } = await createFaultInjectedMerchantTestkudosEnvironment(t);
-
- // Withdraw digital cash into the wallet.
-
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
-
- const merchant = faultyMerchant;
-
- let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
- });
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- sessionId: "mysession-one",
- });
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- t.assertTrue(orderStatus.already_paid_order_id === undefined);
- let publicOrderStatusUrl = orderStatus.order_status_url;
-
- let publicOrderStatusResp = await axios.get(publicOrderStatusUrl, {
- validateStatus: () => true,
- });
-
- if (publicOrderStatusResp.status != 402) {
-
-
- throw Error(
- `expected status 402 (before claiming), but got ${publicOrderStatusResp.status}`,
- );
- }
-
- let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
- publicOrderStatusResp.data,
- );
-
- console.log(pubUnpaidStatus);
-
- let preparePayResp = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: pubUnpaidStatus.taler_pay_uri,
- },
- );
-
- t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible);
-
- const proposalId = preparePayResp.proposalId;
-
- publicOrderStatusResp = await axios.get(publicOrderStatusUrl, {
- validateStatus: () => true,
- });
-
- if (publicOrderStatusResp.status != 402) {
- throw Error(
- `expected status 402 (after claiming), but got ${publicOrderStatusResp.status}`,
- );
- }
-
- pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
- publicOrderStatusResp.data,
- );
-
- let faultInjected = false;
-
- faultyMerchant.faultProxy.addFault({
- async modifyResponse(ctx: FaultInjectionResponseContext) {
- console.log("in modifyResponse");
- const url = new URL(ctx.request.requestUrl);
- console.log("pathname is", url.pathname);
- if (!url.pathname.endsWith("/pay")) {
- return;
- }
- if (faultInjected) {
- console.log("not injecting pay fault");
- return;
- }
- faultInjected = true;
- console.log("injecting pay fault");
- const err: TalerErrorDetails = {
- code: TalerErrorCode.GENERIC_DB_COMMIT_FAILED,
- details: {},
- hint: "huh",
- message: "something went wrong",
- };
- ctx.responseBody = Buffer.from(JSON.stringify(err));
- ctx.statusCode = 500;
- },
- });
-
- const confirmPayResp = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId,
- },
- );
-
- console.log(confirmPayResp);
-
- t.assertTrue(confirmPayResp.type === ConfirmPayResultType.Pending);
- t.assertTrue(faultInjected);
-
- const confirmPayRespTwo = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId,
- },
- );
-
- t.assertTrue(confirmPayRespTwo.type === ConfirmPayResultType.Done);
-
- // Now ask the merchant if paid
-
- console.log("requesting", publicOrderStatusUrl);
- publicOrderStatusResp = await axios.get(publicOrderStatusUrl, {
- validateStatus: () => true,
- });
-
- console.log(publicOrderStatusResp.data);
-
- if (publicOrderStatusResp.status != 200) {
- console.log(publicOrderStatusResp.data);
- throw Error(
- `expected status 200 (after paying), but got ${publicOrderStatusResp.status}`,
- );
- }
-}
-
-runPaymentTransientTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment-zero.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment-zero.ts
deleted file mode 100644
index c38b8b382..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-payment-zero.ts
+++ /dev/null
@@ -1,72 +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 { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState } from "../harness/harness.js";
-import {
- createSimpleTestkudosEnvironment,
- withdrawViaBank,
- makeTestPayment,
-} from "../harness/helpers.js";
-
-/**
- * Run test for a payment for a "free" order with
- * an amount of zero.
- */
-export async function runPaymentZeroTest(t: GlobalTestState) {
- // Set up test environment
-
- const {
- wallet,
- bank,
- exchange,
- merchant,
- } = await createSimpleTestkudosEnvironment(t);
-
- // First, make a "free" payment when we don't even have
- // any money in the
-
- // Withdraw digital cash into the wallet.
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" });
-
- await wallet.runUntilDone();
-
- await makeTestPayment(t, {
- wallet,
- merchant,
- order: {
- summary: "I am free!",
- amount: "TESTKUDOS:0",
- fulfillment_url: "taler://fulfillment-success/thx",
- },
- });
-
- await wallet.runUntilDone();
-
- const transactions = await wallet.client.call(
- WalletApiOperation.GetTransactions,
- {},
- );
-
- for (const tr of transactions.transactions) {
- t.assertDeepEqual(tr.pending, false);
- }
-}
-
-runPaymentZeroTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-payment.ts b/packages/taler-wallet-cli/src/integrationtests/test-payment.ts
deleted file mode 100644
index 967d491be..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-payment.ts
+++ /dev/null
@@ -1,55 +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 } from "../harness/harness.js";
-import {
- createSimpleTestkudosEnvironment,
- withdrawViaBank,
- makeTestPayment,
-} from "../harness/helpers.js";
-
-/**
- * Run test for basic, bank-integrated withdrawal and payment.
- */
-export async function runPaymentTest(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" });
-
- const order = {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "taler://fulfillment-success/thx",
- };
-
- await makeTestPayment(t, { wallet, merchant, order });
-
- await wallet.runUntilDone();
-}
-
-runPaymentTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-paywall-flow.ts b/packages/taler-wallet-cli/src/integrationtests/test-paywall-flow.ts
deleted file mode 100644
index a8e3b3e95..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-paywall-flow.ts
+++ /dev/null
@@ -1,252 +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, MerchantPrivateApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
-import {
- PreparePayResultType,
- codecForMerchantOrderStatusUnpaid,
- ConfirmPayResultType,
- URL,
-} from "@gnu-taler/taler-util";
-import axios from "axios";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runPaywallFlowTest(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" });
-
- /**
- * =========================================================================
- * Create an order and let the wallet pay under a session ID
- *
- * We check along the way that the JSON response to /orders/{order_id}
- * returns the right thing.
- * =========================================================================
- */
-
- let orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
- });
-
- const firstOrderId = orderResp.order_id;
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- sessionId: "mysession-one",
- });
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- const talerPayUriOne = orderStatus.taler_pay_uri;
-
- t.assertTrue(orderStatus.already_paid_order_id === undefined);
- let publicOrderStatusUrl = new URL(orderStatus.order_status_url);
-
- let publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, {
- validateStatus: () => true,
- });
-
- if (publicOrderStatusResp.status != 402) {
- throw Error(
- `expected status 402 (before claiming), but got ${publicOrderStatusResp.status}`,
- );
- }
-
- let pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
- publicOrderStatusResp.data,
- );
-
- console.log(pubUnpaidStatus);
-
- let preparePayResp = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: pubUnpaidStatus.taler_pay_uri,
- },
- );
-
- t.assertTrue(preparePayResp.status === PreparePayResultType.PaymentPossible);
-
- const proposalId = preparePayResp.proposalId;
-
- console.log("requesting", publicOrderStatusUrl.href);
- publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, {
- validateStatus: () => true,
- });
- console.log("response body", publicOrderStatusResp.data);
- if (publicOrderStatusResp.status != 402) {
- throw Error(
- `expected status 402 (after claiming), but got ${publicOrderStatusResp.status}`,
- );
- }
-
- pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
- publicOrderStatusResp.data,
- );
-
- const confirmPayRes = await wallet.client.call(
- WalletApiOperation.ConfirmPay,
- {
- proposalId: proposalId,
- },
- );
-
- t.assertTrue(confirmPayRes.type === ConfirmPayResultType.Done);
-
- publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, {
- validateStatus: () => true,
- });
-
- console.log(publicOrderStatusResp.data);
-
- if (publicOrderStatusResp.status != 200) {
- console.log(publicOrderStatusResp.data);
- throw Error(
- `expected status 200 (after paying), but got ${publicOrderStatusResp.status}`,
- );
- }
-
- /**
- * =========================================================================
- * Now change up the session ID!
- * =========================================================================
- */
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- sessionId: "mysession-two",
- });
-
- // Should be claimed (not paid!) because of a new session ID
- t.assertTrue(orderStatus.order_status === "claimed");
-
- // Pay with new taler://pay URI, which should
- // have the new session ID!
- // Wallet should now automatically re-play payment.
- preparePayResp = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: talerPayUriOne,
- },
- );
-
- t.assertTrue(preparePayResp.status === PreparePayResultType.AlreadyConfirmed);
- t.assertTrue(preparePayResp.paid);
-
- /**
- * =========================================================================
- * Now we test re-purchase detection.
- * =========================================================================
- */
-
- orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
- order: {
- summary: "Buy me!",
- amount: "TESTKUDOS:5",
- // Same fulfillment URL as previously!
- fulfillment_url: "https://example.com/article42",
- public_reorder_url: "https://example.com/article42-share",
- },
- });
-
- const secondOrderId = orderResp.order_id;
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: secondOrderId,
- sessionId: "mysession-three",
- });
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- t.assertTrue(orderStatus.already_paid_order_id === undefined);
- publicOrderStatusUrl = new URL(orderStatus.order_status_url);
-
- // Here the re-purchase detection should kick in,
- // and the wallet should re-pay for the old order
- // under the new session ID (mysession-three).
- preparePayResp = await wallet.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: orderStatus.taler_pay_uri,
- },
- );
-
- t.assertTrue(preparePayResp.status === PreparePayResultType.AlreadyConfirmed);
- t.assertTrue(preparePayResp.paid);
-
- // The first order should now be paid under "mysession-three",
- // as the wallet did re-purchase detection
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: firstOrderId,
- sessionId: "mysession-three",
- });
-
- t.assertTrue(orderStatus.order_status === "paid");
-
- // Check that with a completely new session ID, the status would NOT
- // be paid.
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: firstOrderId,
- sessionId: "mysession-four",
- });
-
- t.assertTrue(orderStatus.order_status === "claimed");
-
- // Now check if the public status of the new order is correct.
-
- console.log("requesting public status", publicOrderStatusUrl);
-
- // Ask the order status of the claimed-but-unpaid order
- publicOrderStatusResp = await axios.get(publicOrderStatusUrl.href, {
- validateStatus: () => true,
- });
-
- if (publicOrderStatusResp.status != 402) {
- throw Error(`expected status 402, but got ${publicOrderStatusResp.status}`);
- }
-
- pubUnpaidStatus = codecForMerchantOrderStatusUnpaid().decode(
- publicOrderStatusResp.data,
- );
-
- console.log(publicOrderStatusResp.data);
-
- t.assertTrue(pubUnpaidStatus.already_paid_order_id === firstOrderId);
-}
-
-runPaywallFlowTest.suites = ["merchant", "wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-refund-auto.ts b/packages/taler-wallet-cli/src/integrationtests/test-refund-auto.ts
deleted file mode 100644
index 230fc942d..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-refund-auto.ts
+++ /dev/null
@@ -1,104 +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, MerchantPrivateApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
-import { durationFromSpec } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runRefundAutoTest(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,
- },
- },
- refund_delay: durationFromSpec({ minutes: 5 }),
- });
-
- 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.client.call(WalletApiOperation.PreparePayForUri, {
- talerPayUri: orderStatus.taler_pay_uri,
- });
-
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
- // FIXME: should be validated, don't cast!
- proposalId: r1.proposalId,
- });
-
- // 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.client.call(
- WalletApiOperation.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();
-}
-
-runRefundAutoTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-refund-gone.ts b/packages/taler-wallet-cli/src/integrationtests/test-refund-gone.ts
deleted file mode 100644
index acb74b3d3..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-refund-gone.ts
+++ /dev/null
@@ -1,127 +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, MerchantPrivateApi } from "../harness/harness.js";
-import {
- createSimpleTestkudosEnvironment,
- withdrawViaBank,
- applyTimeTravel,
-} from "../harness/helpers.js";
-import {
- durationFromSpec,
- timestampAddDuration,
- getTimestampNow,
- timestampTruncateToSecond,
-} from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runRefundGoneTest(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",
- pay_deadline: timestampTruncateToSecond(
- timestampAddDuration(
- getTimestampNow(),
- durationFromSpec({
- minutes: 10,
- }),
- ),
- ),
- },
- refund_delay: durationFromSpec({ minutes: 1 }),
- });
-
- 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.client.call(WalletApiOperation.PreparePayForUri, {
- talerPayUri: orderStatus.taler_pay_uri,
- });
-
- const r2 = await wallet.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: r1.proposalId,
- });
-
- // Check if payment was successful.
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "paid");
-
- console.log(orderStatus);
-
- await applyTimeTravel(durationFromSpec({ hours: 1 }), { exchange, wallet });
-
- await exchange.runAggregatorOnce();
-
- const ref = await MerchantPrivateApi.giveRefund(merchant, {
- amount: "TESTKUDOS:5",
- instance: "default",
- justification: "foo",
- orderId: orderResp.order_id,
- });
-
- console.log(ref);
-
- let rr = await wallet.client.call(WalletApiOperation.ApplyRefund, {
- talerRefundUri: ref.talerRefundUri,
- });
-
- t.assertAmountEquals(rr.amountRefundGone, "TESTKUDOS:5");
- console.log(rr);
-
- await wallet.runUntilDone();
-
- let r = await wallet.client.call(WalletApiOperation.GetBalances, {});
- console.log(JSON.stringify(r, undefined, 2));
-
- const r3 = await wallet.client.call(WalletApiOperation.GetTransactions, {});
- console.log(JSON.stringify(r3, undefined, 2));
-
- await t.shutdown();
-}
-
-runRefundGoneTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-refund-incremental.ts b/packages/taler-wallet-cli/src/integrationtests/test-refund-incremental.ts
deleted file mode 100644
index 47c2293e2..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-refund-incremental.ts
+++ /dev/null
@@ -1,196 +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, delayMs, MerchantPrivateApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
-import {
- TransactionType,
- Amounts,
- durationFromSpec,
-} from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runRefundIncrementalTest(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:10",
- fulfillment_url: "taler://fulfillment-success/thx",
- },
- refund_delay: durationFromSpec({ minutes: 5 }),
- });
-
- 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.client.call(WalletApiOperation.PreparePayForUri, {
- talerPayUri: orderStatus.taler_pay_uri,
- });
-
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: r1.proposalId,
- });
-
- // Check if payment was successful.
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "paid");
-
- let ref = await MerchantPrivateApi.giveRefund(merchant, {
- amount: "TESTKUDOS:2.5",
- instance: "default",
- justification: "foo",
- orderId: orderResp.order_id,
- });
-
- console.log("first refund increase response", ref);
-
- {
- let wr = await wallet.client.call(WalletApiOperation.ApplyRefund, {
- talerRefundUri: ref.talerRefundUri,
- });
- console.log(wr);
- const txs = await wallet.client.call(
- WalletApiOperation.GetTransactions,
- {},
- );
- console.log(
- "transactions after applying first refund:",
- JSON.stringify(txs, undefined, 2),
- );
- }
-
- // Wait at least a second, because otherwise the increased
- // refund will be grouped with the previous one.
- await delayMs(1200);
-
- ref = await MerchantPrivateApi.giveRefund(merchant, {
- amount: "TESTKUDOS:5",
- instance: "default",
- justification: "bar",
- orderId: orderResp.order_id,
- });
-
- console.log("second refund increase response", ref);
-
- // Wait at least a second, because otherwise the increased
- // refund will be grouped with the previous one.
- await delayMs(1200);
-
- ref = await MerchantPrivateApi.giveRefund(merchant, {
- amount: "TESTKUDOS:10",
- instance: "default",
- justification: "bar",
- orderId: orderResp.order_id,
- });
-
- console.log("third refund increase response", ref);
-
- {
- let wr = await wallet.client.call(WalletApiOperation.ApplyRefund, {
- talerRefundUri: ref.talerRefundUri,
- });
- console.log(wr);
- }
-
- orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(merchant, {
- orderId: orderResp.order_id,
- });
-
- t.assertTrue(orderStatus.order_status === "paid");
-
- t.assertAmountEquals(orderStatus.refund_amount, "TESTKUDOS:10");
-
- console.log(JSON.stringify(orderStatus, undefined, 2));
-
- await wallet.runUntilDone();
-
- const bal = await wallet.client.call(WalletApiOperation.GetBalances, {});
- console.log(JSON.stringify(bal, undefined, 2));
-
- {
- const txs = await wallet.client.call(
- WalletApiOperation.GetTransactions,
- {},
- );
- console.log(JSON.stringify(txs, undefined, 2));
-
- const txTypes = txs.transactions.map((x) => x.type);
- t.assertDeepEqual(txTypes, [
- "withdrawal",
- "payment",
- "refund",
- "refund",
- "refund",
- ]);
-
- for (const tx of txs.transactions) {
- if (tx.type !== TransactionType.Refund) {
- continue;
- }
- t.assertAmountLeq(tx.amountEffective, tx.amountRaw);
- }
-
- const raw = Amounts.sum(
- txs.transactions
- .filter((x) => x.type === TransactionType.Refund)
- .map((x) => x.amountRaw),
- ).amount;
-
- t.assertAmountEquals("TESTKUDOS:10", raw);
-
- const effective = Amounts.sum(
- txs.transactions
- .filter((x) => x.type === TransactionType.Refund)
- .map((x) => x.amountEffective),
- ).amount;
-
- t.assertAmountEquals("TESTKUDOS:8.33", effective);
- }
-
- await t.shutdown();
-}
-
-runRefundIncrementalTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-refund.ts b/packages/taler-wallet-cli/src/integrationtests/test-refund.ts
deleted file mode 100644
index f11771922..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-refund.ts
+++ /dev/null
@@ -1,105 +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 { durationFromSpec } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, MerchantPrivateApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runRefundTest(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",
- },
- refund_delay: durationFromSpec({ minutes: 5 }),
- });
-
- 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.client.call(WalletApiOperation.PreparePayForUri, {
- talerPayUri: orderStatus.taler_pay_uri,
- });
-
- await wallet.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: r1.proposalId,
- });
-
- // 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);
-
- let r = await wallet.client.call(WalletApiOperation.ApplyRefund, {
- talerRefundUri: ref.talerRefundUri,
- });
- console.log(r);
-
- await wallet.runUntilDone();
-
- {
- const r2 = await wallet.client.call(WalletApiOperation.GetBalances, {});
- console.log(JSON.stringify(r2, undefined, 2));
- }
- {
- const r2 = await wallet.client.call(WalletApiOperation.GetTransactions, {});
- console.log(JSON.stringify(r2, undefined, 2));
- }
-
- await t.shutdown();
-}
-
-runRefundTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-revocation.ts b/packages/taler-wallet-cli/src/integrationtests/test-revocation.ts
deleted file mode 100644
index 276c532b5..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-revocation.ts
+++ /dev/null
@@ -1,212 +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 { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { CoinConfig } from "../harness/denomStructures";
-import {
- GlobalTestState,
- ExchangeService,
- MerchantService,
- WalletCli,
- setupDb,
- BankService,
- delayMs,
-} from "../harness/harness.js";
-import {
- withdrawViaBank,
- makeTestPayment,
- SimpleTestEnvironment,
-} from "../harness/helpers.js";
-
-async function revokeAllWalletCoins(req: {
- wallet: WalletCli;
- exchange: ExchangeService;
- merchant: MerchantService;
-}): Promise<void> {
- const { wallet, exchange, merchant } = req;
- const coinDump = await wallet.client.call(WalletApiOperation.DumpCoins, {});
- console.log(coinDump);
- const usedDenomHashes = new Set<string>();
- for (const coin of coinDump.coins) {
- usedDenomHashes.add(coin.denom_pub_hash);
- }
- for (const x of usedDenomHashes.values()) {
- await exchange.revokeDenomination(x);
- }
- await delayMs(1000);
- await exchange.keyup();
- await delayMs(1000);
- await merchant.stop();
- await merchant.start();
- await merchant.pingUntilAvailable();
-}
-
-async function createTestEnvironment(
- t: GlobalTestState,
-): Promise<SimpleTestEnvironment> {
- 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();
-
- const coin_u1: CoinConfig = {
- durationLegal: "3 years",
- durationSpend: "2 years",
- durationWithdraw: "7 days",
- rsaKeySize: 1024,
- name: `TESTKUDOS_u1`,
- value: `TESTKUDOS:1`,
- feeDeposit: `TESTKUDOS:0`,
- feeRefresh: `TESTKUDOS:0`,
- feeRefund: `TESTKUDOS:0`,
- feeWithdraw: `TESTKUDOS:0`,
- };
-
- exchange.addCoinConfigList([coin_u1]);
-
- 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://x-taler-bank/merchant-default`],
- });
-
- await merchant.addInstance({
- id: "minst1",
- name: "minst1",
- paytoUris: ["payto://x-taler-bank/minst1"],
- });
-
- console.log("setup done!");
-
- const wallet = new WalletCli(t);
-
- return {
- commonDb: db,
- exchange,
- merchant,
- wallet,
- bank,
- exchangeBankAccount,
- };
-}
-
-/**
- * Basic time travel test.
- */
-export async function runRevocationTest(t: GlobalTestState) {
- // Set up test environment
-
- const { wallet, bank, exchange, merchant } = await createTestEnvironment(t);
-
- // Withdraw digital cash into the wallet.
-
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" });
-
- console.log("revoking first time");
- await revokeAllWalletCoins({ wallet, exchange, merchant });
-
- // FIXME: this shouldn't be necessary once https://bugs.taler.net/n/6565
- // is implemented.
- await wallet.client.call(WalletApiOperation.AddExchange, {
- exchangeBaseUrl: exchange.baseUrl,
- forceUpdate: true,
- });
- await wallet.runUntilDone();
- await wallet.runUntilDone();
- const bal = await wallet.client.call(WalletApiOperation.GetBalances, {});
- console.log("wallet balance", bal);
-
- const order = {
- summary: "Buy me!",
- amount: "TESTKUDOS:10",
- fulfillment_url: "taler://fulfillment-success/thx",
- };
-
- await makeTestPayment(t, { wallet, merchant, order });
-
- wallet.deleteDatabase();
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" });
-
- const coinDump = await wallet.client.call(WalletApiOperation.DumpCoins, {});
- console.log(coinDump);
- const coinPubList = coinDump.coins.map((x) => x.coin_pub);
- await wallet.client.call(WalletApiOperation.ForceRefresh, {
- coinPubList,
- });
- await wallet.runUntilDone();
-
- console.log("revoking second time");
- await revokeAllWalletCoins({ wallet, exchange, merchant });
-
- // FIXME: this shouldn't be necessary once https://bugs.taler.net/n/6565
- // is implemented.
- await wallet.client.call(WalletApiOperation.AddExchange, {
- exchangeBaseUrl: exchange.baseUrl,
- forceUpdate: true,
- });
- await wallet.runUntilDone();
- await wallet.runUntilDone();
- {
- const bal = await wallet.client.call(WalletApiOperation.GetBalances, {});
- console.log("wallet balance", bal);
- }
-
- await makeTestPayment(t, { wallet, merchant, order });
-}
-
-runRevocationTest.timeoutMs = 120000;
-runRevocationTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-timetravel-autorefresh.ts b/packages/taler-wallet-cli/src/integrationtests/test-timetravel-autorefresh.ts
deleted file mode 100644
index e20d8bdad..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-timetravel-autorefresh.ts
+++ /dev/null
@@ -1,215 +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 {
- ConfirmPayResultType,
- Duration,
- durationFromSpec,
- PreparePayResultType,
-} from "@gnu-taler/taler-util";
-import {
- PendingOperationsResponse,
- WalletApiOperation,
-} from "@gnu-taler/taler-wallet-core";
-import { makeNoFeeCoinConfig } from "../harness/denomStructures";
-import {
- BankService,
- ExchangeService,
- GlobalTestState,
- MerchantPrivateApi,
- MerchantService,
- setupDb,
- WalletCli,
-} from "../harness/harness.js";
-import { startWithdrawViaBank, withdrawViaBank } from "../harness/helpers.js";
-
-async function applyTimeTravel(
- timetravelDuration: Duration,
- s: {
- exchange?: ExchangeService;
- merchant?: MerchantService;
- wallet?: WalletCli;
- },
-): Promise<void> {
- if (s.exchange) {
- await s.exchange.stop();
- s.exchange.setTimetravel(timetravelDuration);
- await s.exchange.start();
- await s.exchange.pingUntilAvailable();
- }
-
- if (s.merchant) {
- await s.merchant.stop();
- s.merchant.setTimetravel(timetravelDuration);
- await s.merchant.start();
- await s.merchant.pingUntilAvailable();
- }
-
- if (s.wallet) {
- console.log("setting wallet time travel to", timetravelDuration);
- s.wallet.setTimetravel(timetravelDuration);
- }
-}
-
-/**
- * Basic time travel test.
- */
-export async function runTimetravelAutorefreshTest(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.addCoinConfigList(makeNoFeeCoinConfig("TESTKUDOS"));
-
- 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://x-taler-bank/merchant-default`],
- });
-
- await merchant.addInstance({
- id: "minst1",
- name: "minst1",
- paytoUris: ["payto://x-taler-bank/minst1"],
- });
-
- console.log("setup done!");
-
- const wallet = new WalletCli(t);
-
- // Withdraw digital cash into the wallet.
-
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:15" });
-
- // Travel into the future, the deposit expiration is two years
- // into the future.
- console.log("applying first time travel");
- await applyTimeTravel(durationFromSpec({ days: 400 }), {
- wallet,
- exchange,
- merchant,
- });
-
- await wallet.runUntilDone();
-
- let p: PendingOperationsResponse;
- p = await wallet.client.call(WalletApiOperation.GetPendingOperations, {});
-
- console.log("pending operations after first time travel");
- console.log(JSON.stringify(p, undefined, 2));
-
- await startWithdrawViaBank(t, {
- wallet,
- bank,
- exchange,
- amount: "TESTKUDOS:20",
- });
-
- await wallet.runUntilDone();
-
- // Travel into the future, the deposit expiration is two years
- // into the future.
- console.log("applying second time travel");
- await applyTimeTravel(durationFromSpec({ years: 2, months: 6 }), {
- wallet,
- exchange,
- merchant,
- });
-
- // At this point, the original coins should've been refreshed.
- // It would be too late to refresh them now, as we're past
- // the two year deposit expiration.
-
- await wallet.runUntilDone();
-
- const orderResp = await MerchantPrivateApi.createOrder(merchant, "default", {
- order: {
- fulfillment_url: "http://example.com",
- summary: "foo",
- amount: "TESTKUDOS:30",
- },
- });
-
- const orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- instance: "default",
- },
- );
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- const r = await wallet.client.call(WalletApiOperation.PreparePayForUri, {
- talerPayUri: orderStatus.taler_pay_uri,
- });
-
- console.log(r);
-
- t.assertTrue(r.status === PreparePayResultType.PaymentPossible);
-
- const cpr = await wallet.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: r.proposalId,
- });
-
- t.assertTrue(cpr.type === ConfirmPayResultType.Done);
-}
-
-runTimetravelAutorefreshTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-timetravel-withdraw.ts b/packages/taler-wallet-cli/src/integrationtests/test-timetravel-withdraw.ts
deleted file mode 100644
index 2ff857057..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-timetravel-withdraw.ts
+++ /dev/null
@@ -1,96 +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 } from "../harness/harness.js";
-import {
- createSimpleTestkudosEnvironment,
- withdrawViaBank,
- startWithdrawViaBank,
-} from "../harness/helpers.js";
-import { Duration, TransactionType } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Basic time travel test.
- */
-export async function runTimetravelWithdrawTest(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:15" });
-
- // Travel 400 days into the future,
- // as the deposit expiration is two years
- // into the future.
- const timetravelDuration: Duration = {
- d_ms: 1000 * 60 * 60 * 24 * 400,
- };
-
- await exchange.stop();
- exchange.setTimetravel(timetravelDuration);
- await exchange.start();
- await exchange.pingUntilAvailable();
- await exchange.keyup();
-
- await merchant.stop();
- merchant.setTimetravel(timetravelDuration);
- await merchant.start();
- await merchant.pingUntilAvailable();
-
- // This should fail, as the wallet didn't time travel yet.
- await startWithdrawViaBank(t, {
- wallet,
- bank,
- exchange,
- amount: "TESTKUDOS:20",
- });
-
- // Check that transactions are correct for the failed withdrawal
- {
- await wallet.runUntilDone({ maxRetries: 5 });
- const transactions = await wallet.client.call(
- WalletApiOperation.GetTransactions,
- {},
- );
- console.log(transactions);
- const types = transactions.transactions.map((x) => x.type);
- t.assertDeepEqual(types, ["withdrawal", "withdrawal"]);
- const wtrans = transactions.transactions[1];
- t.assertTrue(wtrans.type === TransactionType.Withdrawal);
- t.assertTrue(wtrans.pending);
- }
-
- // Now we also let the wallet time travel
-
- wallet.setTimetravel(timetravelDuration);
-
- // This doesn't work yet, see https://bugs.taler.net/n/6585
-
- // await wallet.runUntilDone({ maxRetries: 5 });
-}
-
-runTimetravelWithdrawTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-tipping.ts b/packages/taler-wallet-cli/src/integrationtests/test-tipping.ts
deleted file mode 100644
index c6a7f8402..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-tipping.ts
+++ /dev/null
@@ -1,130 +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 { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, MerchantPrivateApi, BankApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runTippingTest(t: GlobalTestState) {
- // Set up test environment
-
- const {
- wallet,
- bank,
- exchange,
- merchant,
- exchangeBankAccount,
- } = await createSimpleTestkudosEnvironment(t);
-
- const mbu = await BankApi.createRandomBankUser(bank);
-
- const tipReserveResp = await MerchantPrivateApi.createTippingReserve(
- merchant,
- "default",
- {
- exchange_url: exchange.baseUrl,
- initial_balance: "TESTKUDOS:10",
- wire_method: "x-taler-bank",
- },
- );
-
- console.log("tipReserveResp:", tipReserveResp);
-
- t.assertDeepEqual(
- tipReserveResp.payto_uri,
- exchangeBankAccount.accountPaytoUri,
- );
-
- await BankApi.adminAddIncoming(bank, {
- amount: "TESTKUDOS:10",
- debitAccountPayto: mbu.accountPaytoUri,
- exchangeBankAccount,
- reservePub: tipReserveResp.reserve_pub,
- });
-
- await exchange.runWirewatchOnce();
-
- await merchant.stop();
- await merchant.start();
- await merchant.pingUntilAvailable();
-
- const r = await MerchantPrivateApi.queryTippingReserves(merchant, "default");
- console.log("tipping reserves:", JSON.stringify(r, undefined, 2));
-
- t.assertTrue(r.reserves.length === 1);
- t.assertDeepEqual(
- r.reserves[0].exchange_initial_amount,
- r.reserves[0].merchant_initial_amount,
- );
-
- const tip = await MerchantPrivateApi.giveTip(merchant, "default", {
- amount: "TESTKUDOS:5",
- justification: "why not?",
- next_url: "https://example.com/after-tip",
- });
-
- console.log("created tip", tip);
-
- const doTip = async (): Promise<void> => {
- const ptr = await wallet.client.call(WalletApiOperation.PrepareTip, {
- talerTipUri: tip.taler_tip_uri,
- });
-
- console.log(ptr);
-
- t.assertAmountEquals(ptr.tipAmountRaw, "TESTKUDOS:5");
- t.assertAmountEquals(ptr.tipAmountEffective, "TESTKUDOS:4.85");
-
- await wallet.client.call(WalletApiOperation.AcceptTip, {
- walletTipId: ptr.walletTipId,
- });
-
- await wallet.runUntilDone();
-
- const bal = await wallet.client.call(WalletApiOperation.GetBalances, {});
-
- console.log(bal);
-
- t.assertAmountEquals(bal.balances[0].available, "TESTKUDOS:4.85");
-
- const txns = await wallet.client.call(
- WalletApiOperation.GetTransactions,
- {},
- );
-
- console.log("Transactions:", JSON.stringify(txns, undefined, 2));
-
- t.assertDeepEqual(txns.transactions[0].type, "tip");
- t.assertDeepEqual(txns.transactions[0].pending, false);
- t.assertAmountEquals(
- txns.transactions[0].amountEffective,
- "TESTKUDOS:4.85",
- );
- t.assertAmountEquals(txns.transactions[0].amountRaw, "TESTKUDOS:5.0");
- };
-
- // Check twice so make sure tip handling is idempotent
- await doTip();
- await doTip();
-}
-
-runTippingTest.suites = ["wallet", "wallet-tipping"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-basic.ts b/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-basic.ts
deleted file mode 100644
index 23e01e5e1..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-basic.ts
+++ /dev/null
@@ -1,151 +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 { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, WalletCli } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment, withdrawViaBank } from "../harness/helpers.js";
-import { SyncService } from "../harness/sync";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runWalletBackupBasicTest(t: GlobalTestState) {
- // Set up test environment
-
- const {
- commonDb,
- merchant,
- wallet,
- bank,
- exchange,
- } = await createSimpleTestkudosEnvironment(t);
-
- const sync = await SyncService.create(t, {
- currency: "TESTKUDOS",
- annualFee: "TESTKUDOS:0.5",
- database: commonDb.connStr,
- fulfillmentUrl: "taler://fulfillment-success",
- httpPort: 8089,
- name: "sync1",
- paymentBackendUrl: merchant.makeInstanceBaseUrl(),
- uploadLimitMb: 10,
- });
-
- await sync.start();
- await sync.pingUntilAvailable();
-
- await wallet.client.call(WalletApiOperation.AddBackupProvider, {
- backupProviderBaseUrl: sync.baseUrl,
- activate: false,
- name: sync.baseUrl,
- });
-
- {
- const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {});
- t.assertDeepEqual(bi.providers[0].active, false);
- }
-
- await wallet.client.call(WalletApiOperation.AddBackupProvider, {
- backupProviderBaseUrl: sync.baseUrl,
- activate: true,
- name: sync.baseUrl,
- });
-
- {
- const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {});
- t.assertDeepEqual(bi.providers[0].active, true);
- }
-
- await wallet.client.call(WalletApiOperation.RunBackupCycle, {});
-
- {
- const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {});
- console.log(bi);
- t.assertDeepEqual(
- bi.providers[0].paymentStatus.type,
- "insufficient-balance",
- );
- }
-
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:10" });
-
- await wallet.client.call(WalletApiOperation.RunBackupCycle, {});
-
- {
- const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {});
- console.log(bi);
- }
-
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:5" });
-
- await wallet.client.call(WalletApiOperation.RunBackupCycle, {});
-
- {
- const bi = await wallet.client.call(WalletApiOperation.GetBackupInfo, {});
- console.log(bi);
- }
-
- const backupRecovery = await wallet.client.call(
- WalletApiOperation.ExportBackupRecovery,
- {},
- );
-
- const wallet2 = new WalletCli(t, "wallet2");
-
- // Check that the second wallet is a fresh wallet.
- {
- const bal = await wallet2.client.call(WalletApiOperation.GetBalances, {});
- t.assertTrue(bal.balances.length === 0);
- }
-
- await wallet2.client.call(WalletApiOperation.ImportBackupRecovery, {
- recovery: backupRecovery,
- });
-
- await wallet2.client.call(WalletApiOperation.RunBackupCycle, {});
-
- // Check that now the old balance is available!
- {
- const bal = await wallet2.client.call(WalletApiOperation.GetBalances, {});
- t.assertTrue(bal.balances.length === 1);
- console.log(bal);
- }
-
- // Now do some basic checks that the restored wallet is still functional
- {
- const bal1 = await wallet2.client.call(WalletApiOperation.GetBalances, {});
-
- t.assertAmountEquals(bal1.balances[0].available, "TESTKUDOS:14.1");
-
- await withdrawViaBank(t, {
- wallet: wallet2,
- bank,
- exchange,
- amount: "TESTKUDOS:10",
- });
-
- await wallet2.runUntilDone();
-
- const bal2 = await wallet2.client.call(WalletApiOperation.GetBalances, {});
-
- t.assertAmountEquals(bal2.balances[0].available, "TESTKUDOS:23.82");
- }
-}
-
-runWalletBackupBasicTest.suites = ["wallet", "wallet-backup"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-doublespend.ts b/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-doublespend.ts
deleted file mode 100644
index 8c20dcc2b..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-doublespend.ts
+++ /dev/null
@@ -1,168 +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 { PreparePayResultType } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, WalletCli, MerchantPrivateApi } from "../harness/harness.js";
-import {
- createSimpleTestkudosEnvironment,
- makeTestPayment,
- withdrawViaBank,
-} from "../harness/helpers.js";
-import { SyncService } from "../harness/sync";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runWalletBackupDoublespendTest(t: GlobalTestState) {
- // Set up test environment
-
- const {
- commonDb,
- merchant,
- wallet,
- bank,
- exchange,
- } = await createSimpleTestkudosEnvironment(t);
-
- const sync = await SyncService.create(t, {
- currency: "TESTKUDOS",
- annualFee: "TESTKUDOS:0.5",
- database: commonDb.connStr,
- fulfillmentUrl: "taler://fulfillment-success",
- httpPort: 8089,
- name: "sync1",
- paymentBackendUrl: merchant.makeInstanceBaseUrl(),
- uploadLimitMb: 10,
- });
-
- await sync.start();
- await sync.pingUntilAvailable();
-
- await wallet.client.call(WalletApiOperation.AddBackupProvider, {
- backupProviderBaseUrl: sync.baseUrl,
- activate: true,
- name: sync.baseUrl,
- });
-
- await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:10" });
-
- await wallet.client.call(WalletApiOperation.RunBackupCycle, {});
- await wallet.runUntilDone();
- await wallet.client.call(WalletApiOperation.RunBackupCycle, {});
-
- const backupRecovery = await wallet.client.call(
- WalletApiOperation.ExportBackupRecovery,
- {},
- );
-
- const wallet2 = new WalletCli(t, "wallet2");
-
- await wallet2.client.call(WalletApiOperation.ImportBackupRecovery, {
- recovery: backupRecovery,
- });
-
- await wallet2.client.call(WalletApiOperation.RunBackupCycle, {});
-
- console.log(
- "wallet1 balance before spend:",
- await wallet.client.call(WalletApiOperation.GetBalances, {}),
- );
-
- await makeTestPayment(t, {
- merchant,
- wallet,
- order: {
- summary: "foo",
- amount: "TESTKUDOS:7",
- },
- });
-
- await wallet.runUntilDone();
-
- console.log(
- "wallet1 balance after spend:",
- await wallet.client.call(WalletApiOperation.GetBalances, {}),
- );
-
- {
- console.log(
- "wallet2 balance:",
- await wallet2.client.call(WalletApiOperation.GetBalances, {}),
- );
- }
-
- // Now we double-spend with the second wallet
-
- {
- const instance = "default";
-
- const orderResp = await MerchantPrivateApi.createOrder(merchant, instance, {
- order: {
- amount: "TESTKUDOS:8",
- summary: "bla",
- fulfillment_url: "taler://fulfillment-success",
- },
- });
-
- let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus(
- merchant,
- {
- orderId: orderResp.order_id,
- },
- );
-
- t.assertTrue(orderStatus.order_status === "unpaid");
-
- // Make wallet pay for the order
-
- const preparePayResult = await wallet2.client.call(
- WalletApiOperation.PreparePayForUri,
- {
- talerPayUri: orderStatus.taler_pay_uri,
- },
- );
-
- t.assertTrue(
- preparePayResult.status === PreparePayResultType.PaymentPossible,
- );
-
- const res = await wallet2.client.call(WalletApiOperation.ConfirmPay, {
- proposalId: preparePayResult.proposalId,
- });
-
- console.log(res);
-
- // FIXME: wait for a notification that indicates insufficient funds!
-
- await withdrawViaBank(t, {
- wallet: wallet2,
- bank,
- exchange,
- amount: "TESTKUDOS:50",
- });
-
- const bal = await wallet2.client.call(WalletApiOperation.GetBalances, {});
- console.log("bal", bal);
-
- await wallet2.runUntilDone();
- }
-}
-
-runWalletBackupDoublespendTest.suites = ["wallet", "wallet-backup"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-wallettesting.ts b/packages/taler-wallet-cli/src/integrationtests/test-wallettesting.ts
deleted file mode 100644
index c21a7279b..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-wallettesting.ts
+++ /dev/null
@@ -1,226 +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/>
- */
-
-/**
- * Integration test for the wallet testing functionality used by the exchange
- * test cases.
- */
-
-/**
- * Imports.
- */
-import { Amounts } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
-import {
- BankService,
- ExchangeService,
- GlobalTestState,
- MerchantService,
- setupDb,
- WalletCli,
-} from "../harness/harness.js";
-import { SimpleTestEnvironment } from "../harness/helpers.js";
-
-const merchantAuthToken = "secret-token:sandbox";
-
-/**
- * Run a test case with a simple TESTKUDOS Taler environment, consisting
- * of one exchange, one bank and one merchant.
- */
-export async function createMyEnvironment(
- t: GlobalTestState,
- coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")),
-): Promise<SimpleTestEnvironment> {
- 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.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://x-taler-bank/merchant-default`],
- });
-
- console.log("setup done!");
-
- const wallet = new WalletCli(t);
-
- return {
- commonDb: db,
- exchange,
- merchant,
- wallet,
- bank,
- exchangeBankAccount,
- };
-}
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runWallettestingTest(t: GlobalTestState) {
- const { wallet, bank, exchange, merchant } = await createMyEnvironment(t);
-
- await wallet.client.call(WalletApiOperation.RunIntegrationTest, {
- amountToSpend: "TESTKUDOS:5",
- amountToWithdraw: "TESTKUDOS:10",
- bankBaseUrl: bank.baseUrl,
- exchangeBaseUrl: exchange.baseUrl,
- merchantAuthToken: merchantAuthToken,
- merchantBaseUrl: merchant.makeInstanceBaseUrl(),
- });
-
- let txns = await wallet.client.call(WalletApiOperation.GetTransactions, {});
- console.log(JSON.stringify(txns, undefined, 2));
- let txTypes = txns.transactions.map((x) => x.type);
-
- t.assertDeepEqual(txTypes, [
- "withdrawal",
- "payment",
- "withdrawal",
- "payment",
- "refund",
- "payment",
- ]);
-
- wallet.deleteDatabase();
-
- await wallet.client.call(WalletApiOperation.WithdrawTestBalance, {
- amount: "TESTKUDOS:10",
- bankBaseUrl: bank.baseUrl,
- exchangeBaseUrl: exchange.baseUrl,
- });
-
- await wallet.runUntilDone();
-
- await wallet.client.call(WalletApiOperation.TestPay, {
- amount: "TESTKUDOS:5",
- merchantAuthToken: merchantAuthToken,
- merchantBaseUrl: merchant.makeInstanceBaseUrl(),
- summary: "foo",
- });
-
- await wallet.runUntilDone();
-
- txns = await wallet.client.call(WalletApiOperation.GetTransactions, {});
- console.log(JSON.stringify(txns, undefined, 2));
- txTypes = txns.transactions.map((x) => x.type);
-
- t.assertDeepEqual(txTypes, ["withdrawal", "payment"]);
-
- wallet.deleteDatabase();
-
- await wallet.client.call(WalletApiOperation.WithdrawTestBalance, {
- amount: "TESTKUDOS:10",
- bankBaseUrl: bank.baseUrl,
- exchangeBaseUrl: exchange.baseUrl,
- });
-
- await wallet.runUntilDone();
-
- const coinDump = await wallet.client.call(WalletApiOperation.DumpCoins, {});
-
- console.log("coin dump:", JSON.stringify(coinDump, undefined, 2));
-
- let susp: string | undefined;
- {
- for (const c of coinDump.coins) {
- if (0 === Amounts.cmp(c.remaining_value, "TESTKUDOS:8")) {
- susp = c.coin_pub;
- }
- }
- }
-
- t.assertTrue(susp !== undefined);
-
- console.log("suspending coin");
-
- await wallet.client.call(WalletApiOperation.SetCoinSuspended, {
- coinPub: susp,
- suspended: true,
- });
-
- // This should fail, as we've suspended a coin that we need
- // to pay.
- await t.assertThrowsAsync(async () => {
- await wallet.client.call(WalletApiOperation.TestPay, {
- amount: "TESTKUDOS:5",
- merchantAuthToken: merchantAuthToken,
- merchantBaseUrl: merchant.makeInstanceBaseUrl(),
- summary: "foo",
- });
- });
-
- console.log("unsuspending coin");
-
- await wallet.client.call(WalletApiOperation.SetCoinSuspended, {
- coinPub: susp,
- suspended: false,
- });
-
- await wallet.client.call(WalletApiOperation.TestPay, {
- amount: "TESTKUDOS:5",
- merchantAuthToken: merchantAuthToken,
- merchantBaseUrl: merchant.makeInstanceBaseUrl(),
- summary: "foo",
- });
-
- await t.shutdown();
-}
-
-runWallettestingTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-abort-bank.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-abort-bank.ts
deleted file mode 100644
index fe719ea62..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-abort-bank.ts
+++ /dev/null
@@ -1,73 +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 { TalerErrorCode } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { GlobalTestState, BankApi, BankAccessApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runWithdrawalAbortBankTest(t: GlobalTestState) {
- // Set up test environment
-
- const { wallet, bank, exchange } = await createSimpleTestkudosEnvironment(t);
-
- // Create a withdrawal operation
-
- const user = await BankApi.createRandomBankUser(bank);
- const wop = await BankAccessApi.createWithdrawalOperation(
- bank,
- user,
- "TESTKUDOS:10",
- );
-
- // Hand it to the wallet
-
- await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, {
- talerWithdrawUri: wop.taler_withdraw_uri,
- });
-
- await wallet.runPending();
-
- // Confirm it
-
- await BankApi.abortWithdrawalOperation(bank, user, wop);
-
- // Withdraw
-
- const e = await t.assertThrowsOperationErrorAsync(async () => {
- await wallet.client.call(
- WalletApiOperation.AcceptBankIntegratedWithdrawal,
- {
- exchangeBaseUrl: exchange.baseUrl,
- talerWithdrawUri: wop.taler_withdraw_uri,
- },
- );
- });
- t.assertDeepEqual(
- e.operationError.code,
- TalerErrorCode.WALLET_WITHDRAWAL_OPERATION_ABORTED_BY_BANK,
- );
-
- await t.shutdown();
-}
-
-runWithdrawalAbortBankTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts
deleted file mode 100644
index 35969c78f..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-bank-integrated.ts
+++ /dev/null
@@ -1,70 +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, BankApi, BankAccessApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
-import { codecForBalancesResponse } from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runWithdrawalBankIntegratedTest(t: GlobalTestState) {
- // Set up test environment
-
- const { wallet, bank, exchange } = await createSimpleTestkudosEnvironment(t);
-
- // Create a withdrawal operation
-
- const user = await BankApi.createRandomBankUser(bank);
- const wop = await BankAccessApi.createWithdrawalOperation(
- bank,
- user,
- "TESTKUDOS:10",
- );
-
- // Hand it to the wallet
-
- const r1 = await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, {
- talerWithdrawUri: wop.taler_withdraw_uri,
- });
-
- await wallet.runPending();
-
- // Confirm it
-
- await BankApi.confirmWithdrawalOperation(bank, user, wop);
-
- // Withdraw
-
- const r2 = await wallet.client.call(WalletApiOperation.AcceptBankIntegratedWithdrawal, {
- exchangeBaseUrl: exchange.baseUrl,
- talerWithdrawUri: wop.taler_withdraw_uri,
- });
- await wallet.runUntilDone();
-
- // Check balance
-
- const balResp = await wallet.client.call(WalletApiOperation.GetBalances, {});
- t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available);
-
- await t.shutdown();
-}
-
-runWithdrawalBankIntegratedTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts
deleted file mode 100644
index 97beba1bf..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-fakebank.ts
+++ /dev/null
@@ -1,96 +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,
- BankApi,
- WalletCli,
- setupDb,
- ExchangeService,
- FakeBankService,
-} from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
-import { URL } from "@gnu-taler/taler-util";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runTestWithdrawalFakebankTest(t: GlobalTestState) {
- // Set up test environment
-
- const db = await setupDb(t);
-
- const bank = await FakeBankService.create(t, {
- currency: "TESTKUDOS",
- httpPort: 8082,
- });
-
- const exchange = ExchangeService.create(t, {
- name: "testexchange-1",
- currency: "TESTKUDOS",
- httpPort: 8081,
- database: db.connStr,
- });
-
- exchange.addBankAccount("1", {
- accountName: "exchange",
- accountPassword: "x",
- wireGatewayApiBaseUrl: new URL("/exchange/", bank.baseUrl).href,
- accountPaytoUri: "payto://x-taler-bank/localhost/exchange",
- });
-
- await bank.start();
-
- await bank.pingUntilAvailable();
-
- const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS"));
- exchange.addCoinConfigList(coinConfig);
-
- await exchange.start();
- await exchange.pingUntilAvailable();
-
- console.log("setup done!");
-
- const wallet = new WalletCli(t);
-
- await wallet.client.call(WalletApiOperation.AddExchange, {
- exchangeBaseUrl: exchange.baseUrl,
- });
-
- await wallet.client.call(WalletApiOperation.WithdrawFakebank, {
- exchange: exchange.baseUrl,
- amount: "TESTKUDOS:10",
- bank: bank.baseUrl,
- });
-
- await exchange.runWirewatchOnce();
-
- await wallet.runUntilDone();
-
- // Check balance
-
- const balResp = await wallet.client.call(WalletApiOperation.GetBalances, {});
- t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available);
-
- await t.shutdown();
-}
-
-runTestWithdrawalFakebankTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts b/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts
deleted file mode 100644
index b93d1b500..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/test-withdrawal-manual.ts
+++ /dev/null
@@ -1,72 +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, BankApi } from "../harness/harness.js";
-import { createSimpleTestkudosEnvironment } from "../harness/helpers.js";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
-
-/**
- * Run test for basic, bank-integrated withdrawal.
- */
-export async function runTestWithdrawalManualTest(t: GlobalTestState) {
- // Set up test environment
-
- const {
- wallet,
- bank,
- exchange,
- exchangeBankAccount,
- } = await createSimpleTestkudosEnvironment(t);
-
- // Create a withdrawal operation
-
- const user = await BankApi.createRandomBankUser(bank);
-
- await wallet.client.call(WalletApiOperation.AddExchange, {
- exchangeBaseUrl: exchange.baseUrl,
- });
-
-
- const wres = await wallet.client.call(WalletApiOperation.AcceptManualWithdrawal, {
- exchangeBaseUrl: exchange.baseUrl,
- amount: "TESTKUDOS:10",
- });
-
- const reservePub: string = wres.reservePub;
-
- await BankApi.adminAddIncoming(bank, {
- exchangeBankAccount,
- amount: "TESTKUDOS:10",
- debitAccountPayto: user.accountPaytoUri,
- reservePub: reservePub,
- });
-
- await exchange.runWirewatchOnce();
-
- await wallet.runUntilDone();
-
- // Check balance
-
- const balResp = await wallet.client.call(WalletApiOperation.GetBalances, {});
- t.assertAmountEquals("TESTKUDOS:9.72", balResp.balances[0].available);
-
- await t.shutdown();
-}
-
-runTestWithdrawalManualTest.suites = ["wallet"];
diff --git a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
deleted file mode 100644
index d985ed67f..000000000
--- a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
+++ /dev/null
@@ -1,482 +0,0 @@
-/*
- 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 {
- minimatch
-} from "@gnu-taler/taler-util";
-import {
- GlobalTestState,
- runTestWithState,
- shouldLingerInTest,
- TestRunResult,
-} from "../harness/harness.js";
-import { runPaymentTest } from "./test-payment";
-import { runPaymentDemoTest } from "./test-payment-on-demo";
-import * as fs from "fs";
-import * as path from "path";
-import * as os from "os";
-import * as child_process from "child_process";
-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 { runMerchantExchangeConfusionTest } from "./test-merchant-exchange-confusion";
-import { runLibeufinBasicTest } from "./test-libeufin-basic";
-import { runLibeufinC5xTest } from "./test-libeufin-c5x";
-import { runLibeufinNexusBalanceTest } from "./test-libeufin-nexus-balance";
-import { runLibeufinBadGatewayTest } from "./test-libeufin-bad-gateway";
-import { runLibeufinKeyrotationTest } from "./test-libeufin-keyrotation";
-import { runLibeufinRefundTest } from "./test-libeufin-refund";
-import { runLibeufinRefundMultipleUsersTest } from "./test-libeufin-refund-multiple-users";
-import { runLibeufinTutorialTest } from "./test-libeufin-tutorial";
-import { runLibeufinApiPermissionsTest } from "./test-libeufin-api-permissions";
-import { runLibeufinApiFacadeTest } from "./test-libeufin-api-facade";
-import { runLibeufinApiFacadeBadRequestTest } from "./test-libeufin-api-facade-bad-request";
-import { runLibeufinAnastasisFacadeTest } from "./test-libeufin-facade-anastasis";
-import { runLibeufinApiSchedulingTest } from "./test-libeufin-api-scheduling";
-import { runLibeufinApiBankconnectionTest } from "./test-libeufin-api-bankconnection";
-import { runLibeufinApiUsersTest } from "./test-libeufin-api-users";
-import { runLibeufinApiBankaccountTest } from "./test-libeufin-api-bankaccount";
-import { runLibeufinApiSandboxTransactionsTest } from "./test-libeufin-api-sandbox-transactions";
-import { runLibeufinApiSandboxCamtTest } from "./test-libeufin-api-sandbox-camt";
-import { runLibeufinSandboxWireTransferCliTest } from "./test-libeufin-sandbox-wire-transfer-cli";
-import { runDepositTest } from "./test-deposit";
-import CancellationToken from "cancellationtoken";
-import { runMerchantInstancesTest } from "./test-merchant-instances";
-import { runMerchantInstancesUrlsTest } from "./test-merchant-instances-urls";
-import { runWalletBackupBasicTest } from "./test-wallet-backup-basic";
-import { runMerchantInstancesDeleteTest } from "./test-merchant-instances-delete";
-import { runWalletBackupDoublespendTest } from "./test-wallet-backup-doublespend";
-import { runPaymentForgettableTest } from "./test-payment-forgettable.js";
-import { runPaymentZeroTest } from "./test-payment-zero.js";
-import { runMerchantSpecPublicOrdersTest } from "./test-merchant-spec-public-orders.js";
-import { runExchangeTimetravelTest } from "./test-exchange-timetravel.js";
-import { runDenomUnofferedTest } from "./test-denom-unoffered.js";
-import { runTestWithdrawalFakebankTest } from "./test-withdrawal-fakebank.js";
-
-/**
- * Test runner.
- */
-
-/**
- * Spec for one test.
- */
-interface TestMainFunction {
- (t: GlobalTestState): Promise<void>;
- timeoutMs?: number;
- excludeByDefault?: boolean;
- suites?: string[];
-}
-
-const allTests: TestMainFunction[] = [
- runBankApiTest,
- runClaimLoopTest,
- runDepositTest,
- runDenomUnofferedTest,
- runExchangeManagementTest,
- runExchangeTimetravelTest,
- runFeeRegressionTest,
- runLibeufinBasicTest,
- runLibeufinKeyrotationTest,
- runLibeufinTutorialTest,
- runLibeufinRefundTest,
- runLibeufinC5xTest,
- runLibeufinNexusBalanceTest,
- runLibeufinBadGatewayTest,
- runLibeufinRefundMultipleUsersTest,
- runLibeufinApiPermissionsTest,
- runLibeufinApiFacadeTest,
- runLibeufinApiFacadeBadRequestTest,
- runLibeufinAnastasisFacadeTest,
- runLibeufinApiSchedulingTest,
- runLibeufinApiUsersTest,
- runLibeufinApiBankaccountTest,
- runLibeufinApiBankconnectionTest,
- runLibeufinApiSandboxTransactionsTest,
- runLibeufinApiSandboxCamtTest,
- runLibeufinSandboxWireTransferCliTest,
- runMerchantExchangeConfusionTest,
- runMerchantInstancesTest,
- runMerchantInstancesDeleteTest,
- runMerchantInstancesUrlsTest,
- runMerchantLongpollingTest,
- runMerchantSpecPublicOrdersTest,
- runMerchantRefundApiTest,
- runPayAbortTest,
- runPaymentClaimTest,
- runPaymentFaultTest,
- runPaymentForgettableTest,
- runPaymentIdempotencyTest,
- runPaymentMultipleTest,
- runPaymentTest,
- runPaymentDemoTest,
- runPaymentTransientTest,
- runPaymentZeroTest,
- runPayPaidTest,
- runPaywallFlowTest,
- runRefundAutoTest,
- runRefundGoneTest,
- runRefundIncrementalTest,
- runRefundTest,
- runRevocationTest,
- runTestWithdrawalManualTest,
- runTestWithdrawalFakebankTest,
- runTimetravelAutorefreshTest,
- runTimetravelWithdrawTest,
- runTippingTest,
- runWalletBackupBasicTest,
- runWalletBackupDoublespendTest,
- runWallettestingTest,
- runWithdrawalAbortBankTest,
- runWithdrawalBankIntegratedTest,
-];
-
-export interface TestRunSpec {
- includePattern?: string;
- suiteSpec?: string;
- dryRun?: boolean;
- verbosity: number;
-}
-
-export interface TestInfo {
- name: string;
- suites: string[];
- excludeByDefault: boolean;
-}
-
-function updateCurrentSymlink(testDir: string): void {
- const currLink = path.join(
- os.tmpdir(),
- `taler-integrationtests-${os.userInfo().username}-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]/g, (x) => {
- return x[0] + "-" + x[1];
- })
- .toLowerCase();
-}
-
-interface RunTestChildInstruction {
- testName: string;
- testRootDir: string;
-}
-
-export async function runTests(spec: TestRunSpec) {
- const testRootDir = fs.mkdtempSync(
- path.join(os.tmpdir(), "taler-integrationtests-"),
- );
- updateCurrentSymlink(testRootDir);
- console.log("testsuite root directory: ", testRootDir);
-
- const testResults: TestRunResult[] = [];
-
- let currentChild: child_process.ChildProcess | undefined;
-
- const handleSignal = (s: NodeJS.Signals) => {
- console.log(`received signal ${s} in test parent`);
- if (currentChild) {
- currentChild.kill("SIGTERM");
- }
- reportAndQuit(testRootDir, testResults, true);
- };
-
- process.on("SIGINT", (s) => handleSignal(s));
- process.on("SIGTERM", (s) => handleSignal(s));
- //process.on("unhandledRejection", handleSignal);
- //process.on("uncaughtException", handleSignal);
-
- let suites: Set<string> | undefined;
-
- if (spec.suiteSpec) {
- suites = new Set(spec.suiteSpec.split(",").map((x) => x.trim()));
- }
-
- for (const [n, testCase] of allTests.entries()) {
- const testName = getTestName(testCase);
- if (spec.includePattern && !minimatch(testName, spec.includePattern)) {
- continue;
- }
-
- if (suites) {
- const ts = new Set(testCase.suites ?? []);
- const intersection = new Set([...suites].filter((x) => ts.has(x)));
- if (intersection.size === 0) {
- continue;
- }
- } else {
- if (testCase.excludeByDefault) {
- continue;
- }
- }
-
- if (spec.dryRun) {
- console.log(`dry run: would run test ${testName}`);
- continue;
- }
-
- const testInstr: RunTestChildInstruction = {
- testName,
- testRootDir,
- };
-
- currentChild = child_process.fork(__filename, ["__TWCLI_TESTWORKER"], {
- env: {
- TWCLI_RUN_TEST_INSTRUCTION: JSON.stringify(testInstr),
- ...process.env,
- },
- stdio: ["pipe", "pipe", "pipe", "ipc"],
- });
-
- const testDir = path.join(testRootDir, testName);
- fs.mkdirSync(testDir, { recursive: true });
-
- const harnessLogFilename = path.join(testRootDir, testName, "harness.log");
- const harnessLogStream = fs.createWriteStream(harnessLogFilename);
-
- if (spec.verbosity > 0) {
- currentChild.stderr?.pipe(process.stderr);
- currentChild.stdout?.pipe(process.stdout);
- }
-
- currentChild.stdout?.pipe(harnessLogStream);
- currentChild.stderr?.pipe(harnessLogStream);
-
- const defaultTimeout = 60000;
- const testTimeoutMs = testCase.timeoutMs ?? defaultTimeout;
-
- console.log(`running ${testName} with timeout ${testTimeoutMs}ms`);
-
- const { token } = CancellationToken.timeout(testTimeoutMs);
-
- const resultPromise: Promise<TestRunResult> = new Promise(
- (resolve, reject) => {
- let msg: TestRunResult | undefined;
- currentChild!.on("message", (m) => {
- if (token.isCancelled) {
- return;
- }
- msg = m as TestRunResult;
- });
- currentChild!.on("exit", (code, signal) => {
- if (token.isCancelled) {
- return;
- }
- console.log(`process exited code=${code} signal=${signal}`);
- if (signal) {
- reject(new Error(`test worker exited with signal ${signal}`));
- } else if (code != 0) {
- reject(new Error(`test worker exited with code ${code}`));
- } else if (!msg) {
- reject(
- new Error(
- `test worker exited without giving back the test results`,
- ),
- );
- } else {
- resolve(msg);
- }
- });
- currentChild!.on("error", (err) => {
- if (token.isCancelled) {
- return;
- }
- reject(err);
- });
- },
- );
-
- let result: TestRunResult;
-
- try {
- result = await token.racePromise(resultPromise);
- } catch (e: any) {
- console.error(`test ${testName} timed out`);
- if (token.isCancelled) {
- result = {
- status: "fail",
- reason: "timeout",
- timeSec: testTimeoutMs / 1000,
- name: testName,
- };
- currentChild.kill("SIGTERM");
- } else {
- throw Error(e);
- }
- }
-
- harnessLogStream.close();
-
- console.log(`parent: got result ${JSON.stringify(result)}`);
-
- testResults.push(result);
- }
-
- reportAndQuit(testRootDir, testResults);
-}
-
-export function reportAndQuit(
- testRootDir: string,
- testResults: TestRunResult[],
- interrupted: boolean = false,
-): never {
- let numTotal = 0;
- let numFail = 0;
- let numSkip = 0;
- let numPass = 0;
-
- for (const result of testResults) {
- 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, interrupted }, undefined, 2),
- );
- if (interrupted) {
- console.log("test suite was interrupted");
- }
- console.log(`See ${resultsFile} for details`);
- console.log(`Skipped: ${numSkip}/${numTotal}`);
- console.log(`Failed: ${numFail}/${numTotal}`);
- console.log(`Passed: ${numPass}/${numTotal}`);
-
- if (interrupted) {
- process.exit(3);
- } else if (numPass < numTotal - numSkip) {
- process.exit(1);
- } else {
- process.exit(0);
- }
-}
-
-export function getTestInfo(): TestInfo[] {
- return allTests.map((x) => ({
- name: getTestName(x),
- suites: x.suites ?? [],
- excludeByDefault: x.excludeByDefault ?? false,
- }));
-}
-
-const runTestInstrStr = process.env["TWCLI_RUN_TEST_INSTRUCTION"];
-if (runTestInstrStr && process.argv.includes("__TWCLI_TESTWORKER")) {
- // Test will call taler-wallet-cli, so we must not propagate this variable.
- delete process.env["TWCLI_RUN_TEST_INSTRUCTION"];
- const { testRootDir, testName } = JSON.parse(
- runTestInstrStr,
- ) as RunTestChildInstruction;
- console.log(`running test ${testName} in worker process`);
-
- process.on("disconnect", () => {
- console.log("got disconnect from parent");
- process.exit(3);
- });
-
- try {
- require("source-map-support").install();
- } catch (e) {
- // Do nothing.
- }
-
- const runTest = async () => {
- let testMain: TestMainFunction | undefined;
- for (const t of allTests) {
- if (getTestName(t) === testName) {
- testMain = t;
- break;
- }
- }
-
- if (!process.send) {
- console.error("can't communicate with parent");
- process.exit(2);
- }
-
- if (!testMain) {
- console.log(`test ${testName} not found`);
- process.exit(2);
- }
-
- const testDir = path.join(testRootDir, testName);
- console.log(`running test ${testName}`);
- const gc = new GlobalTestState({
- testDir,
- });
- const testResult = await runTestWithState(gc, testMain, testName);
- process.send(testResult);
- };
-
- runTest()
- .then(() => {
- console.log(`test ${testName} finished in worker`);
- if (shouldLingerInTest()) {
- console.log("lingering ...");
- return;
- }
- process.exit(0);
- })
- .catch((e) => {
- console.log(e);
- process.exit(1);
- });
-}
diff --git a/packages/taler-wallet-cli/src/lint.ts b/packages/taler-wallet-cli/src/lint.ts
deleted file mode 100644
index 2b888ccf4..000000000
--- a/packages/taler-wallet-cli/src/lint.ts
+++ /dev/null
@@ -1,534 +0,0 @@
-/*
- 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/>
- */
-
-/**
- * The deployment linter implements checks for a deployment
- * of the GNU Taler exchange. It is meant to help sysadmins
- * when setting up an exchange.
- *
- * The linter does checks in the configuration and uses
- * various tools of the exchange in test mode (-t).
- *
- * To be able to run the tools as the right user, the linter should be
- * run as root.
- *
- * @author Florian Dold <dold@taler.net>
- */
-
-/**
- * Imports.
- */
-import {
- codecForExchangeKeysJson,
- codecForKeysManagementResponse,
- Configuration,
- decodeCrock,
-} from "@gnu-taler/taler-util";
-import {
- NodeHttpLib,
- readSuccessResponseJsonOrThrow,
-} from "@gnu-taler/taler-wallet-core";
-import { URL } from "url";
-import { spawn } from "child_process";
-import { delayMs } from "./harness/harness.js";
-
-interface BasicConf {
- mainCurrency: string;
-}
-
-interface PubkeyConf {
- masterPublicKey: string;
-}
-
-const httpLib = new NodeHttpLib();
-
-interface ShellResult {
- stdout: string;
- stderr: string;
- status: number;
-}
-
-interface LintContext {
- /**
- * Be more verbose.
- */
- verbose: boolean;
-
- /**
- * Always continue even after errors.
- */
- cont: boolean;
-
- cfg: Configuration;
-
- numErr: number;
-}
-
-/**
- * Run a shell command, return stdout.
- */
-export async function sh(
- context: LintContext,
- command: string,
- env: { [index: string]: string | undefined } = process.env,
-): Promise<ShellResult> {
- if (context.verbose) {
- console.log("executing command:", command);
- }
- return new Promise((resolve, reject) => {
- const stdoutChunks: Buffer[] = [];
- const stderrChunks: Buffer[] = [];
- const proc = spawn(command, {
- stdio: ["inherit", "pipe", "pipe"],
- shell: true,
- env: env,
- });
- proc.stdout.on("data", (x) => {
- if (x instanceof Buffer) {
- stdoutChunks.push(x);
- } else {
- throw Error("unexpected data chunk type");
- }
- });
- proc.stderr.on("data", (x) => {
- if (x instanceof Buffer) {
- stderrChunks.push(x);
- } else {
- throw Error("unexpected data chunk type");
- }
- });
- proc.on("exit", (code, signal) => {
- if (code != 0 && context.verbose) {
- console.log(`child process exited (${code} / ${signal})`);
- }
- const bOut = Buffer.concat(stdoutChunks).toString("utf-8");
- const bErr = Buffer.concat(stderrChunks).toString("utf-8");
- resolve({
- status: code ?? -1,
- stderr: bErr,
- stdout: bOut,
- });
- });
- proc.on("error", () => {
- reject(Error("Child process had error"));
- });
- });
-}
-
-function checkBasicConf(context: LintContext): BasicConf {
- const cfg = context.cfg;
- const currencyEntry = cfg.getString("taler", "currency");
- let mainCurrency: string | undefined;
-
- if (!currencyEntry.value) {
- context.numErr++;
- console.log("error: currency not defined in section TALER option CURRENCY");
- console.log("Aborting further checks.");
- process.exit(1);
- } else {
- mainCurrency = currencyEntry.value.toUpperCase();
- }
-
- if (mainCurrency === "KUDOS") {
- console.log(
- "warning: section TALER option CURRENCY contains toy currency value KUDOS",
- );
- }
-
- const roundUnit = cfg.getAmount("taler", "currency_round_unit");
- const ru = roundUnit.required();
- if (ru.currency.toLowerCase() != mainCurrency.toLowerCase()) {
- context.numErr++;
- console.log(
- "error: [TALER]/CURRENCY_ROUND_UNIT: currency does not match main currency",
- );
- }
- return { mainCurrency };
-}
-
-function checkCoinConfig(context: LintContext, basic: BasicConf): void {
- const cfg = context.cfg;
- const coinPrefix1 = "COIN_";
- const coinPrefix2 = "COIN-";
- let numCoins = 0;
-
- for (const secName of cfg.getSectionNames()) {
- if (!(secName.startsWith(coinPrefix1) || secName.startsWith(coinPrefix2))) {
- continue;
- }
- numCoins++;
-
- // FIXME: check that section is well-formed
- }
-
- if (numCoins == 0) {
- context.numErr++;
- console.log(
- "error: no coin denomination configured, please configure [coin-*] sections",
- );
- }
-}
-
-async function checkWireConfig(context: LintContext): Promise<void> {
- const cfg = context.cfg;
- const accountPrefix = "EXCHANGE-ACCOUNT-";
- const accountCredentialsPrefix = "EXCHANGE-ACCOUNTCREDENTIALS-";
-
- let accounts = new Set<string>();
- let credentials = new Set<string>();
-
- for (const secName of cfg.getSectionNames()) {
- if (secName.startsWith(accountPrefix)) {
- accounts.add(secName.slice(accountPrefix.length));
- // FIXME: check settings
- }
-
- if (secName.startsWith(accountCredentialsPrefix)) {
- credentials.add(secName.slice(accountCredentialsPrefix.length));
- // FIXME: check settings
- }
- }
-
- if (accounts.size === 0) {
- context.numErr++;
- console.log(
- "error: No accounts configured (no sections EXCHANGE-ACCOUNT-*).",
- );
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- }
-
- for (const acc of accounts) {
- if (!credentials.has(acc)) {
- console.log(
- `warning: no credentials configured for exchange-account-${acc}`,
- );
- }
- }
-
- for (const acc of accounts) {
- // test credit history
- {
- const res = await sh(
- context,
- "su -l --shell /bin/sh " +
- `-c 'taler-exchange-wire-gateway-client -s exchange-accountcredentials-${acc} --credit-history' ` +
- "taler-exchange-wire",
- );
- if (res.status != 0) {
- context.numErr++;
- console.log(res.stdout);
- console.log(res.stderr);
- console.log(
- "error: Could not run taler-exchange-wire-gateway-client. Please review logs above.",
- );
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- }
- }
- }
-
- // TWG client
- {
- const res = await sh(
- context,
- `su -l --shell /bin/sh -c 'taler-exchange-wirewatch -t' taler-exchange-wire`,
- );
- if (res.status != 0) {
- context.numErr++;
- console.log(res.stdout);
- console.log(res.stderr);
- console.log("error: Could not run wirewatch. Please review logs above.");
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- }
- }
-
- // Wirewatch
- {
- const res = await sh(
- context,
- `su -l --shell /bin/sh -c 'taler-exchange-wirewatch -t' taler-exchange-wire`,
- );
- if (res.status != 0) {
- context.numErr++;
- console.log(res.stdout);
- console.log(res.stderr);
- console.log("error: Could not run wirewatch. Please review logs above.");
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- }
- }
-
- // Closer
- {
- const res = await sh(
- context,
- `su -l --shell /bin/sh -c 'taler-exchange-closer -t' taler-exchange-closer`,
- );
- if (res.status != 0) {
- context.numErr++;
- console.log(res.stdout);
- console.log(res.stderr);
- console.log("error: Could not run closer. Please review logs above.");
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- }
- }
-}
-
-async function checkAggregatorConfig(context: LintContext) {
- const res = await sh(
- context,
- "su -l --shell /bin/sh -c 'taler-exchange-aggregator -t' taler-exchange-aggregator",
- );
- if (res.status != 0) {
- context.numErr++;
- console.log(res.stdout);
- console.log(res.stderr);
- console.log("error: Could not run aggregator. Please review logs above.");
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- }
-}
-
-async function checkCloserConfig(context: LintContext) {
- const res = await sh(
- context,
- `su -l --shell /bin/sh -c 'taler-exchange-closer -t' taler-exchange-closer`,
- );
- if (res.status != 0) {
- context.numErr++;
- console.log(res.stdout);
- console.log(res.stderr);
- console.log("error: Could not run closer. Please review logs above.");
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- }
-}
-
-function checkMasterPublicKeyConfig(context: LintContext): PubkeyConf {
- const cfg = context.cfg;
- const pub = cfg.getString("exchange", "master_public_key");
-
- const pubDecoded = decodeCrock(pub.required());
-
- if (pubDecoded.length != 32) {
- context.numErr++;
- console.log("error: invalid master public key");
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- }
-
- return {
- masterPublicKey: pub.required(),
- };
-}
-
-export async function checkExchangeHttpd(
- context: LintContext,
- pubConf: PubkeyConf,
-): Promise<void> {
- const cfg = context.cfg;
- const baseUrlEntry = cfg.getString("exchange", "base_url");
-
- if (!baseUrlEntry.isDefined) {
- context.numErr++;
- console.log(
- "error: configuration needs to specify section EXCHANGE option BASE_URL",
- );
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- }
-
- const baseUrl = baseUrlEntry.required();
-
- if (!baseUrl.startsWith("http")) {
- context.numErr++;
- console.log(
- "error: section EXCHANGE option BASE_URL needs to be an http or https URL",
- );
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- }
-
- if (!baseUrl.endsWith("/")) {
- context.numErr++;
- console.log(
- "error: section EXCHANGE option BASE_URL needs to end with a slash",
- );
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- }
-
- if (!baseUrl.startsWith("https://")) {
- console.log(
- "warning: section EXCHANGE option BASE_URL: it is recommended to serve the exchange via HTTPS",
- );
- }
-
- {
- const mgmtUrl = new URL("management/keys", baseUrl);
- const resp = await httpLib.get(mgmtUrl.href);
-
- const futureKeys = await readSuccessResponseJsonOrThrow(
- resp,
- codecForKeysManagementResponse(),
- );
-
- if (futureKeys.future_denoms.length > 0) {
- console.log(
- `warning: exchange has denomination keys that need to be signed by the offline signing procedure`,
- );
- }
-
- if (futureKeys.future_signkeys.length > 0) {
- console.log(
- `warning: exchange has signing keys that need to be signed by the offline signing procedure`,
- );
- }
- }
-
- // Check if we can use /keys already
- {
- const keysUrl = new URL("keys", baseUrl);
-
- const resp = await Promise.race([httpLib.get(keysUrl.href), delayMs(2000)]);
-
- if (!resp) {
- context.numErr++;
- console.log(
- "error: request to /keys timed out. " +
- "Make sure to sign and upload denomination and signing keys " +
- "with taler-exchange-offline.",
- );
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- } else {
- const keys = await readSuccessResponseJsonOrThrow(
- resp,
- codecForExchangeKeysJson(),
- );
-
- if (keys.master_public_key !== pubConf.masterPublicKey) {
- context.numErr++;
- console.log(
- "error: master public key of exchange does not match public key of live exchange",
- );
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- }
- }
- }
-
- // Check /wire
- {
- const keysUrl = new URL("wire", baseUrl);
-
- const resp = await Promise.race([httpLib.get(keysUrl.href), delayMs(2000)]);
-
- if (!resp) {
- context.numErr++;
- console.log(
- "error: request to /wire timed out. " +
- "Make sure to sign and upload accounts and wire fees " +
- "using the taler-exchange-offline tool.",
- );
- if (!context.cont) {
- console.log("Aborting further checks.");
- process.exit(1);
- }
- } else {
- if (resp.status !== 200) {
- console.log(
- "error: Can't access exchange /wire. Please check " +
- "the logs of taler-exchange-httpd for further information.",
- );
- }
- }
- }
-}
-
-/**
- * Do some basic checks in the configuration of a Taler deployment.
- */
-export async function lintExchangeDeployment(
- verbose: boolean,
- cont: boolean,
-): Promise<void> {
- if (process.getuid() != 0) {
- console.log(
- "warning: the exchange deployment linter is designed to be run as root",
- );
- }
-
- const cfg = Configuration.load();
-
- const context: LintContext = {
- cont,
- verbose,
- cfg,
- numErr: 0,
- };
-
- const basic = checkBasicConf(context);
-
- checkCoinConfig(context, basic);
-
- await checkWireConfig(context);
-
- await checkAggregatorConfig(context);
-
- await checkCloserConfig(context);
-
- const pubConf = checkMasterPublicKeyConfig(context);
-
- await checkExchangeHttpd(context, pubConf);
-
- if (context.numErr == 0) {
- console.log("Linting completed without errors.");
- process.exit(0);
- } else {
- console.log(`Linting completed with ${context.numErr} errors.`);
- process.exit(1);
- }
-}
diff --git a/packages/taler-wallet-cli/tsconfig.json b/packages/taler-wallet-cli/tsconfig.json
index 945161176..4b46790c8 100644
--- a/packages/taler-wallet-cli/tsconfig.json
+++ b/packages/taler-wallet-cli/tsconfig.json
@@ -2,11 +2,11 @@
"compileOnSave": true,
"compilerOptions": {
"composite": true,
- "target": "ES2018",
- "module": "ESNext",
- "moduleResolution": "node",
+ "target": "ES2020",
+ "module": "Node16",
+ "moduleResolution": "Node16",
"sourceMap": true,
- "lib": ["es6"],
+ "lib": ["ES2020"],
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"strict": true,
@@ -21,7 +21,7 @@
"baseUrl": "./src",
"typeRoots": ["./node_modules/@types"]
},
- "include": ["src/**/*"],
+ "include": ["src/**/*", "../taler-util/src/twrpc.ts"],
"references": [
{
"path": "../taler-wallet-core/"