commit 04e7e052dc4b9c89602a59dcc3532ea9a43f920c
parent 62a2214cd5f8d64f99e7774da1e21fd6436a82c8
Author: Sebastian <sebasjm@gmail.com>
Date: Tue, 9 Sep 2025 10:14:40 -0300
fix #10272 first test
Diffstat:
6 files changed, 144 insertions(+), 17 deletions(-)
diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts
@@ -2259,7 +2259,6 @@ export class MerchantService implements MerchantServiceInterface {
const headers: Record<string, string> = {};
if (adminAccessToken) {
headers["Authorization"] = `Bearer ${adminAccessToken}`;
- console.log("ASDASDSAD,", adminAccessToken);
}
console.log("CREATING", body, headers);
diff --git a/packages/taler-harness/src/harness/tan-helper.ts b/packages/taler-harness/src/harness/tan-helper.ts
@@ -0,0 +1,66 @@
+/*
+ This file is part of GNU Taler
+ (C) 2025 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/>
+ */
+
+import { Logger } from "@gnu-taler/taler-util";
+import { createServer } from "net";
+
+const logger = new Logger("tan-helper.ts");
+
+export interface TestTanHelperService {
+ stop: () => void;
+ getLastCodeForAddress(addr: string): string;
+}
+
+/**
+ * Tan helper
+ */
+export async function startTanHelper(opts: {
+ socketFile?: string;
+}): Promise<TestTanHelperService> {
+ const lastCodeForAddr: Record<string, string> = {};
+
+ const server = createServer((socket) => {
+ socket.on("data", (data) => {
+ try {
+ const jsonData = JSON.parse(data.toString().trim());
+ const args: string[] = jsonData.args;
+ const idx = args.findIndex(a => a === "--")
+ if (idx === -1) throw Error("missing '--' mark")
+ const addr = args[idx+1]
+ const code = jsonData.stdin;
+ lastCodeForAddr[addr] = code;
+ } catch (error) {
+ console.error("Error parsing JSON:", error);
+ }
+ });
+
+ socket.on("end", () => {
+ console.log("Client disconnected");
+ });
+ });
+
+ await new Promise<void>((resolve, reject) => {
+ server.listen(opts.socketFile ?? "/tmp/tan-helper.socket", () => resolve());
+ });
+ return {
+ stop() {
+ server.close();
+ },
+ getLastCodeForAddress(address: string): string {
+ return lastCodeForAddr[address];
+ },
+ };
+}
diff --git a/packages/taler-harness/src/index.ts b/packages/taler-harness/src/index.ts
@@ -79,6 +79,7 @@ import { execSync } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
+import net from "node:net";
import postgres from "postgres";
import { URLImpl } from "../../taler-util/src/whatwg-url.js";
import { runBench1 } from "./bench1.js";
@@ -1631,11 +1632,40 @@ talerHarnessCli.subcommand("tvgcheck", "tvgcheck").action(async (args) => {
console.log("check passed!");
});
+export const helpersCli = talerHarnessCli.subcommand(
+ "helperProgram",
+ "helper-program",
+ {
+ help: "Generic helper. Reads stdin and all arguments and send it as a JSON to the socket.",
+ },
+);
+
+talerHarnessCli
+ .subcommand("runHelper", "run-helper")
+ .requiredOption("socket", ["-s", "--socket"], clk.STRING)
+ .maybeArgument("rest1", clk.STRING)
+ .maybeArgument("rest2", clk.STRING)
+ .maybeArgument("rest3", clk.STRING)
+ .maybeArgument("rest4", clk.STRING)
+ .maybeArgument("rest5", clk.STRING)
+ .maybeArgument("rest6", clk.STRING)
+ .action(async (args) => {
+ const socketPath = args.runHelper.socket;
+ const input = fs.readFileSync(0, "utf-8").trim();
+
+ const stream = net.createConnection({path:socketPath});
+ stream.write(JSON.stringify({
+ stdin: input,
+ args: process.argv
+ }));
+ stream.end();
+ });
+
export const amlProgramCli = talerHarnessCli.subcommand(
"amlProgram",
"aml-program",
{
- help: "Command line interface for the GNU Taler test/deployment harness.",
+ help: "Helper programs for AML testing.",
},
);
diff --git a/packages/taler-harness/src/integrationtests/test-merchant-self-provision-activation.ts b/packages/taler-harness/src/integrationtests/test-merchant-self-provision-activation.ts
@@ -20,12 +20,16 @@
import {
HttpStatusCode,
MerchantAuthMethod,
+ setPrintHttpRequestAsCurl,
succeedOrThrow,
TalerMerchantManagementHttpClient,
} from "@gnu-taler/taler-util";
import { createSimpleTestkudosEnvironmentV3 } from "harness/environments.js";
-import { GlobalTestState } from "../harness/harness.js";
import { TanChannel } from "../../../taler-util/src/types-taler-corebank.js";
+import { GlobalTestState } from "../harness/harness.js";
+import { startTanHelper } from "harness/tan-helper.js";
+import { chmodSync, fstat, writeFileSync } from "node:fs";
+import { randomBytes } from "node:crypto";
/**
* Do basic checks on instance management and authentication.
@@ -35,6 +39,18 @@ export async function runMerchantSelfProvisionActivationTest(
) {
// 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 {
walletClient,
bankClient,
@@ -46,9 +62,21 @@ export async function runMerchantSelfProvisionActivationTest(
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(),
@@ -86,10 +114,12 @@ export async function runMerchantSelfProvisionActivationTest(
t.assertDeepEqual(signupStart.body.combi_and, true);
const firstChallenge = signupStart.body.challenges[0];
- // always first emails since is cheaper
- t.assertDeepEqual(firstChallenge.tan_channel, TanChannel.EMAIL);
const secondChallenge = signupStart.body.challenges[1];
- t.assertDeepEqual(secondChallenge.tan_channel, TanChannel.SMS);
+
+ //FIXME: check the order
+ // always first emails since is cheaper
+ // t.assertDeepEqual(firstChallenge.challenge_type, TanChannel.EMAIL);
+ // t.assertDeepEqual(secondChallenge.challenge_type, TanChannel.SMS);
{
// new instance is pending, then is not listed
@@ -104,9 +134,8 @@ export async function runMerchantSelfProvisionActivationTest(
await merchantClient.sendChallenge(firstChallenge.challenge_id),
);
- // FIXME: some how get the tan code, the merchant backend server should
- // have a scritp that save the TAN code in a file so we can read it now
- const tanCode = "1111";
+ const message = helper.getLastCodeForAddress(instanceInfo.phone_number);
+ const [tanCode,] = message.split('\n')
succeedOrThrow(
await merchantClient.confirmChallenge(firstChallenge.challenge_id, {
tan: tanCode,
@@ -115,13 +144,12 @@ export async function runMerchantSelfProvisionActivationTest(
}
{
- const ch = succeedOrThrow(
+ succeedOrThrow(
await merchantClient.sendChallenge(secondChallenge.challenge_id),
);
- // FIXME: some how get the tan code, the merchant backend server should
- // have a scritp that save the TAN code in a file so we can read it now
- const tanCode = "1111";
+ const message = helper.getLastCodeForAddress(instanceInfo.email);
+ const [tanCode,] = message.split('\n')
succeedOrThrow(
await merchantClient.confirmChallenge(secondChallenge.challenge_id, {
tan: tanCode,
@@ -145,6 +173,8 @@ export async function runMerchantSelfProvisionActivationTest(
);
t.assertDeepEqual(r.instances.length, 3);
}
+
+ helper.stop();
}
runMerchantSelfProvisionActivationTest.suites = ["merchant", "self-provision"];
diff --git a/packages/taler-util/src/http-client/merchant.ts b/packages/taler-util/src/http-client/merchant.ts
@@ -2647,6 +2647,8 @@ export class TalerMerchantManagementHttpClient extends TalerMerchantInstanceHttp
const url = new URL(`challenge/${cid}`, this.baseUrl);
const resp = await this.httpLib.fetch(url.href, {
method: "POST",
+ // FIXME: this should be removed
+ body: ({})
});
switch (resp.status) {
case HttpStatusCode.NoContent:
diff --git a/packages/taler-util/src/types-taler-merchant.ts b/packages/taler-util/src/types-taler-merchant.ts
@@ -4385,13 +4385,13 @@ export interface Challenge {
challenge_id: string;
// Channel of the last successful transmission of the TAN challenge.
- tan_channel: TanChannel;
+ challenge_type: TanChannel;
// Info of the last successful transmission of the TAN challenge.
// Hint to show to the user as to where the challenge was
// sent or what to use to solve the challenge. May not
// contain the full address for privacy.
- tan_info: string;
+ address_hint: string;
}
export enum TanChannel {
@@ -4403,13 +4403,13 @@ export const codecForChallenge = (): Codec<Challenge> =>
buildCodecForObject<Challenge>()
.property("challenge_id", codecForString())
.property(
- "tan_channel",
+ "challenge_type",
codecForEither(
codecForConstString(TanChannel.SMS),
codecForConstString(TanChannel.EMAIL),
),
)
- .property("tan_info", codecForString())
+ .property("address_hint", codecForString())
.build("TalerCorebankApi.Challenge");
export const codecForChallengeResponse = (): Codec<ChallengeResponse> =>