gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] 01/02: harness: reusable test env


From: gnunet
Subject: [taler-wallet-core] 01/02: harness: reusable test env
Date: Wed, 23 Aug 2023 17:04:23 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-core.

commit 7fbe28e640d81f60b815ba123e30e97bc0747220
Author: Florian Dold <florian@dold.me>
AuthorDate: Wed Aug 23 16:04:16 2023 +0200

    harness: reusable test env
---
 packages/taler-harness/src/harness/harness.ts      |  68 ++++++++++---
 packages/taler-harness/src/harness/helpers.ts      | 107 ++++++++++++++++-----
 .../src/integrationtests/testrunner.ts             |   4 +-
 packages/taler-util/src/logging.ts                 |   4 +-
 4 files changed, 146 insertions(+), 37 deletions(-)

diff --git a/packages/taler-harness/src/harness/harness.ts 
b/packages/taler-harness/src/harness/harness.ts
index 4e2bae8f2..3a12024dc 100644
--- a/packages/taler-harness/src/harness/harness.ts
+++ b/packages/taler-harness/src/harness/harness.ts
@@ -489,6 +489,7 @@ export interface BankConfig {
   database: string;
   allowRegistrations: boolean;
   maxDebt?: string;
+  overrideTestDir?: string;
 }
 
 export interface FakeBankConfig {
@@ -534,6 +535,14 @@ function setCoin(config: Configuration, c: CoinConfig) {
   }
 }
 
+function backoffStart(): number {
+  return 10;
+}
+
+function backoffIncrement(n: number): number {
+  return Math.max(n * 2, 1000);
+}
+
 /**
  * Send an HTTP request until it succeeds or the process dies.
  */
