diff options
Diffstat (limited to 'packages/taler-harness/src/harness')
-rw-r--r-- | packages/taler-harness/src/harness/harness.ts | 166 | ||||
-rw-r--r-- | packages/taler-harness/src/harness/helpers.ts | 4 | ||||
-rw-r--r-- | packages/taler-harness/src/harness/libeufin-apis.ts | 3 | ||||
-rw-r--r-- | packages/taler-harness/src/harness/libeufin.ts | 2 |
4 files changed, 124 insertions, 51 deletions
diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts index b6e80cfb7..0ee0d7960 100644 --- a/packages/taler-harness/src/harness/harness.ts +++ b/packages/taler-harness/src/harness/harness.ts @@ -102,6 +102,21 @@ interface WaitResult { signal: NodeJS.Signals | null; } +class CommandError extends Error { + constructor( + public message: string, + public logName: string, + public command: string, + public args: string[], + public env: Env, + public code: number | null, + ) { + super(message); + } +} +interface Env { + [index: string]: string | undefined; +} /** * Run a shell command, return stdout. */ @@ -109,15 +124,15 @@ export async function sh( t: GlobalTestState, logName: string, command: string, - env: { [index: string]: string | undefined } = process.env, + env: Env = process.env, ): Promise<string> { - logger.info(`running command ${command}`); + logger.trace(`running command ${command}`); return new Promise((resolve, reject) => { const stdoutChunks: Buffer[] = []; const proc = spawn(command, { stdio: ["inherit", "pipe", "pipe"], shell: true, - env: env, + env, }); proc.stdout.on("data", (x) => { if (x instanceof Buffer) { @@ -132,16 +147,34 @@ export async function sh( }); proc.stderr.pipe(stderrLog); proc.on("exit", (code, signal) => { - logger.info(`child process exited (${code} / ${signal})`); + logger.info(`child process ${logName} exited (${code} / ${signal})`); if (code != 0) { - reject(Error(`Unexpected exit code ${code} for '${command}'`)); + reject( + new CommandError( + `Unexpected exit code ${code}`, + logName, + command, + [], + env, + code, + ), + ); return; } const b = Buffer.concat(stdoutChunks).toString("utf-8"); resolve(b); }); - proc.on("error", () => { - reject(Error("Child process had error")); + proc.on("error", (err) => { + reject( + new CommandError( + "Child process had error:" + err.message, + logName, + command, + [], + env, + null, + ), + ); }); }); } @@ -170,6 +203,7 @@ export async function runCommand( env: { [index: string]: string | undefined } = process.env, ): Promise<string> { logger.info(`running command ${shellescape([command, ...args])}`); + return new Promise((resolve, reject) => { const stdoutChunks: Buffer[] = []; const proc = spawn(command, args, { @@ -190,16 +224,34 @@ export async function runCommand( }); proc.stderr.pipe(stderrLog); proc.on("exit", (code, signal) => { - logger.info(`child process exited (${code} / ${signal})`); + logger.trace(`child process exited (${code} / ${signal})`); if (code != 0) { - reject(Error(`Unexpected exit code ${code} for '${command}'`)); + reject( + new CommandError( + `Unexpected exit code ${code}`, + logName, + command, + [], + env, + code, + ), + ); return; } const b = Buffer.concat(stdoutChunks).toString("utf-8"); resolve(b); }); - proc.on("error", () => { - reject(Error("Child process had error")); + proc.on("error", (err) => { + reject( + new CommandError( + "Child process had error:" + err.message, + logName, + command, + [], + env, + null, + ), + ); }); }); } @@ -321,14 +373,14 @@ export class GlobalTestState { logName: string, env: { [index: string]: string | undefined } = process.env, ): ProcessWrapper { - logger.info( + logger.trace( `spawning process (${logName}): ${shellescape([command, ...args])}`, ); const proc = spawn(command, args, { stdio: ["inherit", "pipe", "pipe"], env: env, }); - logger.info(`spawned process (${logName}) with pid ${proc.pid}`); + logger.trace(`spawned process (${logName}) with pid ${proc.pid}`); proc.on("error", (err) => { logger.warn(`could not start process (${command})`, err); }); @@ -355,18 +407,18 @@ export class GlobalTestState { return; } if (shouldLingerInTest()) { - logger.info("refusing to shut down, lingering was requested"); + logger.trace("refusing to shut down, lingering was requested"); return; } this.inShutdown = true; - logger.info("shutting down"); + logger.trace("shutting down"); for (const s of this.servers) { s.close(); s.removeAllListeners(); } for (const p of this.procs) { if (p.proc.exitCode == null) { - logger.info(`killing process ${p.proc.pid}`); + logger.trace(`killing process ${p.proc.pid}`); p.proc.kill("SIGTERM"); await p.wait(); } @@ -473,12 +525,12 @@ export async function pingProc( } while (true) { try { - logger.info(`pinging ${serviceName} at ${url}`); + logger.trace(`pinging ${serviceName} at ${url}`); const resp = await axios.get(url); - logger.info(`service ${serviceName} available`); + logger.trace(`service ${serviceName} available`); return; } catch (e: any) { - logger.info(`service ${serviceName} not ready:`, e.toString()); + logger.warn(`service ${serviceName} not ready:`, e.toString()); //console.log(e); await delayMs(1000); } @@ -506,7 +558,10 @@ class LibEuFinBankService extends BankServiceBase implements BankServiceHandle { sandboxProc: ProcessWrapper | undefined; nexusProc: ProcessWrapper | undefined; - http = createPlatformHttpLib(); + http = createPlatformHttpLib({ + allowHttp: true, + enableThrottling: false, + }); static async create( gc: GlobalTestState, @@ -539,12 +594,6 @@ class LibEuFinBankService extends BankServiceBase implements BankServiceHandle { return url.href; } - // FIXME: Duplicate? Where is this needed? - get baseUrlAccessApi(): string { - let url = new URL("access-api/", this.baseUrlDemobank); - return url.href; - } - get bankAccessApiBaseUrl(): string { let url = new URL("access-api/", this.baseUrlDemobank); return url.href; @@ -555,7 +604,7 @@ class LibEuFinBankService extends BankServiceBase implements BankServiceHandle { } get baseUrl(): string { - return this.baseUrlAccessApi; + return this.bankAccessApiBaseUrl; } async setSuggestedExchange( @@ -587,7 +636,7 @@ class LibEuFinBankService extends BankServiceBase implements BankServiceHandle { await this.start(); await this.pingUntilAvailable(); await LibeufinSandboxApi.createDemobankAccount(accountName, password, { - baseUrl: this.baseUrlAccessApi, + baseUrl: this.bankAccessApiBaseUrl, }); let bankAccountLabel = accountName; await LibeufinSandboxApi.createDemobankEbicsSubscriber( @@ -697,7 +746,7 @@ class LibEuFinBankService extends BankServiceBase implements BankServiceHandle { let accountInfoResp = await LibeufinSandboxApi.demobankAccountInfo( "admin", "secret", - { baseUrl: this.baseUrlAccessApi }, + { baseUrl: this.bankAccessApiBaseUrl }, accountName, // bank account label. ); return { @@ -793,7 +842,7 @@ export class FakebankService { proc: ProcessWrapper | undefined; - http = createPlatformHttpLib(); + http = createPlatformHttpLib({ allowHttp: true, enableThrottling: false }); // We store "created" accounts during setup and // register them after startup. @@ -1282,7 +1331,9 @@ export class ExchangeService implements ExchangeServiceInterface { } } - logger.info("configuring bank accounts", accounts); + const accountsDescription = accounts.map((acc) => ` * ${acc}`).join("\n"); + logger.info("configuring bank accounts:"); + logger.info(accountsDescription); for (const acc of accounts) { await runCommand( @@ -1494,7 +1545,7 @@ export class MerchantApiClient { ) {} // FIXME: Migrate everything to this in favor of axios - http = createPlatformHttpLib(); + http = createPlatformHttpLib({ allowHttp: true, enableThrottling: false }); async changeAuth(auth: MerchantAuthConfiguration): Promise<void> { const url = new URL("private/auth", this.baseUrl); @@ -2052,6 +2103,7 @@ export async function runTestWithState( try { logger.info("running test in directory", gc.testDir); await Promise.race([testMain(gc), p.promise]); + logger.info("completed test in directory", gc.testDir); status = "pass"; if (linger) { const rl = readline.createInterface({ @@ -2068,9 +2120,22 @@ export async function runTestWithState( rl.close(); } } catch (e) { - console.error("FATAL: test failed with exception", e); - if (e instanceof TalerError) { - console.error(`error detail: ${j2s(e.errorDetail)}`); + if (e instanceof CommandError) { + console.error("FATAL: test failed for", e.logName); + const errorLog = fs.readFileSync( + path.join(gc.testDir, `${e.logName}-stderr.log`), + ); + console.error(`${e.message}: "${e.command}"`); + console.error(errorLog.toString()); + console.error(e); + } else if (e instanceof TalerError) { + console.error( + "FATAL: test failed", + e.message, + `error detail: ${j2s(e.errorDetail)}`, + ); + } else { + console.error("FATAL: test failed with exception", e); } status = "fail"; } finally { @@ -2243,23 +2308,28 @@ export class WalletCli { const cryptoWorkerArg = cliOpts.cryptoWorkerType ? `--crypto-worker=${cliOpts.cryptoWorkerType}` : ""; - const resp = await sh( - self.globalTestState, - `wallet-${self.name}`, - `taler-wallet-cli ${ - self.timetravelArg ?? "" - } ${cryptoWorkerArg} --no-throttle -LTRACE --skip-defaults --wallet-db '${ - self.dbfile - }' api '${op}' ${shellWrap(JSON.stringify(payload))}`, - ); + const logName = `wallet-${self.name}`; + const command = `taler-wallet-cli ${ + self.timetravelArg ?? "" + } ${cryptoWorkerArg} --allow-http --no-throttle -LTRACE --skip-defaults --wallet-db '${ + self.dbfile + }' api '${op}' ${shellWrap(JSON.stringify(payload))}`; + const resp = await sh(self.globalTestState, logName, command); logger.info("--- wallet core response ---"); logger.info(resp); logger.info("--- end of response ---"); - let ar: any; + let ar: CoreApiResponse; try { - ar = JSON.parse(resp) as CoreApiResponse; + ar = JSON.parse(resp); } catch (e) { - throw new Error("wallet CLI did not return a proper JSON response"); + throw new CommandError( + "wallet CLI did not return a proper JSON response", + logName, + command, + [], + {}, + null, + ); } if (ar.type === "error") { throw TalerError.fromUncheckedDetail(ar.error); @@ -2295,6 +2365,7 @@ export class WalletCli { `wallet-${this.name}`, "taler-wallet-cli", [ + "--allow-http", "--no-throttle", ...this.timetravelArgArr, "-LTRACE", @@ -2313,6 +2384,7 @@ export class WalletCli { `wallet-${this.name}`, "taler-wallet-cli", [ + "--allow-http", "--no-throttle", "--skip-defaults", "-LTRACE", diff --git a/packages/taler-harness/src/harness/helpers.ts b/packages/taler-harness/src/harness/helpers.ts index 516312ed8..b13fa9cf4 100644 --- a/packages/taler-harness/src/harness/helpers.ts +++ b/packages/taler-harness/src/harness/helpers.ts @@ -99,7 +99,7 @@ export interface EnvOptions { /** * Run a test case with a simple TESTKUDOS Taler environment, consisting * of one exchange, one bank and one merchant. - * + * * @deprecated use {@link createSimpleTestkudosEnvironmentV2} instead */ export async function createSimpleTestkudosEnvironment( @@ -557,7 +557,7 @@ export async function withdrawViaBankV2( /** * Withdraw balance. - * + * * @deprecated use {@link withdrawViaBankV2 instead} */ export async function withdrawViaBank( diff --git a/packages/taler-harness/src/harness/libeufin-apis.ts b/packages/taler-harness/src/harness/libeufin-apis.ts index 4ef588fb5..cb9acdaa4 100644 --- a/packages/taler-harness/src/harness/libeufin-apis.ts +++ b/packages/taler-harness/src/harness/libeufin-apis.ts @@ -5,10 +5,9 @@ * the services get actually started and managed. */ +import { URL } from "@gnu-taler/taler-util"; import axiosImp from "axios"; const axios = axiosImp.default; -import { AmountString, Logger, URL } from "@gnu-taler/taler-util"; -import { createPlatformHttpLib } from "@gnu-taler/taler-util/http"; export interface LibeufinSandboxServiceInterface { baseUrl: string; diff --git a/packages/taler-harness/src/harness/libeufin.ts b/packages/taler-harness/src/harness/libeufin.ts index 001fb613b..8fd276fad 100644 --- a/packages/taler-harness/src/harness/libeufin.ts +++ b/packages/taler-harness/src/harness/libeufin.ts @@ -899,6 +899,8 @@ export function findNexusPayment( ): LibeufinNexusMoneyMovement | void { let transactions = payments["transactions"]; for (let i = 0; i < transactions.length; i++) { + //FIXME: last line won't compile with the current definition of the type + //@ts-ignore let batches = transactions[i]["camtData"]["batches"]; for (let y = 0; y < batches.length; y++) { let movements = batches[y]["batchTransactions"]; |