commit e70d28b93941fc22c3298fcce965b42b2311683e
parent b119d5c17108b9e97be8c06ceccf622139048f6f
Author: Florian Dold <florian@dold.me>
Date: Fri, 4 Oct 2024 14:41:31 +0200
harness: log test steps
Diffstat:
5 files changed, 264 insertions(+), 234 deletions(-)
diff --git a/packages/taler-harness/src/harness/environments.ts b/packages/taler-harness/src/harness/environments.ts
@@ -27,6 +27,7 @@ import {
AmlDecisionRequest,
AmlDecisionRequestWithoutSignature,
AmountString,
+ Configuration,
ConfirmPayResultType,
decodeCrock,
Duration,
@@ -45,7 +46,11 @@ import {
TransactionMajorState,
WalletNotification,
} from "@gnu-taler/taler-util";
-import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
+import {
+ createSyncCryptoApi,
+ EddsaKeyPairStrings,
+ WalletApiOperation,
+} from "@gnu-taler/taler-wallet-core";
import { CoinConfig, defaultCoinConfig } from "./denomStructures.js";
import {
FaultInjectedExchangeService,
@@ -1092,3 +1097,172 @@ export async function postAmlDecision(
t.assertDeepEqual(resp.status, HttpStatusCode.NoContent);
}
+
+export interface KycEnvOptions {
+ coinConfig?: CoinConfig[];
+
+ adjustExchangeConfig?(config: Configuration): void;
+}
+
+export interface KycTestEnv {
+ commonDb: DbInfo;
+ bankClient: TalerCorebankApiClient;
+ exchange: ExchangeService;
+ exchangeBankAccount: HarnessExchangeBankAccount;
+ walletClient: WalletClient;
+ walletService: WalletService;
+ amlKeypair: EddsaKeyPairStrings;
+ merchant: MerchantService;
+}
+
+export async function createKycTestkudosEnvironment(
+ t: GlobalTestState,
+ opts: KycEnvOptions = {},
+): Promise<KycTestEnv> {
+ const db = await setupDb(t);
+
+ let coinConfig: CoinConfig[];
+ if (opts.coinConfig) {
+ coinConfig = opts.coinConfig;
+ } else {
+ coinConfig = defaultCoinConfig.map((x) => x("TESTKUDOS"));
+ }
+
+ const bank = await BankService.create(t, {
+ allowRegistrations: true,
+ currency: "TESTKUDOS",
+ database: db.connStr,
+ httpPort: 8082,
+ });
+
+ const exchange = ExchangeService.create(t, {
+ name: "testexchange-1",
+ currency: "TESTKUDOS",
+ httpPort: 8081,
+ database: db.connStr,
+ });
+
+ let receiverName = "Exchange";
+ let exchangeBankUsername = "exchange";
+ let exchangeBankPassword = "mypw";
+ let exchangePaytoUri = getTestHarnessPaytoForLabel(exchangeBankUsername);
+
+ const wireGatewayApiBaseUrl = new URL(
+ `accounts/${exchangeBankUsername}/taler-wire-gateway/`,
+ bank.corebankApiBaseUrl,
+ ).href;
+
+ await exchange.addBankAccount("1", {
+ accountName: exchangeBankUsername,
+ accountPassword: exchangeBankPassword,
+ wireGatewayApiBaseUrl,
+ accountPaytoUri: exchangePaytoUri,
+ });
+
+ bank.setSuggestedExchange(exchange, exchangePaytoUri);
+
+ await bank.start();
+
+ await bank.pingUntilAvailable();
+
+ const bankClient = new TalerCorebankApiClient(bank.corebankApiBaseUrl, {
+ auth: {
+ username: "admin",
+ password: "adminpw",
+ },
+ });
+
+ await bankClient.registerAccountExtended({
+ name: receiverName,
+ password: exchangeBankPassword,
+ username: exchangeBankUsername,
+ is_taler_exchange: true,
+ payto_uri: exchangePaytoUri,
+ });
+
+ exchange.addCoinConfigList(coinConfig);
+
+ const adjustExchangeConfig = opts.adjustExchangeConfig;
+ if (adjustExchangeConfig) {
+ await exchange.modifyConfig(async (config) => {
+ adjustExchangeConfig(config);
+ });
+ }
+
+ await exchange.start();
+
+ const cryptoApi = createSyncCryptoApi();
+ const amlKeypair = await cryptoApi.createEddsaKeypair({});
+
+ await exchange.enableAmlAccount(amlKeypair.pub, "Alice");
+
+ const walletService = new WalletService(t, {
+ name: "wallet",
+ useInMemoryDb: true,
+ });
+ await walletService.start();
+ await walletService.pingUntilAvailable();
+
+ const walletClient = new WalletClient({
+ name: "wallet",
+ unixPath: walletService.socketPath,
+ onNotification(n) {
+ console.log("wallet-core notification", n);
+ },
+ });
+ await walletClient.connect();
+ await walletClient.client.call(WalletApiOperation.InitWallet, {
+ config: {
+ testing: {
+ skipDefaults: true,
+ },
+ },
+ });
+
+ const merchant = await MerchantService.create(t, {
+ name: "testmerchant-1",
+ currency: "TESTKUDOS",
+ httpPort: 8083,
+ database: db.connStr,
+ });
+
+ merchant.addExchange(exchange);
+
+ await merchant.start();
+
+ await merchant.addInstanceWithWireAccount({
+ id: "default",
+ name: "Default Instance",
+ paytoUris: [getTestHarnessPaytoForLabel("merchant-default")],
+ defaultWireTransferDelay: Duration.toTalerProtocolDuration(
+ Duration.fromSpec({ minutes: 1 }),
+ ),
+ });
+
+ await merchant.addInstanceWithWireAccount({
+ id: "minst1",
+ name: "minst1",
+ paytoUris: [getTestHarnessPaytoForLabel("minst1")],
+ defaultWireTransferDelay: Duration.toTalerProtocolDuration(
+ Duration.fromSpec({ minutes: 1 }),
+ ),
+ });
+
+ t.logStep("env-setup-done");
+
+ return {
+ commonDb: db,
+ exchange,
+ amlKeypair,
+ walletClient,
+ walletService,
+ bankClient,
+ exchangeBankAccount: {
+ accountName: exchangeBankUsername,
+ accountPassword: exchangeBankPassword,
+ accountPaytoUri: exchangePaytoUri,
+ wireGatewayApiBaseUrl,
+ },
+ merchant,
+ };
+}
diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts
@@ -277,12 +277,33 @@ export class GlobalTestState {
servers: http.Server[];
inShutdown: boolean = false;
stepSet: Set<string> = new Set();
+
+ spanStack: string[] = [];
+
constructor(params: GlobalTestParams) {
this.testDir = params.testDir;
this.procs = [];
this.servers = [];
}
+ async runSpanAsync(
+ spanName: string,
+ block: () => Promise<void>,
+ ): Promise<void> {
+ if (this.stepSet.has(spanName)) {
+ throw Error(`duplicate step (${spanName})`);
+ }
+ let steps = `${this.testDir}/steps.txt`;
+ fs.appendFileSync(steps, `START ${spanName}\n`);
+ try {
+ await block();
+ } catch (e) {
+ fs.appendFileSync(steps, `FAIL ${(e as any).message}\n`);
+ throw e;
+ }
+ fs.appendFileSync(steps, `END ${spanName}\n`);
+ }
+
async assertThrowsTalerErrorAsync(
block: () => Promise<void>,
): Promise<TalerError> {
@@ -310,9 +331,13 @@ export class GlobalTestState {
);
}
- assertTrue(b: boolean): asserts b {
+ assertTrue(b: boolean, message?: string): asserts b {
if (!b) {
- throw Error("test assertion failed");
+ if (message) {
+ throw Error(`test assertion failed: ${message}`);
+ } else {
+ throw Error("test assertion failed");
+ }
}
}
@@ -420,15 +445,17 @@ export class GlobalTestState {
/**
* Log that the test arrived a certain step.
*
- * The step name should be unique across the whole
+ * The step name should be unique across the whole test run.
*/
logStep(stepName: string): void {
- // Now we just log, later we may report the steps that were done
- // to easily see where the test hangs.
- console.info(`STEP: ${stepName}`);
if (this.stepSet.has(stepName)) {
throw Error(`duplicate step (${stepName})`);
}
+ // Now we just log, later we may report the steps that were done
+ // to easily see where the test hangs.
+ console.info(`STEP: ${stepName}`);
+ let steps = `${this.testDir}/steps.txt`;
+ fs.appendFileSync(steps, `STEP ${stepName}\n`);
}
}
@@ -1781,7 +1808,8 @@ export class MerchantService implements MerchantServiceInterface {
}
/**
- * Start the merchant,
+ * Start the merchant.
+ * Waits for the service to become fully available.
*/
async start(opts: { skipDbinit?: boolean } = {}): Promise<void> {
const skipSetup = opts.skipDbinit ?? false;
@@ -2049,6 +2077,8 @@ export async function runTestWithState(
} else {
console.error("FATAL: test failed with exception", e);
}
+ let steps = `${gc.testDir}/steps.txt`;
+ fs.appendFileSync(steps, `FAIL ${(e as any).message}\n`);
status = "fail";
} finally {
await gc.shutdown();
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-deposit-deposit-kyctransfer.ts b/packages/taler-harness/src/integrationtests/test-kyc-deposit-deposit-kyctransfer.ts
@@ -207,8 +207,8 @@ async function createKycTestkudosEnvironment(
walletService,
bankClient,
exchangeBankAccount: {
- accountName: "",
- accountPassword: "",
+ accountName: exchangeBankUsername,
+ accountPassword: exchangeBankPassword,
accountPaytoUri: exchangePaytoUri,
wireGatewayApiBaseUrl,
},
diff --git a/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit.ts b/packages/taler-harness/src/integrationtests/test-kyc-merchant-deposit.ts
@@ -1,6 +1,6 @@
/*
This file is part of GNU Taler
- (C) 2020 Taler Systems S.A.
+ (C) 2024 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
@@ -21,13 +21,11 @@ import {
codecForAccountKycRedirects,
codecForKycProcessClientInformation,
codecForQueryInstancesResponse,
- Duration,
encodeCrock,
hashPaytoUri,
j2s,
Logger,
MerchantAccountKycRedirectsResponse,
- TalerCorebankApiClient,
TalerMerchantApi,
WireGatewayApiClient,
} from "@gnu-taler/taler-util";
@@ -36,229 +34,14 @@ import {
readSuccessResponseJsonOrThrow,
} from "@gnu-taler/taler-util/http";
import {
- createSyncCryptoApi,
- EddsaKeyPairStrings,
- WalletApiOperation,
-} from "@gnu-taler/taler-wallet-core";
-import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js";
-import {
- BankService,
- DbInfo,
- ExchangeService,
- getTestHarnessPaytoForLabel,
- GlobalTestState,
- HarnessExchangeBankAccount,
- harnessHttpLib,
- MerchantService,
- setupDb,
- WalletClient,
- WalletService,
-} from "../harness/harness.js";
-import {
- EnvOptions,
+ createKycTestkudosEnvironment,
postAmlDecisionNoRules,
withdrawViaBankV3,
} from "../harness/environments.js";
+import { GlobalTestState, harnessHttpLib } from "../harness/harness.js";
const logger = new Logger(`test-kyc-merchant-deposit.ts`);
-interface KycTestEnv {
- commonDb: DbInfo;
- bankClient: TalerCorebankApiClient;
- exchange: ExchangeService;
- exchangeBankAccount: HarnessExchangeBankAccount;
- walletClient: WalletClient;
- walletService: WalletService;
- amlKeypair: EddsaKeyPairStrings;
- merchant: MerchantService;
-}
-
-async function createKycTestkudosEnvironment(
- t: GlobalTestState,
- coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")),
- opts: EnvOptions = {},
-): Promise<KycTestEnv> {
- const db = await setupDb(t);
-
- const bank = await BankService.create(t, {
- allowRegistrations: true,
- currency: "TESTKUDOS",
- database: db.connStr,
- httpPort: 8082,
- });
-
- const exchange = ExchangeService.create(t, {
- name: "testexchange-1",
- currency: "TESTKUDOS",
- httpPort: 8081,
- database: db.connStr,
- });
-
- let receiverName = "Exchange";
- let exchangeBankUsername = "exchange";
- let exchangeBankPassword = "mypw";
- let exchangePaytoUri = getTestHarnessPaytoForLabel(exchangeBankUsername);
-
- const wireGatewayApiBaseUrl = new URL(
- `accounts/${exchangeBankUsername}/taler-wire-gateway/`,
- bank.corebankApiBaseUrl,
- ).href;
-
- await exchange.addBankAccount("1", {
- accountName: exchangeBankUsername,
- accountPassword: exchangeBankPassword,
- wireGatewayApiBaseUrl,
- accountPaytoUri: exchangePaytoUri,
- });
-
- bank.setSuggestedExchange(exchange, exchangePaytoUri);
-
- await bank.start();
-
- const bankClient = new TalerCorebankApiClient(bank.corebankApiBaseUrl, {
- auth: {
- username: "admin",
- password: "adminpw",
- },
- });
-
- await bankClient.registerAccountExtended({
- name: receiverName,
- password: exchangeBankPassword,
- username: exchangeBankUsername,
- is_taler_exchange: true,
- payto_uri: exchangePaytoUri,
- });
-
- const ageMaskSpec = opts.ageMaskSpec;
-
- if (ageMaskSpec) {
- exchange.enableAgeRestrictions(ageMaskSpec);
- // Enable age restriction for all coins.
- exchange.addCoinConfigList(
- coinConfig.map((x) => ({
- ...x,
- name: `${x.name}-age`,
- ageRestricted: true,
- })),
- );
- // For mixed age restrictions, we also offer coins without age restrictions
- if (opts.mixedAgeRestriction) {
- exchange.addCoinConfigList(
- coinConfig.map((x) => ({ ...x, ageRestricted: false })),
- );
- }
- } else {
- exchange.addCoinConfigList(coinConfig);
- }
-
- await exchange.modifyConfig(async (config) => {
- config.setString("exchange", "enable_kyc", "yes");
-
- config.setString("KYC-RULE-R1", "operation_type", "deposit");
- config.setString("KYC-RULE-R1", "enabled", "yes");
- config.setString("KYC-RULE-R1", "exposed", "yes");
- config.setString("KYC-RULE-R1", "is_and_combinator", "yes");
- config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:0");
- config.setString("KYC-RULE-R1", "timeframe", "1d");
- config.setString("KYC-RULE-R1", "next_measures", "M1");
-
- config.setString("KYC-MEASURE-M1", "check_name", "C1");
- config.setString("KYC-MEASURE-M1", "context", "{}");
- config.setString("KYC-MEASURE-M1", "program", "P1");
-
- config.setString("AML-PROGRAM-P1", "command", "/bin/true");
- config.setString("AML-PROGRAM-P1", "enabled", "true");
- config.setString("AML-PROGRAM-P1", "description", "this does nothing");
- config.setString("AML-PROGRAM-P1", "fallback", "M1");
-
- config.setString("KYC-CHECK-C1", "type", "INFO");
- config.setString("KYC-CHECK-C1", "description", "my check!");
- config.setString("KYC-CHECK-C1", "fallback", "M1");
- });
-
- await exchange.start();
-
- const cryptoApi = createSyncCryptoApi();
- const amlKeypair = await cryptoApi.createEddsaKeypair({});
-
- await exchange.enableAmlAccount(amlKeypair.pub, "Alice");
-
- const walletService = new WalletService(t, {
- name: "wallet",
- useInMemoryDb: true,
- });
- await walletService.start();
- await walletService.pingUntilAvailable();
-
- const walletClient = new WalletClient({
- name: "wallet",
- unixPath: walletService.socketPath,
- onNotification(n) {
- console.log("got notification", n);
- },
- });
- await walletClient.connect();
- await walletClient.client.call(WalletApiOperation.InitWallet, {
- config: {
- testing: {
- skipDefaults: true,
- },
- },
- });
-
- const merchant = await MerchantService.create(t, {
- name: "testmerchant-1",
- currency: "TESTKUDOS",
- httpPort: 8083,
- database: db.connStr,
- });
-
- merchant.addExchange(exchange);
-
- if (opts.additionalMerchantConfig) {
- opts.additionalMerchantConfig(merchant);
- }
- await merchant.start();
- await merchant.pingUntilAvailable();
-
- await merchant.addInstanceWithWireAccount({
- id: "default",
- name: "Default Instance",
- paytoUris: [getTestHarnessPaytoForLabel("merchant-default")],
- defaultWireTransferDelay: Duration.toTalerProtocolDuration(
- Duration.fromSpec({ minutes: 1 }),
- ),
- });
-
- await merchant.addInstanceWithWireAccount({
- id: "minst1",
- name: "minst1",
- paytoUris: [getTestHarnessPaytoForLabel("minst1")],
- defaultWireTransferDelay: Duration.toTalerProtocolDuration(
- Duration.fromSpec({ minutes: 1 }),
- ),
- });
-
- console.log("setup done!");
-
- return {
- commonDb: db,
- exchange,
- amlKeypair,
- walletClient,
- walletService,
- bankClient,
- merchant,
- exchangeBankAccount: {
- accountName: "",
- accountPassword: "",
- accountPaytoUri: "",
- wireGatewayApiBaseUrl,
- },
- };
-}
-
export async function runKycMerchantDepositTest(t: GlobalTestState) {
// Set up test environment
@@ -269,7 +52,32 @@ export async function runKycMerchantDepositTest(t: GlobalTestState) {
exchange,
exchangeBankAccount,
amlKeypair,
- } = await createKycTestkudosEnvironment(t);
+ } = await createKycTestkudosEnvironment(t, {
+ adjustExchangeConfig(config) {
+ config.setString("exchange", "enable_kyc", "yes");
+
+ config.setString("KYC-RULE-R1", "operation_type", "deposit");
+ config.setString("KYC-RULE-R1", "enabled", "yes");
+ config.setString("KYC-RULE-R1", "exposed", "yes");
+ config.setString("KYC-RULE-R1", "is_and_combinator", "yes");
+ config.setString("KYC-RULE-R1", "threshold", "TESTKUDOS:0");
+ config.setString("KYC-RULE-R1", "timeframe", "1d");
+ config.setString("KYC-RULE-R1", "next_measures", "M1");
+
+ config.setString("KYC-MEASURE-M1", "check_name", "C1");
+ config.setString("KYC-MEASURE-M1", "context", "{}");
+ config.setString("KYC-MEASURE-M1", "program", "P1");
+
+ config.setString("AML-PROGRAM-P1", "command", "/bin/true");
+ config.setString("AML-PROGRAM-P1", "enabled", "true");
+ config.setString("AML-PROGRAM-P1", "description", "this does nothing");
+ config.setString("AML-PROGRAM-P1", "fallback", "M1");
+
+ config.setString("KYC-CHECK-C1", "type", "INFO");
+ config.setString("KYC-CHECK-C1", "description", "my check!");
+ config.setString("KYC-CHECK-C1", "fallback", "M1");
+ },
+ });
let accountPub: string;
@@ -328,8 +136,15 @@ export async function runKycMerchantDepositTest(t: GlobalTestState) {
logger.info(`mechant kyc status: ${j2s(kycRespOne)}`);
+ t.assertDeepEqual(kycRespOne.kyc_data[0].exchange_http_status, 404);
+
+ t.assertTrue(
+ (kycRespOne.kyc_data[0].limits?.length ?? 0) > 0,
+ "kyc status should contain non-empty limits",
+ );
+
// Order creation should fail!
- {
+ await t.runSpanAsync("order creation should be rejected", async () => {
let url = new URL("private/orders", merchant.makeInstanceBaseUrl());
const order = {
summary: "Test",
@@ -344,7 +159,8 @@ export async function runKycMerchantDepositTest(t: GlobalTestState) {
});
logger.info(`order creation status: ${resp.status}`);
- }
+ t.assertTrue(resp.status !== 200);
+ });
await wireGatewayApiClient.adminAddKycauth({
amount: "TESTKUDOS:0.1",
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -21,13 +21,13 @@ import * as fs from "fs";
import * as os from "os";
import * as path from "path";
import url from "url";
+import { getSharedTestDir } from "../harness/environments.js";
import {
GlobalTestState,
TestRunResult,
runTestWithState,
shouldLingerInTest,
} from "../harness/harness.js";
-import { getSharedTestDir } from "../harness/environments.js";
import { runAccountRestrictionsTest } from "./test-account-restrictions.js";
import { runAgeRestrictionsDepositTest } from "./test-age-restrictions-deposit.js";
import { runAgeRestrictionsMerchantTest } from "./test-age-restrictions-merchant.js";
@@ -526,6 +526,16 @@ export async function runTests(spec: TestRunSpec) {
harnessLogStream.close();
+ const stepsFile = `${testDir}/steps.txt`;
+ if (spec.verbosity > 0 && fs.existsSync(stepsFile)) {
+ let stepsLog = fs.readFileSync(stepsFile, {
+ encoding: "utf-8",
+ });
+ console.log("-- test steps --");
+ console.log(stepsLog.trim());
+ console.log("-- end --");
+ }
+
console.log(`parent: got result ${JSON.stringify(result)}`);
testResults.push(result);