commit 8a7ef910bfb3438cf8ab5ed130c7327faea2e869
parent f775b5cd8da7fb4cdcf03de9df77b0f894059f30
Author: Sebastian <sebasjm@taler-systems.com>
Date: Mon, 9 Mar 2026 17:09:04 -0300
#11215 add integration test
Diffstat:
2 files changed, 228 insertions(+), 0 deletions(-)
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-self-provision-activation-two-bank-account.ts b/packages/taler-harness/src/integrationtests/test-merchant-self-provision-activation-two-bank-account.ts
@@ -0,0 +1,226 @@
+/*
+ This file is part of GNU Taler
+ (C) 2021 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
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Imports.
+ */
+import {
+ alternativeOrThrow,
+ Duration,
+ HttpStatusCode,
+ LoginTokenScope,
+ MerchantAuthMethod,
+ succeedOrThrow,
+ TalerMerchantInstanceHttpClient,
+ TalerMerchantManagementHttpClient,
+ TanChannel,
+} from "@gnu-taler/taler-util";
+import { createSimpleTestkudosEnvironmentV3 } from "harness/environments.js";
+import { startTanHelper } from "harness/tan-helper.js";
+import { randomBytes } from "node:crypto";
+import { chmodSync, writeFileSync } from "node:fs";
+import { getTestHarnessPaytoForLabel, GlobalTestState } from "../harness/harness.js";
+import { solveMFA } from "./test-merchant-self-provision-inactive-account-permissions.js";
+
+/**
+ * Do basic checks on instance management and authentication.
+ */
+export async function runMerchantSelfProvisionActivationTwoBankAccountsTest(
+ t: GlobalTestState,
+) {
+ // Set up test environment
+
+ // FIXME: maybe merchant can use commands?
+ const RND = randomBytes(10).toString("hex");
+ const socketFile = `${t.testDir}/tan-helper-${RND}.socket`;
+ const helperScript = `${t.testDir}/harness-helper-${RND}.sh`;
+ writeFileSync(
+ helperScript,
+ `#!/bin/bash
+taler-harness run-helper --socket ${socketFile} -- $@
+`,
+ );
+ chmodSync(helperScript, "777");
+
+ const {
+ merchant,
+ merchantAdminAccessToken,
+ } = await createSimpleTestkudosEnvironmentV3(t, undefined, {
+ additionalMerchantConfig(m) {
+ m.modifyConfig(async (cfg) => {
+ cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes");
+ cfg.setString("merchant", "HELPER_SMS", helperScript);
+ cfg.setString("merchant", "HELPER_EMAIL", helperScript);
+ cfg.setString("merchant", "MANDATORY_TAN_CHANNELS", "email sms");
+ });
+ },
+ });
+ const helper = await startTanHelper({ socketFile });
+
+ const merchantClient = new TalerMerchantManagementHttpClient(
+ merchant.makeInstanceBaseUrl(),
+ );
+
+ {
+ const r = succeedOrThrow(
+ await merchantClient.listInstances(merchantAdminAccessToken),
+ );
+ t.assertDeepEqual(r.instances.length, 2);
+ }
+
+ const instanceInfo = {
+ id: "self-instance",
+ name: "My instance",
+ auth: {
+ method: MerchantAuthMethod.TOKEN,
+ password: "123",
+ },
+ default_pay_delay: Duration.toTalerProtocolDuration(
+ Duration.fromSpec({ days: 14 }),
+ ),
+ default_wire_transfer_delay: Duration.toTalerProtocolDuration(
+ Duration.fromSpec({ days: 14 }),
+ ),
+ jurisdiction: {},
+ address: {},
+ email: "some@taler.net",
+ phone_number: "+1111",
+ use_stefan: false,
+ };
+ const signupStart = alternativeOrThrow(
+ await merchantClient.createInstanceSelfProvision(instanceInfo),
+ HttpStatusCode.Accepted,
+ );
+
+ // creation requires 2fa
+ t.assertDeepEqual(signupStart.challenges.length, 2);
+ t.assertDeepEqual(signupStart.combi_and, true);
+
+ const firstChallenge = signupStart.challenges[0];
+ const secondChallenge = signupStart.challenges[1];
+
+ {
+ // new instance is pending, then is not listed
+ const r = succeedOrThrow(
+ await merchantClient.listInstances(merchantAdminAccessToken),
+ );
+ t.assertDeepEqual(r.instances.length, 2);
+ }
+
+ {
+ succeedOrThrow(
+ await merchantClient.sendChallenge(firstChallenge.challenge_id),
+ );
+
+ const message = helper.getLastCodeForAddress(instanceInfo.phone_number);
+ const [tanCode] = message.split("\n");
+ succeedOrThrow(
+ await merchantClient.confirmChallenge(firstChallenge.challenge_id, {
+ tan: tanCode,
+ }),
+ );
+ }
+
+ {
+ succeedOrThrow(
+ await merchantClient.sendChallenge(secondChallenge.challenge_id),
+ );
+
+ const message = helper.getLastCodeForAddress(instanceInfo.email);
+ const [tanCode] = message.split("\n");
+ succeedOrThrow(
+ await merchantClient.confirmChallenge(secondChallenge.challenge_id, {
+ tan: tanCode,
+ }),
+ );
+ }
+
+ const completeSignup = await merchantClient.createInstanceSelfProvision(
+ instanceInfo,
+ {
+ challengeIds: [firstChallenge.challenge_id, secondChallenge.challenge_id],
+ },
+ );
+
+ t.assertDeepEqual(completeSignup.type, "ok");
+
+ const instanceApi = new TalerMerchantInstanceHttpClient(
+ merchantClient.getSubInstanceAPI(instanceInfo.id),
+ merchantClient.httpLib,
+ );
+
+ {
+ // new instance is completed, now it should be visible
+ const r = succeedOrThrow(
+ await merchantClient.listInstances(merchantAdminAccessToken),
+ );
+ t.assertDeepEqual(r.instances.length, 3);
+ }
+
+ const loginChallenge = alternativeOrThrow(
+ await instanceApi.createAccessToken(
+ instanceInfo.id,
+ instanceInfo.auth.password,
+ {
+ scope: LoginTokenScope.All,
+ },
+ ),
+ HttpStatusCode.Accepted,
+ );
+
+ await solveMFA(merchantClient, helper, loginChallenge, {
+ [TanChannel.EMAIL]: instanceInfo.email,
+ [TanChannel.SMS]: instanceInfo.phone_number,
+ });
+
+ const { access_token: token } = succeedOrThrow(
+ await instanceApi.createAccessToken(
+ instanceInfo.id,
+ instanceInfo.auth.password,
+ {
+ scope: LoginTokenScope.All,
+ },
+ {
+ challengeIds: loginChallenge.challenges.map((c) => c.challenge_id),
+ },
+ ),
+ );
+
+ const bankAccount = succeedOrThrow(await instanceApi.addBankAccount(token, {
+ payto_uri: getTestHarnessPaytoForLabel("account1")
+ }))
+
+ const secondBankAccountChallenge = alternativeOrThrow(await instanceApi.addBankAccount(token, {
+ payto_uri: getTestHarnessPaytoForLabel("account2")
+ }), HttpStatusCode.Accepted)
+
+ await solveMFA(instanceApi, helper, secondBankAccountChallenge, {
+ [TanChannel.EMAIL]: instanceInfo.email,
+ [TanChannel.SMS]: instanceInfo.phone_number,
+ });
+
+ const secondBankAccount = succeedOrThrow(await instanceApi.addBankAccount(token, {
+ payto_uri: getTestHarnessPaytoForLabel("account2")
+ }, {
+ challengeIds: secondBankAccountChallenge.challenges.map((c) => c.challenge_id)
+ }))
+
+
+
+ helper.stop();
+}
+
+runMerchantSelfProvisionActivationTwoBankAccountsTest.suites = ["merchant", "self-provision"];
diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts
@@ -223,6 +223,7 @@ import { runWithdrawalHugeTest } from "./test-withdrawal-huge.js";
import { runWithdrawalIdempotentTest } from "./test-withdrawal-idempotent.js";
import { runWithdrawalManualTest } from "./test-withdrawal-manual.js";
import { runWithdrawalPrepareTest } from "./test-withdrawal-prepare.js";
+import { runMerchantSelfProvisionActivationTwoBankAccountsTest } from "./test-merchant-self-provision-activation-two-bank-account.js";
/**
* Test runner.
@@ -341,6 +342,7 @@ const allTests: TestMainFunction[] = [
runExchangeMasterPubChangeTest,
runMerchantCategoriesTest,
runMerchantSelfProvisionActivationTest,
+ runMerchantSelfProvisionActivationTwoBankAccountsTest,
runWebMerchantLoginTest,
runMerchantSelfProvisionActivationAndLoginTest,
runMerchantSelfProvisionForgotPasswordTest,