WIP: wallet backup integration test
This commit is contained in:
parent
186a38250f
commit
1a0610f222
@ -99,6 +99,7 @@ import {
|
|||||||
import { ApplyRefundResponse } from "@gnu-taler/taler-wallet-core";
|
import { ApplyRefundResponse } from "@gnu-taler/taler-wallet-core";
|
||||||
import { PendingOperationsResponse } from "@gnu-taler/taler-wallet-core";
|
import { PendingOperationsResponse } from "@gnu-taler/taler-wallet-core";
|
||||||
import { CoinConfig } from "./denomStructures";
|
import { CoinConfig } from "./denomStructures";
|
||||||
|
import { AddBackupProviderRequest, BackupInfo } from "@gnu-taler/taler-wallet-core/src/operations/backup";
|
||||||
|
|
||||||
const exec = util.promisify(require("child_process").exec);
|
const exec = util.promisify(require("child_process").exec);
|
||||||
|
|
||||||
@ -396,7 +397,11 @@ export interface TalerConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DbInfo {
|
export interface DbInfo {
|
||||||
|
/**
|
||||||
|
* Postgres connection string.
|
||||||
|
*/
|
||||||
connStr: string;
|
connStr: string;
|
||||||
|
|
||||||
dbname: string;
|
dbname: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,7 +423,7 @@ export interface BankConfig {
|
|||||||
maxDebt?: string;
|
maxDebt?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setPaths(config: Configuration, home: string) {
|
function setTalerPaths(config: Configuration, home: string) {
|
||||||
config.setString("paths", "taler_home", home);
|
config.setString("paths", "taler_home", home);
|
||||||
// We need to make sure that the path of taler_runtime_dir isn't too long,
|
// 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).
|
// as it contains unix domain sockets (108 character limit).
|
||||||
@ -647,7 +652,7 @@ export class BankService implements BankServiceInterface {
|
|||||||
bc: BankConfig,
|
bc: BankConfig,
|
||||||
): Promise<BankService> {
|
): Promise<BankService> {
|
||||||
const config = new Configuration();
|
const config = new Configuration();
|
||||||
setPaths(config, gc.testDir + "/talerhome");
|
setTalerPaths(config, gc.testDir + "/talerhome");
|
||||||
config.setString("taler", "currency", bc.currency);
|
config.setString("taler", "currency", bc.currency);
|
||||||
config.setString("bank", "database", bc.database);
|
config.setString("bank", "database", bc.database);
|
||||||
config.setString("bank", "http_port", `${bc.httpPort}`);
|
config.setString("bank", "http_port", `${bc.httpPort}`);
|
||||||
@ -860,7 +865,7 @@ export class ExchangeService implements ExchangeServiceInterface {
|
|||||||
"currency_round_unit",
|
"currency_round_unit",
|
||||||
e.roundUnit ?? `${e.currency}:0.01`,
|
e.roundUnit ?? `${e.currency}:0.01`,
|
||||||
);
|
);
|
||||||
setPaths(config, gc.testDir + "/talerhome");
|
setTalerPaths(config, gc.testDir + "/talerhome");
|
||||||
|
|
||||||
config.setString(
|
config.setString(
|
||||||
"exchange",
|
"exchange",
|
||||||
@ -1425,7 +1430,7 @@ export class MerchantService implements MerchantServiceInterface {
|
|||||||
config.setString("taler", "currency", mc.currency);
|
config.setString("taler", "currency", mc.currency);
|
||||||
|
|
||||||
const cfgFilename = gc.testDir + `/merchant-${mc.name}.conf`;
|
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", "serve", "tcp");
|
||||||
config.setString("merchant", "port", `${mc.httpPort}`);
|
config.setString("merchant", "port", `${mc.httpPort}`);
|
||||||
config.setString(
|
config.setString(
|
||||||
@ -1846,4 +1851,20 @@ export class WalletCli {
|
|||||||
}
|
}
|
||||||
throw new OperationFailedError(resp.error);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
121
packages/taler-wallet-cli/src/integrationtests/sync.ts
Normal file
121
packages/taler-wallet-cli/src/integrationtests/sync.ts
Normal file
@ -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,
|
||||||
|
) {}
|
||||||
|
}
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
@ -61,6 +61,7 @@ import { runDepositTest } from "./test-deposit";
|
|||||||
import CancellationToken from "cancellationtoken";
|
import CancellationToken from "cancellationtoken";
|
||||||
import { runMerchantInstancesTest } from "./test-merchant-instances";
|
import { runMerchantInstancesTest } from "./test-merchant-instances";
|
||||||
import { runMerchantInstancesUrlsTest } from "./test-merchant-instances-urls";
|
import { runMerchantInstancesUrlsTest } from "./test-merchant-instances-urls";
|
||||||
|
import { runWalletBackupBasicTest } from "./test-wallet-backup-basic";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test runner.
|
* Test runner.
|
||||||
@ -107,6 +108,7 @@ const allTests: TestMainFunction[] = [
|
|||||||
runTimetravelAutorefreshTest,
|
runTimetravelAutorefreshTest,
|
||||||
runTimetravelWithdrawTest,
|
runTimetravelWithdrawTest,
|
||||||
runTippingTest,
|
runTippingTest,
|
||||||
|
runWalletBackupBasicTest,
|
||||||
runWallettestingTest,
|
runWallettestingTest,
|
||||||
runWithdrawalAbortBankTest,
|
runWithdrawalAbortBankTest,
|
||||||
runWithdrawalBankIntegratedTest,
|
runWithdrawalBankIntegratedTest,
|
||||||
|
@ -189,7 +189,7 @@ export interface WalletBackupContentV1 {
|
|||||||
/**
|
/**
|
||||||
* Clock when the purchase was deleted
|
* Clock when the purchase was deleted
|
||||||
*/
|
*/
|
||||||
clock_deleted: number;
|
clock_deleted: ClockStamp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Proposal ID identifying the purchase.
|
* Proposal ID identifying the purchase.
|
||||||
|
@ -1159,6 +1159,10 @@ export class Wallet {
|
|||||||
await runBackupCycle(this.ws);
|
await runBackupCycle(this.ws);
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
case "getBackupInfo": {
|
||||||
|
const resp = await getBackupInfo(this.ws);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
case "createDepositGroup": {
|
case "createDepositGroup": {
|
||||||
const req = codecForCreateDepositGroupRequest().decode(payload);
|
const req = codecForCreateDepositGroupRequest().decode(payload);
|
||||||
return await createDepositGroup(this.ws, req);
|
return await createDepositGroup(this.ws, req);
|
||||||
|
Loading…
Reference in New Issue
Block a user