From 13f0442736479fb6ea8d1ecc7311cdac354a4de5 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Sun, 12 Feb 2023 19:30:59 +0100 Subject: harness: finish kyc test We mock the KYC gateway now, use the new notification-based wallet API and the test is not experimental anymore. --- .../taler-harness/src/integrationtests/test-kyc.ts | 199 ++++++++++++++++++--- 1 file changed, 174 insertions(+), 25 deletions(-) (limited to 'packages/taler-harness/src/integrationtests/test-kyc.ts') diff --git a/packages/taler-harness/src/integrationtests/test-kyc.ts b/packages/taler-harness/src/integrationtests/test-kyc.ts index c652c86fa..b08db66f7 100644 --- a/packages/taler-harness/src/integrationtests/test-kyc.ts +++ b/packages/taler-harness/src/integrationtests/test-kyc.ts @@ -17,7 +17,13 @@ /** * Imports. */ -import { Duration } from "@gnu-taler/taler-util"; +import { Duration, j2s, NotificationType } from "@gnu-taler/taler-util"; +import { + BankAccessApi, + BankApi, + NodeHttpLib, + WalletApiOperation, +} from "@gnu-taler/taler-wallet-core"; import { CoinConfig, defaultCoinConfig } from "../harness/denomStructures.js"; import { BankService, @@ -26,20 +32,17 @@ import { GlobalTestState, MerchantService, setupDb, - WalletCli, + WalletClient, + WalletService, } from "../harness/harness.js"; -import { - withdrawViaBank, - makeTestPayment, - EnvOptions, - SimpleTestEnvironment, -} from "../harness/helpers.js"; +import { EnvOptions, SimpleTestEnvironmentNg } from "../harness/helpers.js"; +import * as http from "node:http"; export async function createKycTestkudosEnvironment( t: GlobalTestState, coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")), opts: EnvOptions = {}, -): Promise { +): Promise { const db = await setupDb(t); const bank = await BankService.create(t, { @@ -117,11 +120,11 @@ export async function createKycTestkudosEnvironment( config.setString( myprov, "kyc_oauth2_info_url", - "http://localhost:6666/oauth/v2/login", + "http://localhost:6666/oauth/v2/info", ); config.setString(myprov, "kyc_oauth2_client_id", "taler-exchange"); config.setString(myprov, "kyc_oauth2_client_secret", "exchange-secret"); - config.setString(myprov, "kyc_oauth2_post_url", "https://taler.com"); + config.setString(myprov, "kyc_oauth2_post_url", "https://taler.net"); config.setString( "kyc-legitimization-withdraw1", @@ -167,40 +170,186 @@ export async function createKycTestkudosEnvironment( ), }); - console.log("setup done!"); + const walletService = new WalletService(t, { + name: "wallet", + useInMemoryDb: true, + }); + await walletService.start(); + await walletService.pingUntilAvailable(); - const wallet = new WalletCli(t); + const walletClient = new WalletClient({ + unixPath: walletService.socketPath, + onNotification(n) { + console.log("got notification", n); + }, + }); + await walletClient.connect(); + await walletClient.client.call(WalletApiOperation.InitWallet, { + skipDefaults: true, + }); + + console.log("setup done!"); return { commonDb: db, exchange, merchant, - wallet, + walletClient, + walletService, bank, exchangeBankAccount, }; } +interface TestfakeKycService { + stop: () => void; +} + +function splitInTwoAt(s: string, separator: string): [string, string] { + const idx = s.indexOf(separator); + if (idx === -1) { + return [s, ""]; + } + return [s.slice(0, idx), s.slice(idx + 1)]; +} + +/** + * Testfake for the kyc service that the exchange talks to. + */ +async function runTestfakeKycService(): Promise { + const server = http.createServer((req, res) => { + const requestUrl = req.url!; + console.log(`kyc: got ${req.method} request`, requestUrl); + + const [path, query] = splitInTwoAt(requestUrl, "?"); + + const qp = new URLSearchParams(query); + + if (path === "/oauth/v2/login") { + // Usually this would render some HTML page for the user to log in, + // but we return JSON here. + const redirUri = new URL(qp.get("redirect_uri")!); + redirUri.searchParams.set("code", "code_is_ok"); + res.writeHead(200, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + redirect_uri: redirUri.href, + }), + ); + } else if (path === "/oauth/v2/token") { + let reqBody = ""; + req.on("data", (x) => { + reqBody += x; + }); + + req.on("end", () => { + console.log("login request body:", reqBody); + + res.writeHead(200, { "Content-Type": "application/json" }); + // Normally, the access_token would also include which user we're trying + // to get info about, but we (for now) skip it in this test. + res.end( + JSON.stringify({ + access_token: "exchange_access_token", + token_type: "Bearer", + }), + ); + }); + } else if (path === "/oauth/v2/info") { + console.log("authorization header:", req.headers.authorization); + res.writeHead(200, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + status: "success", + data: { + id: "foobar", + }, + }), + ); + } else { + res.writeHead(400, { "Content-Type": "application/json" }); + res.end(JSON.stringify({ code: 1, message: "bad request" })); + } + }); + await new Promise((resolve, reject) => { + server.listen(6666, () => resolve()); + }); + return { + stop() { + server.close(); + }, + }; +} + export async function runKycTest(t: GlobalTestState) { // Set up test environment - const { wallet, bank, exchange, merchant } = + const { walletClient, bank, exchange, merchant } = await createKycTestkudosEnvironment(t); + const kycServer = await runTestfakeKycService(); + // Withdraw digital cash into the wallet. - await withdrawViaBank(t, { wallet, bank, exchange, amount: "TESTKUDOS:20" }); + const amount = "TESTKUDOS:20"; + const user = await BankApi.createRandomBankUser(bank); + const wop = await BankAccessApi.createWithdrawalOperation(bank, user, amount); - const order = { - summary: "Buy me!", - amount: "TESTKUDOS:5", - fulfillment_url: "taler://fulfillment-success/thx", - }; + // Hand it to the wallet + + await walletClient.client.call( + WalletApiOperation.GetWithdrawalDetailsForUri, + { + talerWithdrawUri: wop.taler_withdraw_uri, + }, + ); + + // Withdraw + + const kycNotificationCond = walletClient.waitForNotificationCond((x) => { + if (x.type === NotificationType.WithdrawalKycRequested) { + return x; + } + return false; + }); + + const withdrawalDoneCond = walletClient.waitForNotificationCond( + (x) => x.type === NotificationType.WithdrawGroupFinished, + ); + + await walletClient.client.call( + WalletApiOperation.AcceptBankIntegratedWithdrawal, + { + exchangeBaseUrl: exchange.baseUrl, + talerWithdrawUri: wop.taler_withdraw_uri, + }, + ); + + // Confirm it + + await BankApi.confirmWithdrawalOperation(bank, user, wop); + + const kycNotif = await kycNotificationCond; + + console.log("got kyc notification:", j2s(kycNotif)); + + // We now simulate the user interacting with the KYC service, + // which would usually done in the browser. + + const httpClient = new NodeHttpLib(); + const kycServerResp = await httpClient.get(kycNotif.kycUrl); + const kycLoginResp = await kycServerResp.json(); + console.log("kyc server resp:", j2s(kycLoginResp)); + const kycProofUrl = kycLoginResp.redirect_uri; + const proofHttpResp = await httpClient.get(kycProofUrl); + console.log("proof resp status", proofHttpResp.status); + console.log("resp headers", proofHttpResp.headers.toJSON()); + + // Now that KYC is done, withdrawal should finally succeed. + + await withdrawalDoneCond; - await makeTestPayment(t, { wallet, merchant, order }); - await wallet.runUntilDone(); + kycServer.stop(); } runKycTest.suites = ["wallet"]; -// See bugs.taler.net/n/7599 -runKycTest.experimental = true; -- cgit v1.2.3