aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-harness/src/harness/harness.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-harness/src/harness/harness.ts')
-rw-r--r--packages/taler-harness/src/harness/harness.ts166
1 files changed, 119 insertions, 47 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",