aboutsummaryrefslogtreecommitdiff
path: root/packages/taler-harness/src/index.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-harness/src/index.ts')
-rw-r--r--packages/taler-harness/src/index.ts240
1 files changed, 234 insertions, 6 deletions
diff --git a/packages/taler-harness/src/index.ts b/packages/taler-harness/src/index.ts
index 82d8e4326..f06a66a21 100644
--- a/packages/taler-harness/src/index.ts
+++ b/packages/taler-harness/src/index.ts
@@ -28,9 +28,17 @@ import {
MerchantApiClient,
rsaBlind,
setGlobalLogLevelFromString,
+ RegisterAccountRequest,
+ HttpStatusCode,
+ MerchantInstanceConfig,
+ Duration,
+ generateIban,
} from "@gnu-taler/taler-util";
import { clk } from "@gnu-taler/taler-util/clk";
-import { createPlatformHttpLib } from "@gnu-taler/taler-util/http";
+import {
+ HttpResponse,
+ createPlatformHttpLib,
+} from "@gnu-taler/taler-util/http";
import {
CryptoDispatcher,
downloadExchangeInfo,
@@ -46,7 +54,11 @@ import { runBench2 } from "./bench2.js";
import { runBench3 } from "./bench3.js";
import { runEnvFull } from "./env-full.js";
import { runEnv1 } from "./env1.js";
-import { GlobalTestState, runTestWithState } from "./harness/harness.js";
+import {
+ GlobalTestState,
+ delayMs,
+ runTestWithState,
+} from "./harness/harness.js";
import { getTestInfo, runTests } from "./integrationtests/testrunner.js";
import { lintExchangeDeployment } from "./lint.js";
@@ -312,8 +324,7 @@ deploymentCli
const exchangeInfo = await downloadExchangeInfo(exchangeBaseUrl, http);
await topupReserveWithDemobank({
amount: "KUDOS:10",
- corebankApiBaseUrl:
- "https://bank.demo.taler.net/",
+ corebankApiBaseUrl: "https://bank.demo.taler.net/",
exchangeInfo,
http,
reservePub: reserveKeyPair.pub,
@@ -341,8 +352,7 @@ deploymentCli
const exchangeInfo = await downloadExchangeInfo(exchangeBaseUrl, http);
await topupReserveWithDemobank({
amount: "TESTKUDOS:10",
- corebankApiBaseUrl:
- "https://bank.test.taler.net/",
+ corebankApiBaseUrl: "https://bank.test.taler.net/",
exchangeInfo,
http,
reservePub: reserveKeyPair.pub,
@@ -423,6 +433,224 @@ deploymentCli
});
deploymentCli
+ .subcommand("waitService", "wait-taler-service", {
+ help: "Wait for the config endpoint of a Taler-style service to be available",
+ })
+ .requiredArgument("serviceName", clk.STRING)
+ .requiredArgument("serviceConfigUrl", clk.STRING)
+ .action(async (args) => {
+ const serviceName = args.waitService.serviceName;
+ const serviceUrl = args.waitService.serviceConfigUrl;
+ console.log(
+ `Waiting for service ${serviceName} to be ready at ${serviceUrl}`,
+ );
+ const httpLib = createPlatformHttpLib();
+ while (1) {
+ console.log(`Fetching ${serviceUrl}`);
+ let resp: HttpResponse;
+ try {
+ resp = await httpLib.fetch(serviceUrl);
+ } catch (e) {
+ console.log(
+ `Got network error for service ${serviceName} at ${serviceUrl}`,
+ );
+ await delayMs(1000);
+ continue;
+ }
+ if (resp.status != 200) {
+ console.log(
+ `Got unexpected status ${resp.status} for service at ${serviceUrl}`,
+ );
+ await delayMs(1000);
+ continue;
+ }
+ let respJson: any;
+ try {
+ respJson = await resp.json();
+ } catch (e) {
+ console.log(
+ `Got json error for service ${serviceName} at ${serviceUrl}`,
+ );
+ await delayMs(1000);
+ continue;
+ }
+ const recServiceName = respJson.name;
+ console.log(`Got name ${recServiceName}`);
+ if (recServiceName != serviceName) {
+ console.log(`A different service is still running at ${serviceUrl}`);
+ await delayMs(1000);
+ continue;
+ }
+ console.log(`service ${serviceName} at ${serviceUrl} is now available`);
+ return;
+ }
+ });
+
+deploymentCli
+ .subcommand("waitEndpoint", "wait-endpoint", {
+ help: "Wait for an endpoint to return an HTTP 200 Ok status with JSON body",
+ })
+ .requiredArgument("serviceEndpoint", clk.STRING)
+ .action(async (args) => {
+ const serviceUrl = args.waitEndpoint.serviceEndpoint;
+ console.log(`Waiting for endpoint ${serviceUrl} to be ready`);
+ const httpLib = createPlatformHttpLib();
+ while (1) {
+ console.log(`Fetching ${serviceUrl}`);
+ let resp: HttpResponse;
+ try {
+ resp = await httpLib.fetch(serviceUrl);
+ } catch (e) {
+ console.log(`Got network error for service at ${serviceUrl}`);
+ await delayMs(1000);
+ continue;
+ }
+ if (resp.status != 200) {
+ console.log(
+ `Got unexpected status ${resp.status} for service at ${serviceUrl}`,
+ );
+ await delayMs(1000);
+ continue;
+ }
+ let respJson: any;
+ try {
+ respJson = await resp.json();
+ } catch (e) {
+ console.log(`Got json error for service at ${serviceUrl}`);
+ await delayMs(1000);
+ continue;
+ }
+ return;
+ }
+ });
+
+deploymentCli
+ .subcommand("genIban", "gen-iban", {
+ help: "Generate a random IBAN.",
+ })
+ .requiredArgument("countryCode", clk.STRING)
+ .requiredArgument("length", clk.INT)
+ .action(async (args) => {
+ console.log(generateIban(args.genIban.countryCode, args.genIban.length));
+ });
+
+deploymentCli
+ .subcommand("provisionMerchantInstance", "provision-merchant-instance", {
+ help: "Provision a merchant backend instance.",
+ })
+ .requiredArgument("merchantApiBaseUrl", clk.STRING)
+ .requiredOption("managementToken", ["--management-token"], clk.STRING)
+ .requiredOption("instanceToken", ["--instance-token"], clk.STRING)
+ .requiredOption("name", ["--name"], clk.STRING)
+ .requiredOption("id", ["--id"], clk.STRING)
+ .requiredOption("payto", ["--payto"], clk.STRING)
+ .action(async (args) => {
+ const httpLib = createPlatformHttpLib();
+ const baseUrl = args.provisionMerchantInstance.merchantApiBaseUrl;
+ const managementToken = args.provisionMerchantInstance.managementToken;
+ const instanceToken = args.provisionMerchantInstance.instanceToken;
+ const instanceId = args.provisionMerchantInstance.id;
+ const body: MerchantInstanceConfig = {
+ address: {},
+ auth: {
+ method: "token",
+ token: args.provisionMerchantInstance.instanceToken,
+ },
+ default_pay_delay: Duration.toTalerProtocolDuration(
+ Duration.fromSpec({ hours: 1 }),
+ ),
+ default_wire_transfer_delay: { d_us: 1 },
+ id: instanceId,
+ jurisdiction: {},
+ name: args.provisionMerchantInstance.name,
+ use_stefan: true,
+ };
+ const url = new URL("management/instances", baseUrl);
+ const createResp = await httpLib.fetch(url.href, {
+ method: "POST",
+ body,
+ headers: {
+ Authorization: `Bearer ${managementToken}`,
+ },
+ });
+ if (createResp.status >= 200 && createResp.status <= 299) {
+ logger.info(`instance ${instanceId} created successfully`);
+ } else if (createResp.status === HttpStatusCode.Conflict) {
+ logger.info(`instance ${instanceId} already exists`);
+ } else {
+ logger.error(
+ `unable to create instance ${instanceId}, HTTP status ${createResp.status}`,
+ );
+ }
+
+ const accountsUrl = new URL(
+ `instances/${instanceId}/private/accounts`,
+ baseUrl,
+ );
+ const accountBody = {
+ payto_uri: args.provisionMerchantInstance.payto,
+ };
+ const createAccountResp = await httpLib.fetch(accountsUrl.href, {
+ method: "POST",
+ body: accountBody,
+ headers: {
+ Authorization: `Bearer ${instanceToken}`,
+ },
+ });
+ if (createAccountResp.status != 200) {
+ console.error(
+ `unable to configure bank account for instance ${instanceId}, status ${createAccountResp.status}`,
+ );
+ const resp = await createAccountResp.json();
+ console.error(j2s(resp));
+ process.exit(2);
+ }
+ logger.info(`successfully configured bank account for ${instanceId}`);
+ });
+
+deploymentCli
+ .subcommand("provisionBankAccount", "provision-bank-account", {
+ help: "Provision a corebank account.",
+ })
+ .requiredArgument("corebankApiBaseUrl", clk.STRING)
+ .flag("exchange", ["--exchange"])
+ .flag("public", ["--public"])
+ .requiredOption("login", ["--login"], clk.STRING)
+ .requiredOption("name", ["--name"], clk.STRING)
+ .requiredOption("password", ["--password"], clk.STRING)
+ .maybeOption("internalPayto", ["--payto"], clk.STRING)
+ .action(async (args) => {
+ const httpLib = createPlatformHttpLib();
+ const corebankApiBaseUrl = args.provisionBankAccount.corebankApiBaseUrl;
+ const url = new URL("accounts", corebankApiBaseUrl);
+ const accountLogin = args.provisionBankAccount.login;
+ const body: RegisterAccountRequest = {
+ name: args.provisionBankAccount.name,
+ password: args.provisionBankAccount.password,
+ username: accountLogin,
+ is_public: !!args.provisionBankAccount.public,
+ is_taler_exchange: !!args.provisionBankAccount.exchange,
+ internal_payto_uri: args.provisionBankAccount.internalPayto,
+ };
+ const resp = await httpLib.fetch(url.href, {
+ method: "POST",
+ body,
+ });
+ if (resp.status >= 200 && resp.status <= 299) {
+ logger.info(`account ${accountLogin} successfully provisioned`);
+ return;
+ }
+ if (resp.status === HttpStatusCode.Conflict) {
+ logger.info(`account ${accountLogin} already provisioned`);
+ return;
+ }
+ logger.error(
+ `unable to provision bank account, HTTP response status ${resp.status}`,
+ );
+ process.exit(2);
+ });
+
+deploymentCli
.subcommand("coincfg", "gen-coin-config", {
help: "Generate a coin/denomination configuration for the exchange.",
})