aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-03-03 21:20:05 +0100
committerFlorian Dold <florian@dold.me>2021-03-03 21:20:05 +0100
commit1a0610f222f841237f9940a3a3a55635eec7d324 (patch)
treeb80c67be371a006080cd26b2d61f26cd3aa77697
parent186a38250ff4a11f88c8956db4da574f08e741dc (diff)
WIP: wallet backup integration test
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/harness.ts29
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/sync.ts121
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-basic.ts66
-rw-r--r--packages/taler-wallet-cli/src/integrationtests/testrunner.ts2
-rw-r--r--packages/taler-wallet-core/src/types/backupTypes.ts2
-rw-r--r--packages/taler-wallet-core/src/wallet.ts4
6 files changed, 219 insertions, 5 deletions
diff --git a/packages/taler-wallet-cli/src/integrationtests/harness.ts b/packages/taler-wallet-cli/src/integrationtests/harness.ts
index 118bc35d4..a2d2b8e13 100644
--- a/packages/taler-wallet-cli/src/integrationtests/harness.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/harness.ts
@@ -99,6 +99,7 @@ import {
import { ApplyRefundResponse } from "@gnu-taler/taler-wallet-core";
import { PendingOperationsResponse } from "@gnu-taler/taler-wallet-core";
import { CoinConfig } from "./denomStructures";
+import { AddBackupProviderRequest, BackupInfo } from "@gnu-taler/taler-wallet-core/src/operations/backup";
const exec = util.promisify(require("child_process").exec);
@@ -396,7 +397,11 @@ export interface TalerConfig {
}
export interface DbInfo {
+ /**
+ * Postgres connection string.
+ */
connStr: string;
+
dbname: string;
}
@@ -418,7 +423,7 @@ export interface BankConfig {
maxDebt?: string;
}
-function setPaths(config: Configuration, home: string) {
+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).
@@ -647,7 +652,7 @@ export class BankService implements BankServiceInterface {
bc: BankConfig,
): Promise<BankService> {
const config = new Configuration();
- setPaths(config, gc.testDir + "/talerhome");
+ setTalerPaths(config, gc.testDir + "/talerhome");
config.setString("taler", "currency", bc.currency);
config.setString("bank", "database", bc.database);
config.setString("bank", "http_port", `${bc.httpPort}`);
@@ -860,7 +865,7 @@ export class ExchangeService implements ExchangeServiceInterface {
"currency_round_unit",
e.roundUnit ?? `${e.currency}:0.01`,
);
- setPaths(config, gc.testDir + "/talerhome");
+ setTalerPaths(config, gc.testDir + "/talerhome");
config.setString(
"exchange",
@@ -1425,7 +1430,7 @@ export class MerchantService implements MerchantServiceInterface {
config.setString("taler", "currency", mc.currency);
const cfgFilename = gc.testDir + `/merchant-${mc.name}.conf`;
- setPaths(config, gc.testDir + "/talerhome");
+ setTalerPaths(config, gc.testDir + "/talerhome");
config.setString("merchant", "serve", "tcp");
config.setString("merchant", "port", `${mc.httpPort}`);
config.setString(
@@ -1846,4 +1851,20 @@ export class WalletCli {
}
throw new OperationFailedError(resp.error);
}
+
+ async addBackupProvider(req: AddBackupProviderRequest): Promise<void> {
+ const resp = await this.apiRequest("addBackupProvider", req);
+ if (resp.type === "response") {
+ return;
+ }
+ throw new OperationFailedError(resp.error);
+ }
+
+ async getBackupInfo(): Promise<BackupInfo> {
+ const resp = await this.apiRequest("getBackupInfo", {});
+ if (resp.type === "response") {
+ return resp.result as BackupInfo;
+ }
+ throw new OperationFailedError(resp.error);
+ }
}
diff --git a/packages/taler-wallet-cli/src/integrationtests/sync.ts b/packages/taler-wallet-cli/src/integrationtests/sync.ts
new file mode 100644
index 000000000..7aa4b2893
--- /dev/null
+++ b/packages/taler-wallet-cli/src/integrationtests/sync.ts
@@ -0,0 +1,121 @@
+/*
+ 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 { Configuration, URL } from "@gnu-taler/taler-wallet-core";
+import { getRandomIban, getRandomString } from "./helpers";
+import * as fs from "fs";
+import * as util from "util";
+import {
+ GlobalTestState,
+ DbInfo,
+ pingProc,
+ ProcessWrapper,
+ runCommand,
+ setupDb,
+ sh,
+} from "./harness";
+
+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 (ony 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.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/integrationtests/test-wallet-backup-basic.ts b/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-basic.ts
new file mode 100644
index 000000000..9201c558c
--- /dev/null
+++ b/packages/taler-wallet-cli/src/integrationtests/test-wallet-backup-basic.ts
@@ -0,0 +1,66 @@
+/*
+ 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";
+import { createSimpleTestkudosEnvironment } from "./helpers";
+import { codecForBalancesResponse } from "@gnu-taler/taler-wallet-core";
+import { SyncService } from "./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.addBackupProvider({
+ backupProviderBaseUrl: sync.baseUrl,
+ activate: false,
+ });
+
+ {
+ const bi = await wallet.getBackupInfo();
+ t.assertDeepEqual(bi.providers[0].active, false);
+ }
+
+ await wallet.addBackupProvider({
+ backupProviderBaseUrl: sync.baseUrl,
+ activate: false,
+ });
+
+ {
+ const bi = await wallet.getBackupInfo();
+ t.assertDeepEqual(bi.providers[0].active, true);
+ }
+}
diff --git a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
index 9aeb7ac73..6b158e8d0 100644
--- a/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
+++ b/packages/taler-wallet-cli/src/integrationtests/testrunner.ts
@@ -61,6 +61,7 @@ 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";
/**
* Test runner.
@@ -107,6 +108,7 @@ const allTests: TestMainFunction[] = [
runTimetravelAutorefreshTest,
runTimetravelWithdrawTest,
runTippingTest,
+ runWalletBackupBasicTest,
runWallettestingTest,
runWithdrawalAbortBankTest,
runWithdrawalBankIntegratedTest,
diff --git a/packages/taler-wallet-core/src/types/backupTypes.ts b/packages/taler-wallet-core/src/types/backupTypes.ts
index 5e4844b51..d4b1625f6 100644
--- a/packages/taler-wallet-core/src/types/backupTypes.ts
+++ b/packages/taler-wallet-core/src/types/backupTypes.ts
@@ -189,7 +189,7 @@ export interface WalletBackupContentV1 {
/**
* Clock when the purchase was deleted
*/
- clock_deleted: number;
+ clock_deleted: ClockStamp;
/**
* Proposal ID identifying the purchase.
diff --git a/packages/taler-wallet-core/src/wallet.ts b/packages/taler-wallet-core/src/wallet.ts
index 5aa951d5f..8f9999cc1 100644
--- a/packages/taler-wallet-core/src/wallet.ts
+++ b/packages/taler-wallet-core/src/wallet.ts
@@ -1159,6 +1159,10 @@ export class Wallet {
await runBackupCycle(this.ws);
return {};
}
+ case "getBackupInfo": {
+ const resp = await getBackupInfo(this.ws);
+ return resp;
+ }
case "createDepositGroup": {
const req = codecForCreateDepositGroupRequest().decode(payload);
return await createDepositGroup(this.ws, req);