taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit d199cbb5795a294dbba7fc533afc2d51bab7586d
parent eea9e522e24a4fe606f1dd67d14c81560d42b639
Author: Florian Dold <florian@dold.me>
Date:   Fri, 17 Jul 2026 23:51:46 +0200

harness: reproduce error on upper-cased instance creation

Diffstat:
Apackages/taler-harness/src/integrationtests/test-merchant-self-provision-casing.ts | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpackages/taler-harness/src/integrationtests/testrunner.ts | 2++
2 files changed, 215 insertions(+), 0 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-merchant-self-provision-casing.ts b/packages/taler-harness/src/integrationtests/test-merchant-self-provision-casing.ts @@ -0,0 +1,213 @@ +/* + 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, + Logger, + LoginTokenScope, + MerchantAuthMethod, + succeedOrThrow, + TalerMerchantInstanceHttpClient, + TalerMerchantManagementHttpClient, + TanChannel, +} from "@gnu-taler/taler-util"; +import { createSimpleTestkudosEnvironmentV3 } from "../harness/environments.js"; +import { GlobalTestState } from "../harness/harness.js"; +import { + configureTestMerchantMfa, + makeMfaConfigEmailSms, + solveMFA, + wait2FaCode, +} from "../harness/tan-helper.js"; + +export const logger = new Logger("test-merchant-self-provision-activation.ts"); + +/** + * Regression test for bad case normalization in the + * self onboarding. + */ +export async function runMerchantSelfProvisionCasingTest( + t: GlobalTestState, +) { + // Set up test environment + + const instanceInfo = { + id: "MYUPPERCASEINSTANCE", + name: "My awesome 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 mfaConfig = makeMfaConfigEmailSms( + t, + instanceInfo.email, + instanceInfo.phone_number, + ); + + const { merchant, merchantAdminAccessToken } = + await createSimpleTestkudosEnvironmentV3(t, undefined, { + additionalMerchantConfig(m) { + m.modifyConfig(async (cfg) => { + cfg.setString("merchant", "ENABLE_SELF_PROVISIONING", "yes"); + configureTestMerchantMfa(cfg, mfaConfig); + }); + }, + }); + + const merchantClient = new TalerMerchantManagementHttpClient( + merchant.makeInstanceBaseUrl(), + ); + + { + const r = succeedOrThrow( + await merchantClient.listInstances(merchantAdminAccessToken), + ); + t.assertDeepEqual(r.instances.length, 2); + } + + 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]; + + // FIXME: check the order + // always first emails since is cheaper + t.assertTrue(firstChallenge.tan_channel === TanChannel.EMAIL); + t.assertTrue(secondChallenge.tan_channel === TanChannel.SMS); + + { + // 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 res = await wait2FaCode(mfaConfig.email.path); + console.log(`MFA message: ${res.message}`); + const loginMatch = res.message.match(/^Login: (.*)$/m); + const login = loginMatch?.[1]; + console.log(`login account: ${login}`); + t.assertDeepEqual(res.address, instanceInfo.email); + succeedOrThrow( + await merchantClient.confirmChallenge(firstChallenge.challenge_id, { + tan: res.code, + }), + ); + } + + { + succeedOrThrow( + await merchantClient.sendChallenge(secondChallenge.challenge_id), + ); + const res = await wait2FaCode(mfaConfig.sms.path); + t.assertDeepEqual(res.address, instanceInfo.phone_number); + succeedOrThrow( + await merchantClient.confirmChallenge(secondChallenge.challenge_id, { + tan: res.code, + }), + ); + } + + 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(t, merchantClient, loginChallenge, mfaConfig); + + 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 det = succeedOrThrow( + await instanceApi.getCurrentInstanceDetails(token), + ); + + // check that the instance has the new email + t.assertDeepEqual(det.email, instanceInfo.email); + t.assertDeepEqual(det.email_validated, true); + t.assertDeepEqual(det.phone_number, instanceInfo.phone_number); + t.assertDeepEqual(det.phone_validated, true); +} + +runMerchantSelfProvisionCasingTest.suites = ["merchant", "self-provision"]; diff --git a/packages/taler-harness/src/integrationtests/testrunner.ts b/packages/taler-harness/src/integrationtests/testrunner.ts @@ -122,6 +122,7 @@ import { runMerchantRefundFeesTest } from "./test-merchant-refund-fees.js"; import { runMerchantReportsTest } from "./test-merchant-reports.js"; import { runMerchantSelfProvisionActivationTwoBankAccountsTest } from "./test-merchant-self-provision-activation-two-bank-account.js"; import { runMerchantSelfProvisionActivationTest } from "./test-merchant-self-provision-activation.js"; +import { runMerchantSelfProvisionCasingTest } from "./test-merchant-self-provision-casing.js"; import { runMerchantSelfProvisionForgotPasswordTest } from "./test-merchant-self-provision-forgot-password.js"; import { runMerchantSelfProvisionInactiveAccountPermissionsTest } from "./test-merchant-self-provision-inactive-account-permissions.js"; import { runMerchantSpecPublicOrdersTest } from "./test-merchant-spec-public-orders.js"; @@ -442,6 +443,7 @@ const allTests: TestMainFunction[] = [ runTopsNexusBasicTest, runTopsNexusSwtTest, runTopsMerchantSwtKycauthTest, + runMerchantSelfProvisionCasingTest, ]; export interface TestRunSpec {