taler-typescript-core

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

commit 73b5edde6b404c2e3e770eb3ad3f041485321c01
parent a2fe54a89260d0047f63640708062356c76f805e
Author: Sebastian <sebasjm@taler-systems.com>
Date:   Tue, 21 Apr 2026 11:56:52 -0300

fix harness: use the taler-util api instead of custom http request

Diffstat:
Mpackages/taler-harness/src/integrationtests/test-kyc-decisions.ts | 57++++++++++++++++++++++++++++++---------------------------
Mpackages/taler-harness/src/integrationtests/test-kyc-fail-recover-simple.ts | 152+++++++++++++++++++++++++++++++++----------------------------------------------
Mpackages/taler-harness/src/integrationtests/test-kyc-two-forms.ts | 174++++++++++++++++++++++++++++++++++---------------------------------------------
Mpackages/taler-util/src/http-client/exchange-client.ts | 2++
4 files changed, 169 insertions(+), 216 deletions(-)

diff --git a/packages/taler-harness/src/integrationtests/test-kyc-decisions.ts b/packages/taler-harness/src/integrationtests/test-kyc-decisions.ts @@ -26,8 +26,10 @@ import { encodeCrock, getRandomBytes, hashNormalizedPaytoUri, + HttpStatusCode, j2s, LimitOperationType, + Result, succeedOrThrow, TalerProtocolDuration, TalerProtocolTimestamp, @@ -78,6 +80,7 @@ export async function runKycDecisionsTest(t: GlobalTestState) { walletClient, bankClient, bank, + exchangeApi, exchange, amlKeypair, exchangeBankAccount, @@ -192,26 +195,31 @@ export async function runKycDecisionsTest(t: GlobalTestState) { let checkResp: AccountKycStatus | undefined; while (1) { - let checkHttpResp = await harnessHttpLib.fetch( - new URL(`kyc-check/${kycPaytoHash}`, exchange.baseUrl).href, - { - headers: { - ["Account-Owner-Signature"]: sigResp.sig, - }, - }, - ); - - console.log(`status: ${checkHttpResp.status}`); - - if (checkHttpResp.status == 409 || checkHttpResp.status == 404) { - await waitMs(200); - continue; + const checkHttpResp = await exchangeApi.checkKycStatus({ + paytoHash: kycPaytoHash, + accountPub: merchantPair.pub, + accountSig: sigResp.sig, + }); + + if (checkHttpResp.type === "fail") { + if ( + checkHttpResp.case == HttpStatusCode.Conflict || + checkHttpResp.case == HttpStatusCode.NotFound + ) { + await waitMs(200); + continue; // try again + } } - checkResp = await readSuccessResponseJsonOrThrow( - checkHttpResp, - codecForAccountKycStatus(), - ); + if ( + checkHttpResp.type === "fail" && + checkHttpResp.case === HttpStatusCode.Accepted + ) { + checkResp = checkHttpResp.body; + } + if (checkHttpResp.type === "ok") { + checkResp = checkHttpResp.body; + } break; } @@ -220,16 +228,11 @@ export async function runKycDecisionsTest(t: GlobalTestState) { console.log(j2s(checkResp)); - let infoHttpResp = await harnessHttpLib.fetch( - new URL(`kyc-info/${checkResp.access_token}`, exchange.baseUrl).href, - ); - t.assertDeepEqual(infoHttpResp.status, 200); - const infoResp = await readSuccessResponseJsonOrThrow( - infoHttpResp, - codecForKycProcessClientInformation(), - ); + const infoResp = await exchangeApi.checkKycInfo(checkResp.access_token); + t.assertTrue(infoResp.type === "ok"); + const clientInfo = infoResp.body; - console.log(j2s(infoResp)); + console.log(j2s(clientInfo)); } runKycDecisionsTest.suites = ["wallet"]; diff --git a/packages/taler-harness/src/integrationtests/test-kyc-fail-recover-simple.ts b/packages/taler-harness/src/integrationtests/test-kyc-fail-recover-simple.ts @@ -18,30 +18,29 @@ * Imports. */ import { + AccessToken, AmountString, bufferFromAmount, buildSigPS, - codecForAccountKycStatus, - codecForKycProcessClientInformation, - codecForLegitimizationNeededResponse, - codecOptional, Configuration, createNewWalletKycAccount, eddsaSign, encodeCrock, + ExchangeKycUploadFormRequest, + HttpStatusCode, j2s, + KycRequirementInformationId, Logger, TalerKycAml, TalerSignaturePurpose, - WalletKycRequest, + WalletKycRequest } from "@gnu-taler/taler-util"; -import { readResponseJsonOrThrow } from "@gnu-taler/taler-util/http"; import { execSync } from "node:child_process"; import { configureCommonKyc, createKycTestkudosEnvironmentFull, } from "../harness/environments.js"; -import { GlobalTestState, harnessHttpLib } from "../harness/harness.js"; +import { GlobalTestState } from "../harness/harness.js"; const logger = new Logger(`test-kyc-merchant-fail-recover.ts`); @@ -123,15 +122,18 @@ function adjustExchangeConfig(config: Configuration) { export async function runKycFailRecoverSimpleTest(t: GlobalTestState) { // Set up test environment - const { exchange, amlKeypair } = await createKycTestkudosEnvironmentFull(t, { - adjustExchangeConfig, - onWalletNotification: () => {}, - }); + const { exchangeApi, amlKeypair } = await createKycTestkudosEnvironmentFull( + t, + { + adjustExchangeConfig, + onWalletNotification: () => {}, + }, + ); // Withdraw digital cash into the wallet. let kycPaytoHash: string; - let accessToken: string; - let latestFormId: string; + let accessToken: AccessToken; + let latestFormId: KycRequirementInformationId; const account = await createNewWalletKycAccount(new Uint8Array()); logger.info("step 1) Check balance to trigger AML"); @@ -145,116 +147,88 @@ export async function runKycFailRecoverSimpleTest(t: GlobalTestState) { reserve_pub: account.id, reserve_sig: encodeCrock(eddsaSign(sigBlob, account.signingKey)), }; - const infoResp = await harnessHttpLib.fetch( - new URL(`kyc-wallet`, exchange.baseUrl).href, - { - method: "POST", - body, - }, - ); - t.assertDeepEqual(infoResp.status, 451); - const clientInfo = await readResponseJsonOrThrow( - infoResp, - codecOptional(codecForLegitimizationNeededResponse()), - ); + const infoResp = await exchangeApi.notifyKycBalanceLimit(body); + const clientInfo = + infoResp.type === "fail" && + infoResp.case === HttpStatusCode.UnavailableForLegalReasons + ? infoResp.body + : undefined; + + t.assertTrue(clientInfo !== undefined); + t.assertTrue(clientInfo.h_payto !== undefined); - t.assertTrue(clientInfo?.h_payto !== undefined); - kycPaytoHash = clientInfo?.h_payto; + kycPaytoHash = clientInfo.h_payto; } logger.info("step 2) Get account access token"); { const sigBlob = buildSigPS(TalerSignaturePurpose.KYC_AUTH).build(); - - const infoResp = await harnessHttpLib.fetch( - new URL(`kyc-check/${kycPaytoHash}`, exchange.baseUrl).href, - { - headers: { - "Account-Owner-Signature": encodeCrock( - eddsaSign(sigBlob, account.signingKey), - ), - }, - }, - ); - - t.assertDeepEqual(infoResp.status, 202); - const clientInfo = await readResponseJsonOrThrow( - infoResp, - codecOptional(codecForAccountKycStatus()), + const accountPriv = encodeCrock(eddsaSign(sigBlob, account.signingKey)); + const infoResp = await exchangeApi.checkKycStatus({ + paytoHash: kycPaytoHash, + accountPub: account.id, + accountSig: accountPriv, + }); + + t.assertTrue( + infoResp.type === "fail" && infoResp.case === HttpStatusCode.Accepted, ); + const clientInfo = infoResp.body; t.assertTrue(clientInfo?.access_token !== undefined); - accessToken = clientInfo?.access_token; + accessToken = clientInfo.access_token; } logger.info("step 3) Check KYC info, should be waiting for the first form"); { - const infoResp = await harnessHttpLib.fetch( - new URL(`kyc-info/${accessToken}?timeout_ms=1000`, exchange.baseUrl).href, - ); - const clientInfo = await readResponseJsonOrThrow( - infoResp, - codecOptional(codecForKycProcessClientInformation()), - ); + const infoResp = await exchangeApi.checkKycInfo(accessToken); + t.assertTrue(infoResp.type === "ok"); + const clientInfo = infoResp.body; console.log(j2s(clientInfo)); - t.assertDeepEqual(infoResp.status, 200); - t.assertDeepEqual(clientInfo?.requirements.length, 1); - t.assertDeepEqual(clientInfo?.requirements[0].form, "firstform"); - t.assertTrue(!!clientInfo?.requirements[0].id); - latestFormId = clientInfo?.requirements[0].id; + t.assertDeepEqual(clientInfo.requirements.length, 1); + t.assertDeepEqual(clientInfo.requirements[0].form, "firstform"); + t.assertTrue(!!clientInfo.requirements[0].id); + latestFormId = clientInfo.requirements[0].id; } logger.info("step 4) Complete form expecting to fail"); { - const infoResp = await harnessHttpLib.fetch( - new URL(`kyc-upload/${latestFormId}`, exchange.baseUrl).href, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: { FORM_ID: "test", NAME: "child-fail" }, - }, + const infoResp = await exchangeApi.uploadKycForm(latestFormId, { + FORM_ID: "test", + NAME: "child-fail", + FORM_VERSION: 1, + } as ExchangeKycUploadFormRequest); + + t.assertTrue( + infoResp.type === "fail" && + infoResp.case === HttpStatusCode.InternalServerError, ); - - t.assertDeepEqual(infoResp.status, 500); } logger.info("step 5) Complete form expecting but this time it should work"); { - const infoResp = await harnessHttpLib.fetch( - new URL(`kyc-upload/${latestFormId}`, exchange.baseUrl).href, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: { FORM_ID: "test", NAME: "just-kidding" }, - }, - ); + const infoResp = await exchangeApi.uploadKycForm(latestFormId, { + FORM_ID: "test", + NAME: "just-kidding", + FORM_VERSION: 1, + } as ExchangeKycUploadFormRequest); - logger.info(`kyc-upload response: ${j2s(await infoResp.json())}`); + logger.info(`kyc-upload response: ${j2s(infoResp)}`); - t.assertDeepEqual(infoResp.status, 409); + t.assertTrue(infoResp.type === "ok"); } { logger.info( "step 6) Check KYC info again after some time, here the exchange fails", ); - const infoResp = await harnessHttpLib.fetch( - new URL(`kyc-info/${accessToken}?timeout_ms=1000`, exchange.baseUrl).href, - ); - - const clientInfo = await readResponseJsonOrThrow( - infoResp, - codecOptional(codecForKycProcessClientInformation()), - ); + const infoResp = await exchangeApi.checkKycInfo(accessToken); + t.assertTrue(infoResp.type === "ok"); + const clientInfo = infoResp.body; console.log(j2s(clientInfo)); - t.assertDeepEqual(infoResp.status, 200); - t.assertDeepEqual(clientInfo?.requirements.length, 0); + t.assertDeepEqual(clientInfo.requirements.length, 0); } } diff --git a/packages/taler-harness/src/integrationtests/test-kyc-two-forms.ts b/packages/taler-harness/src/integrationtests/test-kyc-two-forms.ts @@ -18,6 +18,7 @@ * Imports. */ import { + AccessToken, AmountString, bufferFromAmount, buildSigPS, @@ -29,7 +30,10 @@ import { createNewWalletKycAccount, eddsaSign, encodeCrock, + ExchangeKycUploadFormRequest, + HttpStatusCode, j2s, + KycRequirementInformationId, Logger, TalerKycAml, TalerSignaturePurpose, @@ -145,15 +149,16 @@ function adjustExchangeConfig(config: Configuration) { export async function runKycTwoFormsTest(t: GlobalTestState) { // Set up test environment - const { exchange, amlKeypair } = await createKycTestkudosEnvironmentFull(t, { - adjustExchangeConfig, - onWalletNotification: () => {}, - }); + const { exchange, amlKeypair, exchangeApi } = + await createKycTestkudosEnvironmentFull(t, { + adjustExchangeConfig, + onWalletNotification: () => {}, + }); // Withdraw digital cash into the wallet. let kycPaytoHash: string; - let accessToken: string; - let latestFormId: string; + let accessToken: AccessToken; + let latestFormId: KycRequirementInformationId; const account = await createNewWalletKycAccount(new Uint8Array()); { @@ -168,19 +173,13 @@ export async function runKycTwoFormsTest(t: GlobalTestState) { reserve_pub: account.id, reserve_sig: encodeCrock(eddsaSign(sigBlob, account.signingKey)), }; - const infoResp = await harnessHttpLib.fetch( - new URL(`kyc-wallet`, exchange.baseUrl).href, - { - method: "POST", - body, - }, - ); - t.assertDeepEqual(infoResp.status, 451); - const clientInfo = await readResponseJsonOrThrow( - infoResp, - codecOptional(codecForLegitimizationNeededResponse()), - ); + const infoResp = await exchangeApi.notifyKycBalanceLimit(body); + const clientInfo = + infoResp.type === "fail" && + infoResp.case === HttpStatusCode.UnavailableForLegalReasons + ? infoResp.body + : undefined; t.assertTrue(clientInfo?.h_payto !== undefined); kycPaytoHash = clientInfo?.h_payto; @@ -189,39 +188,28 @@ export async function runKycTwoFormsTest(t: GlobalTestState) { { logger.info("step 2) Get account access token"); const sigBlob = buildSigPS(TalerSignaturePurpose.KYC_AUTH).build(); - - const infoResp = await harnessHttpLib.fetch( - new URL(`kyc-check/${kycPaytoHash}`, exchange.baseUrl).href, - { - headers: { - "Account-Owner-Signature": encodeCrock( - eddsaSign(sigBlob, account.signingKey), - ), - }, - }, - ); - - t.assertDeepEqual(infoResp.status, 202); - const clientInfo = await readResponseJsonOrThrow( - infoResp, - codecOptional(codecForAccountKycStatus()), + const accountPriv = encodeCrock(eddsaSign(sigBlob, account.signingKey)); + const infoResp = await exchangeApi.checkKycStatus({ + paytoHash: kycPaytoHash, + accountPub: account.id, + accountSig: accountPriv, + }); + + t.assertTrue( + infoResp.type === "fail" && infoResp.case === HttpStatusCode.Accepted, ); + const clientInfo = infoResp.body; t.assertTrue(clientInfo?.access_token !== undefined); accessToken = clientInfo?.access_token; } { logger.info("step 3) Check KYC info, should be waiting for the first form"); - const infoResp = await harnessHttpLib.fetch( - new URL(`kyc-info/${accessToken}?timeout_ms=1000`, exchange.baseUrl).href, - ); - const clientInfo = await readResponseJsonOrThrow( - infoResp, - codecOptional(codecForKycProcessClientInformation()), - ); + const infoResp = await exchangeApi.checkKycInfo(accessToken); + t.assertTrue(infoResp.type === "ok"); + const clientInfo = infoResp.body; console.log(j2s(clientInfo)); - t.assertDeepEqual(infoResp.status, 200); t.assertDeepEqual(clientInfo?.requirements.length, 1); t.assertDeepEqual(clientInfo?.requirements[0].form, "firstform"); t.assertTrue(!!clientInfo?.requirements[0].id); @@ -230,65 +218,57 @@ export async function runKycTwoFormsTest(t: GlobalTestState) { { logger.info("step 4) Complete form"); - const infoResp = await harnessHttpLib.fetch( - new URL(`kyc-upload/${latestFormId}`, exchange.baseUrl).href, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: { FORM_ID: "test", NAME: "who" }, - }, - ); - - t.assertDeepEqual(infoResp.status, 204); - } - - { - logger.info( - "step 5) Check KYC info again, should see the second form but this time is too fast", + const infoResp = await exchangeApi.uploadKycForm(latestFormId, { + FORM_ID: "test", + NAME: "who", + FORM_VERSION: 1, + } as ExchangeKycUploadFormRequest); + + t.assertTrue( + infoResp.type === "fail" && + infoResp.case === HttpStatusCode.InternalServerError, ); - - /////////////////////////////////////////// - // if the request is too early id result in garbage - // can't be ignored because browser see this - /////////////////////////////////////////// - { - const infoResp = await harnessHttpLib.fetch( - new URL(`kyc-info/${accessToken}?timeout_ms=1000`, exchange.baseUrl) - .href, - ); - { - const clientInfo = await readResponseJsonOrThrow( - infoResp, - codecOptional(codecForKycProcessClientInformation()), - ); - - console.log(j2s(clientInfo)); - // t.assertDeepEqual(infoResp.status, 200); - // t.assertDeepEqual(clientInfo?.requirements.length, 1); - // t.assertDeepEqual(clientInfo?.requirements[0].form, "secondForm"); - } - } } - await waitMs(2000); + // { + // logger.info( + // "step 5) Check KYC info again, should see the second form but this time is too fast", + // ); + + // /////////////////////////////////////////// + // // if the request is too early id result in garbage + // // can't be ignored because browser see this + // /////////////////////////////////////////// + // { + // const infoResp = await harnessHttpLib.fetch( + // new URL(`kyc-info/${accessToken}?timeout_ms=1000`, exchange.baseUrl) + // .href, + // ); + // { + // const clientInfo = await readResponseJsonOrThrow( + // infoResp, + // codecOptional(codecForKycProcessClientInformation()), + // ); + + // console.log(j2s(clientInfo)); + // // t.assertDeepEqual(infoResp.status, 200); + // // t.assertDeepEqual(clientInfo?.requirements.length, 1); + // // t.assertDeepEqual(clientInfo?.requirements[0].form, "secondForm"); + // } + // } + // } + + // await waitMs(2000); { logger.info( "step 6) Check KYC info again after some time, should see the second form", ); // doing a second request shouldn't fail - const infoResp = await harnessHttpLib.fetch( - new URL(`kyc-info/${accessToken}?timeout_ms=1000`, exchange.baseUrl).href, - ); - - const clientInfo = await readResponseJsonOrThrow( - infoResp, - codecOptional(codecForKycProcessClientInformation()), - ); + const infoResp = await exchangeApi.checkKycInfo(accessToken); + t.assertTrue(infoResp.type === "ok"); + const clientInfo = infoResp.body; console.log(j2s(clientInfo)); - t.assertDeepEqual(infoResp.status, 200); t.assertDeepEqual(clientInfo?.requirements.length, 1); t.assertDeepEqual(clientInfo?.requirements[0].form, "secondform"); } @@ -299,17 +279,11 @@ export async function runKycTwoFormsTest(t: GlobalTestState) { logger.info( "step 6) Check KYC info again after some time, here the exchange fails", ); - const infoResp = await harnessHttpLib.fetch( - new URL(`kyc-info/${accessToken}?timeout_ms=1000`, exchange.baseUrl).href, - ); - - const clientInfo = await readResponseJsonOrThrow( - infoResp, - codecOptional(codecForKycProcessClientInformation()), - ); + const infoResp = await exchangeApi.checkKycInfo(accessToken); + t.assertTrue(infoResp.type === "ok"); + const clientInfo = infoResp.body; console.log(j2s(clientInfo)); - t.assertDeepEqual(infoResp.status, 200); t.assertDeepEqual(clientInfo?.requirements.length, 1); t.assertDeepEqual(clientInfo?.requirements[0].form, "secondform"); } diff --git a/packages/taler-util/src/http-client/exchange-client.ts b/packages/taler-util/src/http-client/exchange-client.ts @@ -830,6 +830,7 @@ export class TalerExchangeHttpClient { ): Promise< | OperationOk<void> | OperationFail<HttpStatusCode.Conflict> + | OperationFail<HttpStatusCode.InternalServerError> | OperationFail<HttpStatusCode.NotFound> | OperationFail<HttpStatusCode.PayloadTooLarge> > { @@ -846,6 +847,7 @@ export class TalerExchangeHttpClient { return opEmptySuccess(); } case HttpStatusCode.NotFound: + case HttpStatusCode.InternalServerError: case HttpStatusCode.Conflict: case HttpStatusCode.PayloadTooLarge: return opKnownHttpFailure(resp.status, resp);