@@ -545,6 +554,7 @@ export async function pingProc(
   if (!proc || proc.proc.exitCode !== null) {
     throw Error(`service process ${serviceName} not started, can't ping`);
   }
+  let nextDelay = backoffStart();
   while (true) {
     try {
       logger.trace(`pinging ${serviceName} at ${url}`);
@@ -554,7 +564,8 @@ export async function pingProc(
     } catch (e: any) {
       logger.warn(`service ${serviceName} not ready:`, e.toString());
       //console.log(e);
-      await delayMs(1000);
+      await delayMs(nextDelay);
+      nextDelay = backoffIncrement(nextDelay);
     }
     if (!proc || proc.proc.exitCode != null || proc.proc.signalCode != null) {
       throw Error(`service process ${serviceName} stopped unexpectedly`);
@@ -875,7 +886,7 @@ export class FakebankService
 
   /**
    * Create a new fakebank service handle.
-   * 
+   *
    * First generates the configuration for the fakebank and
    * then creates a fakebank handle, but doesn't start the fakebank
    * service yet.
@@ -885,19 +896,38 @@ export class FakebankService
     bc: BankConfig,
   ): Promise<FakebankService> {
     const config = new Configuration();
-    setTalerPaths(config, gc.testDir + "/talerhome");
+    const testDir = bc.overrideTestDir ?? gc.testDir;
+    setTalerPaths(config, testDir + "/talerhome");
     config.setString("taler", "currency", bc.currency);
     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", "ram_limit", `${1024}`);
-    const cfgFilename = gc.testDir + "/bank.conf";
+    const cfgFilename = testDir + "/bank.conf";
     config.write(cfgFilename);
 
     return new FakebankService(gc, bc, cfgFilename);
   }
 
+  static fromExistingConfig(
+    gc: GlobalTestState,
+    opts: { overridePath?: string },
+  ): FakebankService {
+    const testDir = opts.overridePath ?? gc.testDir;
+    const cfgFilename = testDir + `/bank.conf`;
+    const config = Configuration.load(cfgFilename);
+    const bc: BankConfig = {
+      allowRegistrations:
+        config.getYesNo("bank", "allow_registrations").orUndefined() ?? true,
+      currency: config.getString("taler", "currency").required(),
+      database: "none",
+      httpPort: config.getNumber("bank", "http_port").required(),
+      maxDebt: config.getString("bank", "max_debt").required(),
+    };
+    return new FakebankService(gc, bc, cfgFilename);
+  }
+
   setSuggestedExchange(e: ExchangeServiceInterface, exchangePayto: string) {
     if (!!this.proc) {
       throw Error("Can't set suggested exchange while bank is running.");
@@ -981,6 +1011,7 @@ export interface ExchangeConfig {
   roundUnit?: string;
   httpPort: number;
   database: string;
+  overrideTestDir?: string;
 }
 
 export interface ExchangeServiceInterface {
@@ -991,8 +1022,13 @@ export interface ExchangeServiceInterface {
 }
 
 export class ExchangeService implements ExchangeServiceInterface {
-  static fromExistingConfig(gc: GlobalTestState, exchangeName: string) {
-    const cfgFilename = gc.testDir + `/exchange-${exchangeName}.conf`;
+  static fromExistingConfig(
+    gc: GlobalTestState,
+    exchangeName: string,
+    opts: { overridePath?: string },
+  ) {
+    const testDir = opts.overridePath ?? gc.testDir;
+    const cfgFilename = testDir + `/exchange-${exchangeName}.conf`;
     const config = Configuration.load(cfgFilename);
     const ec: ExchangeConfig = {
       currency: config.getString("taler", "currency").required(),
@@ -1103,7 +1139,9 @@ export class ExchangeService implements 
ExchangeServiceInterface {
   }
 
   static create(gc: GlobalTestState, e: ExchangeConfig) {
+    const testDir = e.overrideTestDir ?? gc.testDir;
     const config = new Configuration();
+    setTalerPaths(config, testDir + "/talerhome");
     config.setString("taler", "currency", e.currency);
     // Required by the exchange but not really used yet.
     config.setString("exchange", "aml_threshold", `${e.currency}:1000000`);
@@ -1112,7 +1150,6 @@ export class ExchangeService implements 
ExchangeServiceInterface {
       "currency_round_unit",
       e.roundUnit ?? `${e.currency}:0.01`,
     );
-    setTalerPaths(config, gc.testDir + "/talerhome");
     config.setString(
       "exchange",
       "revocation_dir",
@@ -1149,7 +1186,7 @@ export class ExchangeService implements 
ExchangeServiceInterface {
 
     fs.writeFileSync(masterPrivFile, Buffer.from(exchangeMasterKey.eddsaPriv));
 
-    const cfgFilename = gc.testDir + `/exchange-${e.name}.conf`;
+    const cfgFilename = testDir + `/exchange-${e.name}.conf`;
     config.write(cfgFilename);
     return new ExchangeService(gc, e, cfgFilename, exchangeMasterKey);
   }
@@ -1553,6 +1590,7 @@ export interface MerchantConfig {
   currency: string;
   httpPort: number;
   database: string;
+  overrideTestDir?: string;
 }
 
 export interface PrivateOrderStatusQuery {
@@ -1798,8 +1836,13 @@ export interface CreateMerchantTippingReserveRequest {
 }
 
 export class MerchantService implements MerchantServiceInterface {
-  static fromExistingConfig(gc: GlobalTestState, name: string) {
-    const cfgFilename = gc.testDir + `/merchant-${name}.conf`;
+  static fromExistingConfig(
+    gc: GlobalTestState,
+    name: string,
+    opts: { overridePath?: string },
+  ) {
+    const testDir = opts.overridePath ?? gc.testDir;
+    const cfgFilename = testDir + `/merchant-${name}.conf`;
     const config = Configuration.load(cfgFilename);
     const mc: MerchantConfig = {
       currency: config.getString("taler", "currency").required(),
@@ -1894,11 +1937,12 @@ export class MerchantService implements 
MerchantServiceInterface {
     gc: GlobalTestState,
     mc: MerchantConfig,
   ): Promise<MerchantService> {
+    const testDir = mc.overrideTestDir ?? gc.testDir;
     const config = new Configuration();
     config.setString("taler", "currency", mc.currency);
 
-    const cfgFilename = gc.testDir + `/merchant-${mc.name}.conf`;
-    setTalerPaths(config, gc.testDir + "/talerhome");
+    const cfgFilename = testDir + `/merchant-${mc.name}.conf`;
+    setTalerPaths(config, testDir + "/talerhome");
     config.setString("merchant", "serve", "tcp");
     config.setString("merchant", "port", `${mc.httpPort}`);
     config.setString(
diff --git a/packages/taler-harness/src/harness/helpers.ts 
b/packages/taler-harness/src/harness/helpers.ts
index 9ad46e587..9004d4419 100644
--- a/packages/taler-harness/src/harness/helpers.ts
+++ b/packages/taler-harness/src/harness/helpers.ts
@@ -32,6 +32,7 @@ import {
   NotificationType,
   WalletNotification,
   TransactionMajorState,
+  Logger,
 } from "@gnu-taler/taler-util";
 import {
   BankAccessApi,
@@ -49,6 +50,7 @@ import {
   DbInfo,
   ExchangeService,
   ExchangeServiceInterface,
+  FakebankService,
   getPayto,
   GlobalTestState,
   MerchantPrivateApi,
@@ -62,6 +64,10 @@ import {
   WithAuthorization,
 } from "./harness.js";
 
+import * as fs from "fs";
+
+const logger = new Logger("helpers.ts");
+
 /**
  * @deprecated
  */
@@ -212,48 +218,103 @@ export async function createSimpleTestkudosEnvironment(
 export async function useSharedTestkudosEnvironment(t: GlobalTestState) {
   const coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => 
x("TESTKUDOS"));
 
+  // FIXME: We should probably have some file to indicate that
+  // the previous env setup finished successfully.
+
+  const sharedDir = `/tmp/taler-harness@${process.env.USER}`;
+
+  fs.mkdirSync(sharedDir, { recursive: true });
+
   const db = await setupSharedDb(t);
 
-  const bank = await BankService.create(t, {
-    allowRegistrations: true,
-    currency: "TESTKUDOS",
-    database: db.connStr,
-    httpPort: 8082,
-  });
+  let bank: FakebankService;
 
-  const exchange = ExchangeService.create(t, {
-    name: "testexchange-1",
-    currency: "TESTKUDOS",
-    httpPort: 8081,
-    database: db.connStr,
-  });
+  const prevSetupDone = fs.existsSync(sharedDir + "/setup-done");
 
-  const merchant = await MerchantService.create(t, {
-    name: "testmerchant-1",
-    currency: "TESTKUDOS",
-    httpPort: 8083,
-    database: db.connStr,
-  });
+  logger.info(`previous setup done: ${prevSetupDone}`);
+
+  if (fs.existsSync(sharedDir + "/bank.conf")) {
+    logger.info("reusing existing bank");
+    bank = BankService.fromExistingConfig(t, {
+      overridePath: sharedDir,
+    });
+  } else {
+    logger.info("creating new bank config");
+    bank = await BankService.create(t, {
+      allowRegistrations: true,
+      currency: "TESTKUDOS",
+      database: db.connStr,
+      httpPort: 8082,
+      overrideTestDir: sharedDir,
+    });
+  }
+
+  logger.info("setting up exchange");
+
+  const exchangeName = "testexchange-1";
+  const exchangeConfigFilename = sharedDir + `/exchange-${exchangeName}}`;
+
+  let exchange: ExchangeService;
+
+  if (fs.existsSync(exchangeConfigFilename)) {
+    exchange = ExchangeService.fromExistingConfig(t, exchangeName, {
+      overridePath: sharedDir,
+    });
+  } else {
+    exchange = ExchangeService.create(t, {
+      name: "testexchange-1",
+      currency: "TESTKUDOS",
+      httpPort: 8081,
+      database: db.connStr,
+      overrideTestDir: sharedDir,
+    });
+  }
+
+  logger.info("setting up exchange");
+
+  let merchant: MerchantService;
+  const merchantName = "testmerchant-1";
+  const merchantConfigFilename = sharedDir + `/merchant-${merchantName}}`;
+
+  if (fs.existsSync(merchantConfigFilename)) {
+    merchant = MerchantService.fromExistingConfig(t, merchantName, {
+      overridePath: sharedDir,
+    });
+  } else {
+    merchant = await MerchantService.create(t, {
+      name: "testmerchant-1",
+      currency: "TESTKUDOS",
+      httpPort: 8083,
+      database: db.connStr,
+      overrideTestDir: sharedDir,
+    });
+  }
+
+  logger.info("creating bank account for exchange");
 
   const exchangeBankAccount = await bank.createExchangeAccount(
     "myexchange",
     "x",
   );
+
+  logger.info("creating exchange bank account");
   await exchange.addBankAccount("1", exchangeBankAccount);
 
   bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri);
 
+  exchange.addCoinConfigList(coinConfig);
+
+  merchant.addExchange(exchange);
+
+  logger.info("basic setup done, starting services");
+
   await bank.start();
 
   await bank.pingUntilAvailable();
 
-  exchange.addCoinConfigList(coinConfig);
-
   await exchange.start();
   await exchange.pingUntilAvailable();
 
-  merchant.addExchange(exchange);
-
   await merchant.start();
   await merchant.pingUntilAvailable();
 
@@ -282,6 +343,8 @@ export async function useSharedTestkudosEnvironment(t: 
GlobalTestState) {
 
   console.log("setup done!");
 
+  fs.writeFileSync(sharedDir + "/setup-done", "OK");
+
   return {
     commonDb: db,
     exchange,
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts 
b/packages/taler-harness/src/integrationtests/testrunner.ts
index a9b6d1304..c1b06f21e 100644
--- a/packages/taler-harness/src/integrationtests/testrunner.ts
+++ b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -14,7 +14,7 @@
  GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
  */
 
-import { CancellationToken, Logger, minimatch } from "@gnu-taler/taler-util";
+import { CancellationToken, Logger, minimatch, setGlobalLogLevelFromString } 
from "@gnu-taler/taler-util";
 import * as child_process from "child_process";
 import * as fs from "fs";
 import * as os from "os";
@@ -494,6 +494,8 @@ if (runTestInstrStr && 
process.argv.includes("__TWCLI_TESTWORKER")) {
     runTestInstrStr,
   ) as RunTestChildInstruction;
 
+  setGlobalLogLevelFromString("TRACE");
+
   process.on("disconnect", () => {
     logger.trace("got disconnect from parent");
     process.exit(3);
diff --git a/packages/taler-util/src/logging.ts 
b/packages/taler-util/src/logging.ts
index c4b2a3da0..b14274560 100644
--- a/packages/taler-util/src/logging.ts
+++ b/packages/taler-util/src/logging.ts
@@ -32,13 +32,13 @@ export enum LogLevel {
   None = "none",
 }
 
-export let globalLogLevel = LogLevel.Info;
+let globalLogLevel = LogLevel.Info;
+const byTagLogLevel: Record<string, LogLevel> = {};
 
 export function setGlobalLogLevelFromString(logLevelStr: string): void {
   globalLogLevel = getLevelForString(logLevelStr);
 }
 
-export const byTagLogLevel: Record<string, LogLevel> = {};
 export function setLogLevelFromString(tag: string, logLevelStr: string): void {
   byTagLogLevel[tag] = getLevelForString(logLevelStr);
 }

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]