diff options
Diffstat (limited to 'packages/taler-harness/src/harness')
| -rw-r--r-- | packages/taler-harness/src/harness/harness.ts | 438 | ||||
| -rw-r--r-- | packages/taler-harness/src/harness/helpers.ts | 358 | ||||
| -rw-r--r-- | packages/taler-harness/src/harness/libeufin-apis.ts | 76 |
3 files changed, 150 insertions, 722 deletions
diff --git a/packages/taler-harness/src/harness/harness.ts b/packages/taler-harness/src/harness/harness.ts index 7db9d82bd..24e42099e 100644 --- a/packages/taler-harness/src/harness/harness.ts +++ b/packages/taler-harness/src/harness/harness.ts @@ -25,59 +25,45 @@ * Imports */ import { + AccountAddDetails, AmountJson, Amounts, - AmountString, - codecForMerchantOrderPrivateStatusResponse, - codecForMerchantPostOrderResponse, - codecForMerchantReserveCreateConfirmation, + BankAccessApiClient, Configuration, CoreApiResponse, - createEddsaKeyPair, Duration, - eddsaGetPublic, EddsaKeyPair, + Logger, + MerchantInstanceConfig, + PartialMerchantInstanceConfig, + TalerError, + WalletNotification, + createEddsaKeyPair, + eddsaGetPublic, encodeCrock, hash, j2s, - Logger, - MerchantInstancesResponse, - MerchantOrderPrivateStatusResponse, - MerchantPostOrderRequest, - MerchantPostOrderResponse, - MerchantReserveCreateConfirmation, - MerchantTemplateAddDetails, parsePaytoUri, stringToBytes, - TalerError, - TalerProtocolDuration, - RewardCreateConfirmation, - RewardCreateRequest, - TippingReserveStatus, - WalletNotification, - codecForAny, } from "@gnu-taler/taler-util"; import { + HttpRequestLibrary, createPlatformHttpLib, expectSuccessResponseOrThrow, - readSuccessResponseJsonOrThrow, } from "@gnu-taler/taler-util/http"; import { - BankApi, - BankServiceHandle, - HarnessExchangeBankAccount, - openPromise, WalletCoreApiClient, WalletCoreRequestType, WalletCoreResponseType, WalletOperations, + openPromise, } from "@gnu-taler/taler-wallet-core"; import { + RemoteWallet, + WalletNotificationWaiter, createRemoteWallet, getClientFromRemoteWallet, makeNotificationWaiter, - RemoteWallet, - WalletNotificationWaiter, } from "@gnu-taler/taler-wallet-core/remote"; import { deepStrictEqual } from "assert"; import { ChildProcess, spawn } from "child_process"; @@ -383,7 +369,11 @@ export class GlobalTestState { logger.warn(`could not start process (${command})`, err); }); proc.on("exit", (code, signal) => { - logger.warn(`process ${logName} exited ${j2s({ code, signal })}`); + if (code == 0 && signal == null) { + logger.info(`process ${logName} exited with success`); + } else { + logger.warn(`process ${logName} exited ${j2s({ code, signal })}`); + } }); const stderrLogFileName = this.testDir + `/${logName}-stderr.log`; const stderrLog = fs.createWriteStream(stderrLogFileName, { @@ -578,6 +568,13 @@ class BankServiceBase { ) {} } +export interface HarnessExchangeBankAccount { + accountName: string; + accountPassword: string; + accountPaytoUri: string; + wireGatewayApiBaseUrl: string; +} + /** * Implementation of the bank service using the "taler-fakebank-run" tool. */ @@ -587,7 +584,7 @@ export class FakebankService { proc: ProcessWrapper | undefined; - http = createPlatformHttpLib({ allowHttp: true, enableThrottling: false }); + http = createPlatformHttpLib({ enableThrottling: false }); // We store "created" accounts during setup and // register them after startup. @@ -695,13 +692,9 @@ export class FakebankService "bank", ); await this.pingUntilAvailable(); + const bankClient = new BankAccessApiClient(this.bankAccessApiBaseUrl); for (const acc of this.accounts) { - await BankApi.registerAccount( - this, - acc.accountName, - acc.accountPassword, - {}, - ); + await bankClient.registerAccount(acc.accountName, acc.accountPassword); } } @@ -714,6 +707,11 @@ export class FakebankService // Use libeufin bank instead of pybank. const useLibeufinBank = false; +export interface BankServiceHandle { + readonly bankAccessApiBaseUrl: string; + readonly http: HttpRequestLibrary; +} + export type BankService = BankServiceHandle; export const BankService = FakebankService; @@ -760,19 +758,19 @@ export class ExchangeService implements ExchangeServiceInterface { return new ExchangeService(gc, ec, cfgFilename, keyPair); } - private currentTimetravel: Duration | undefined; + private currentTimetravelOffsetMs: number | undefined; - setTimetravel(t: Duration | undefined): void { + setTimetravel(t: number | undefined): void { if (this.isRunning()) { throw Error("can't set time travel while the exchange is running"); } - this.currentTimetravel = t; + this.currentTimetravelOffsetMs = t; } private get timetravelArg(): string | undefined { - if (this.currentTimetravel && this.currentTimetravel.d_ms !== "forever") { + if (this.currentTimetravelOffsetMs != null) { // Convert to microseconds - return `--timetravel=+${this.currentTimetravel.d_ms * 1000}`; + return `--timetravel=+${this.currentTimetravelOffsetMs * 1000}`; } return undefined; } @@ -1334,282 +1332,19 @@ export interface MerchantConfig { overrideTestDir?: string; } -export interface PrivateOrderStatusQuery { - instance?: string; - orderId: string; - sessionId?: string; -} - export interface MerchantServiceInterface { makeInstanceBaseUrl(instanceName?: string): string; readonly port: number; readonly name: string; } -export interface DeleteTippingReserveArgs { - reservePub: string; - purge?: boolean; -} - /** * Default HTTP client handle for the integration test harness. */ export const harnessHttpLib = createPlatformHttpLib({ - allowHttp: true, enableThrottling: false, }); -export class MerchantApiClient { - constructor( - private baseUrl: string, - public readonly auth: MerchantAuthConfiguration, - ) {} - - httpClient = createPlatformHttpLib({ allowHttp: true, enableThrottling: false }); - - async changeAuth(auth: MerchantAuthConfiguration): Promise<void> { - const url = new URL("private/auth", this.baseUrl); - const res = await this.httpClient.fetch(url.href, { - method: "POST", - body: auth, - headers: this.makeAuthHeader(), - }); - await expectSuccessResponseOrThrow(res); - } - - async deleteTippingReserve(req: DeleteTippingReserveArgs): Promise<void> { - const url = new URL(`private/reserves/${req.reservePub}`, this.baseUrl); - if (req.purge) { - url.searchParams.set("purge", "YES"); - } - const resp = await this.httpClient.fetch(url.href, { - method: "DELETE", - headers: this.makeAuthHeader(), - }); - logger.info(`delete status: ${resp.status}`); - return; - } - - async createTippingReserve( - req: CreateMerchantTippingReserveRequest, - ): Promise<MerchantReserveCreateConfirmation> { - const url = new URL("private/reserves", this.baseUrl); - const resp = await this.httpClient.fetch(url.href, { - method: "POST", - body: req, - headers: this.makeAuthHeader(), - }); - const respData = readSuccessResponseJsonOrThrow( - resp, - codecForMerchantReserveCreateConfirmation(), - ); - return respData; - } - - async getPrivateInstanceInfo(): Promise<any> { - console.log(this.makeAuthHeader()); - const url = new URL("private", this.baseUrl); - logger.info(`request url ${url.href}`); - const resp = await this.httpClient.fetch(url.href, { - method: "GET", - headers: this.makeAuthHeader(), - }); - return await resp.json(); - } - - async getPrivateTipReserves(): Promise<TippingReserveStatus> { - console.log(this.makeAuthHeader()); - const url = new URL("private/reserves", this.baseUrl); - const resp = await this.httpClient.fetch(url.href, { - method: "GET", - headers: this.makeAuthHeader(), - }); - // FIXME: Validate! - return await resp.json(); - } - - async deleteInstance(instanceId: string) { - const url = new URL(`management/instances/${instanceId}`, this.baseUrl); - const resp = await this.httpClient.fetch(url.href, { - method: "DELETE", - headers: this.makeAuthHeader(), - }); - await expectSuccessResponseOrThrow(resp); - } - - async createInstance(req: MerchantInstanceConfig): Promise<void> { - const url = new URL("management/instances", this.baseUrl); - await this.httpClient.fetch(url.href, { - method: "POST", - body: req, - headers: this.makeAuthHeader(), - }); - } - - async getInstances(): Promise<MerchantInstancesResponse> { - const url = new URL("management/instances", this.baseUrl); - const resp = await this.httpClient.fetch(url.href, { - headers: this.makeAuthHeader(), - }); - return resp.json(); - } - - async getInstanceFullDetails(instanceId: string): Promise<any> { - const url = new URL(`management/instances/${instanceId}`, this.baseUrl); - try { - const resp = await this.httpClient.fetch(url.href, { - headers: this.makeAuthHeader(), - }); - return resp.json(); - } catch (e) { - throw e; - } - } - - makeAuthHeader(): Record<string, string> { - switch (this.auth.method) { - case "external": - return {}; - case "token": - return { - Authorization: `Bearer ${this.auth.token}`, - }; - } - } -} - -/** - * FIXME: This should be deprecated in favor of MerchantApiClient - * - * @deprecated use MerchantApiClient instead - */ -export namespace MerchantPrivateApi { - export async function createOrder( - merchantService: MerchantServiceInterface, - instanceName: string, - req: MerchantPostOrderRequest, - withAuthorization: WithAuthorization = {}, - ): Promise<MerchantPostOrderResponse> { - const baseUrl = merchantService.makeInstanceBaseUrl(instanceName); - let url = new URL("private/orders", baseUrl); - const resp = await harnessHttpLib.fetch(url.href, { - method: "POST", - body: req, - headers: withAuthorization as Record<string, string>, - }); - return readSuccessResponseJsonOrThrow( - resp, - codecForMerchantPostOrderResponse(), - ); - } - - export async function createTemplate( - merchantService: MerchantServiceInterface, - instanceName: string, - req: MerchantTemplateAddDetails, - withAuthorization: WithAuthorization = {}, - ) { - const baseUrl = merchantService.makeInstanceBaseUrl(instanceName); - let url = new URL("private/templates", baseUrl); - const resp = await harnessHttpLib.fetch(url.href, { - method: "POST", - body: req, - headers: withAuthorization as Record<string, string>, - }); - if (resp.status !== 204) { - throw Error("failed to create template"); - } - } - - export async function queryPrivateOrderStatus( - merchantService: MerchantServiceInterface, - query: PrivateOrderStatusQuery, - withAuthorization: WithAuthorization = {}, - ): Promise<MerchantOrderPrivateStatusResponse> { - const reqUrl = new URL( - `private/orders/${query.orderId}`, - merchantService.makeInstanceBaseUrl(query.instance), - ); - if (query.sessionId) { - reqUrl.searchParams.set("session_id", query.sessionId); - } - const resp = await harnessHttpLib.fetch(reqUrl.href, { - headers: withAuthorization as Record<string, string>, - }); - return readSuccessResponseJsonOrThrow( - resp, - codecForMerchantOrderPrivateStatusResponse(), - ); - } - - export async function giveRefund( - merchantService: MerchantServiceInterface, - r: { - instance: string; - orderId: string; - amount: string; - justification: string; - }, - ): Promise<{ talerRefundUri: string }> { - const reqUrl = new URL( - `private/orders/${r.orderId}/refund`, - merchantService.makeInstanceBaseUrl(r.instance), - ); - const resp = await harnessHttpLib.fetch(reqUrl.href, { - method: "POST", - body: { - refund: r.amount, - reason: r.justification, - }, - }); - const respBody = await resp.json(); - return { - talerRefundUri: respBody.taler_refund_uri, - }; - } - - export async function queryTippingReserves( - merchantService: MerchantServiceInterface, - instance: string, - ): Promise<TippingReserveStatus> { - const reqUrl = new URL( - `private/reserves`, - merchantService.makeInstanceBaseUrl(instance), - ); - const resp = await harnessHttpLib.fetch(reqUrl.href); - // FIXME: validate - return resp.json(); - } - - export async function giveTip( - merchantService: MerchantServiceInterface, - instance: string, - req: RewardCreateRequest, - ): Promise<RewardCreateConfirmation> { - const reqUrl = new URL( - `private/tips`, - merchantService.makeInstanceBaseUrl(instance), - ); - const resp = await harnessHttpLib.fetch(reqUrl.href, { - method: "POST", - body: req, - }); - // FIXME: validate - return resp.json(); - } -} - -export interface CreateMerchantTippingReserveRequest { - // Amount that the merchant promises to put into the reserve - initial_balance: AmountString; - - // Exchange the merchant intends to use for tipping - exchange_url: string; - - // Desired wire method, for example "iban" or "x-taler-bank" - wire_method: string; -} - export class MerchantService implements MerchantServiceInterface { static fromExistingConfig( gc: GlobalTestState, @@ -1636,23 +1371,23 @@ export class MerchantService implements MerchantServiceInterface { private configFilename: string, ) {} - private currentTimetravel: Duration | undefined; + private currentTimetravelOffsetMs: number | undefined; private isRunning(): boolean { return !!this.proc; } - setTimetravel(t: Duration | undefined): void { + setTimetravel(t: number | undefined): void { if (this.isRunning()) { throw Error("can't set time travel while the exchange is running"); } - this.currentTimetravel = t; + this.currentTimetravelOffsetMs = t; } private get timetravelArg(): string | undefined { - if (this.currentTimetravel && this.currentTimetravel.d_ms !== "forever") { + if (this.currentTimetravelOffsetMs != null) { // Convert to microseconds - return `--timetravel=+${this.currentTimetravel.d_ms * 1000}`; + return `--timetravel=+${this.currentTimetravelOffsetMs * 1000}`; } return undefined; } @@ -1759,7 +1494,7 @@ export class MerchantService implements MerchantServiceInterface { } async addDefaultInstance(): Promise<void> { - return await this.addInstance({ + return await this.addInstanceWithWireAccount({ id: "default", name: "Default Instance", paytoUris: [getPayto("merchant-default")], @@ -1769,13 +1504,16 @@ export class MerchantService implements MerchantServiceInterface { }); } - async addInstance( + /** + * Add an instance together with a wire account. + */ + async addInstanceWithWireAccount( instanceConfig: PartialMerchantInstanceConfig, ): Promise<void> { if (!this.proc) { throw Error("merchant must be running to add instance"); } - logger.info("adding instance"); + logger.info(`adding instance '${instanceConfig.id}'`); const url = `http://localhost:${this.merchantConfig.httpPort}/management/instances`; const auth = instanceConfig.auth ?? { method: "external" }; @@ -1801,12 +1539,20 @@ export class MerchantService implements MerchantServiceInterface { instanceConfig.defaultPayDelay ?? Duration.toTalerProtocolDuration(Duration.getForever()), }; - const httpLib = createPlatformHttpLib({ - allowHttp: true, - enableThrottling: false, - }); - const resp = await httpLib.fetch(url, { method: "POST", body }); + const resp = await harnessHttpLib.fetch(url, { method: "POST", body }); await expectSuccessResponseOrThrow(resp); + + const accountCreateUrl = `http://localhost:${this.merchantConfig.httpPort}/instances/${instanceConfig.id}/private/accounts`; + for (const paytoUri of instanceConfig.paytoUris) { + const accountReq: AccountAddDetails = { + payto_uri: paytoUri, + }; + const acctResp = await harnessHttpLib.fetch(accountCreateUrl, { + method: "POST", + body: accountReq, + }); + await expectSuccessResponseOrThrow(acctResp); + } } makeInstanceBaseUrl(instanceName?: string): string { @@ -1823,66 +1569,6 @@ export class MerchantService implements MerchantServiceInterface { } } -export interface MerchantAuthConfiguration { - method: "external" | "token"; - token?: string; -} - -// FIXME: Why do we need this? Describe / fix! -export interface PartialMerchantInstanceConfig { - auth?: MerchantAuthConfiguration; - id: string; - name: string; - paytoUris: string[]; - address?: unknown; - jurisdiction?: unknown; - defaultWireTransferDelay?: TalerProtocolDuration; - defaultPayDelay?: TalerProtocolDuration; -} - -// FIXME: Move all these types into merchant-api-types.ts! - -type FacadeCredentials = NoFacadeCredentials | BasicAuthFacadeCredentials; -interface NoFacadeCredentials { - type: "none"; -} -interface BasicAuthFacadeCredentials { - type: "basic"; - - // Username to use to authenticate - username: string; - - // Password to use to authenticate - password: string; -} - -interface MerchantBankAccount { - // The payto:// URI where the wallet will send coins. - payto_uri: string; - - // Optional base URL for a facade where the - // merchant backend can see incoming wire - // transfers to reconcile its accounting - // with that of the exchange. Used by - // taler-merchant-wirewatch. - credit_facade_url?: string; - - // Credentials for accessing the credit facade. - credit_facade_credentials?: FacadeCredentials; -} - -export interface MerchantInstanceConfig { - accounts: MerchantBankAccount[]; - auth: MerchantAuthConfiguration; - id: string; - name: string; - address: unknown; - jurisdiction: unknown; - use_stefan: boolean; - default_wire_transfer_delay: TalerProtocolDuration; - default_pay_delay: TalerProtocolDuration; -} - type TestStatus = "pass" | "fail" | "skip"; export interface TestRunResult { diff --git a/packages/taler-harness/src/harness/helpers.ts b/packages/taler-harness/src/harness/helpers.ts index d1d0ea104..9892e600b 100644 --- a/packages/taler-harness/src/harness/helpers.ts +++ b/packages/taler-harness/src/harness/helpers.ts @@ -25,21 +25,18 @@ */ import { AmountString, + BankAccessApiClient, ConfirmPayResultType, - MerchantContractTerms, Duration, - PreparePayResultType, + Logger, + MerchantApiClient, + MerchantContractTerms, NotificationType, - WalletNotification, + PreparePayResultType, TransactionMajorState, - Logger, + WalletNotification, } from "@gnu-taler/taler-util"; -import { - BankAccessApi, - BankApi, - HarnessExchangeBankAccount, - WalletApiOperation, -} from "@gnu-taler/taler-wallet-core"; +import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { CoinConfig, defaultCoinConfig } from "./denomStructures.js"; import { FaultInjectedExchangeService, @@ -51,17 +48,17 @@ import { ExchangeService, ExchangeServiceInterface, FakebankService, - getPayto, GlobalTestState, - MerchantPrivateApi, + HarnessExchangeBankAccount, MerchantService, MerchantServiceInterface, - setupDb, - setupSharedDb, WalletCli, WalletClient, WalletService, WithAuthorization, + getPayto, + setupDb, + setupSharedDb, } from "./harness.js"; import * as fs from "fs"; @@ -107,114 +104,6 @@ export interface EnvOptions { additionalBankConfig?(b: BankService): void; } -/** - * Run a test case with a simple TESTKUDOS Taler environment, consisting - * of one exchange, one bank and one merchant. - * - * @deprecated use {@link createSimpleTestkudosEnvironmentV2} instead - */ -export async function createSimpleTestkudosEnvironment( - t: GlobalTestState, - coinConfig: CoinConfig[] = defaultCoinConfig.map((x) => x("TESTKUDOS")), - opts: EnvOptions = {}, -): Promise<SimpleTestEnvironment> { - const db = await setupDb(t); - - const bank = await BankService.create(t, { - allowRegistrations: true, - currency: "TESTKUDOS", - database: db.connStr, - httpPort: 8082, - }); - - const exchange = ExchangeService.create(t, { - name: "testexchange-1", - currency: "TESTKUDOS", - httpPort: 8081, - database: db.connStr, - }); - - const merchant = await MerchantService.create(t, { - name: "testmerchant-1", - currency: "TESTKUDOS", - httpPort: 8083, - database: db.connStr, - }); - - const exchangeBankAccount = await bank.createExchangeAccount( - "myexchange", - "x", - ); - await exchange.addBankAccount("1", exchangeBankAccount); - - bank.setSuggestedExchange(exchange, exchangeBankAccount.accountPaytoUri); - - await bank.start(); - - await bank.pingUntilAvailable(); - - const ageMaskSpec = opts.ageMaskSpec; - - if (ageMaskSpec) { - exchange.enableAgeRestrictions(ageMaskSpec); - // Enable age restriction for all coins. - exchange.addCoinConfigList( - coinConfig.map((x) => ({ - ...x, - name: `${x.name}-age`, - ageRestricted: true, - })), - ); - // For mixed age restrictions, we also offer coins without age restrictions - if (opts.mixedAgeRestriction) { - exchange.addCoinConfigList( - coinConfig.map((x) => ({ ...x, ageRestricted: false })), - ); - } - } else { - exchange.addCoinConfigList(coinConfig); - } - - await exchange.start(); - await exchange.pingUntilAvailable(); - - merchant.addExchange(exchange); - - await merchant.start(); - await merchant.pingUntilAvailable(); - - await merchant.addInstance({ - id: "default", - name: "Default Instance", - paytoUris: [getPayto("merchant-default")], - defaultWireTransferDelay: Duration.toTalerProtocolDuration( - Duration.fromSpec({ minutes: 1 }), - ), - }); - - await merchant.addInstance({ - id: "minst1", - name: "minst1", - paytoUris: [getPayto("minst1")], - defaultWireTransferDelay: Duration.toTalerProtocolDuration( - Duration.fromSpec({ minutes: 1 }), - ), - }); - - console.log("setup done!"); - - const wallet = new WalletCli(t); - - return { - commonDb: db, - exchange, - merchant, - wallet, - bank, - exchangeBankAccount, - }; -} - export function getSharedTestDir(): string { return `/tmp/taler-harness@${process.env.USER}`; } @@ -344,7 +233,7 @@ export async function useSharedTestkudosEnvironment(t: GlobalTestState) { await merchant.pingUntilAvailable(); if (!prevSetupDone) { - await merchant.addInstance({ + await merchant.addInstanceWithWireAccount({ id: "default", name: "Default Instance", paytoUris: [getPayto("merchant-default")], @@ -353,7 +242,7 @@ export async function useSharedTestkudosEnvironment(t: GlobalTestState) { ), }); - await merchant.addInstance({ + await merchant.addInstanceWithWireAccount({ id: "minst1", name: "minst1", paytoUris: [getPayto("minst1")], @@ -476,7 +365,7 @@ export async function createSimpleTestkudosEnvironmentV2( await merchant.start(); await merchant.pingUntilAvailable(); - await merchant.addInstance({ + await merchant.addInstanceWithWireAccount({ id: "default", name: "Default Instance", paytoUris: [getPayto("merchant-default")], @@ -485,7 +374,7 @@ export async function createSimpleTestkudosEnvironmentV2( ), }); - await merchant.addInstance({ + await merchant.addInstanceWithWireAccount({ id: "minst1", name: "minst1", paytoUris: [getPayto("minst1")], @@ -554,7 +443,7 @@ export interface FaultyMerchantTestEnvironment { exchangeBankAccount: HarnessExchangeBankAccount; merchant: MerchantService; faultyMerchant: FaultInjectedMerchantService; - wallet: WalletCli; + walletClient: WalletClient; } /** @@ -620,13 +509,13 @@ export async function createFaultInjectedMerchantTestkudosEnvironment( await merchant.start(); await merchant.pingUntilAvailable(); - await merchant.addInstance({ + await merchant.addInstanceWithWireAccount({ id: "default", name: "Default Instance", paytoUris: [getPayto("merchant-default")], }); - await merchant.addInstance({ + await merchant.addInstanceWithWireAccount({ id: "minst1", name: "minst1", paytoUris: [getPayto("minst1")], @@ -634,13 +523,15 @@ export async function createFaultInjectedMerchantTestkudosEnvironment( console.log("setup done!"); - const wallet = new WalletCli(t); + const { walletClient } = await createWalletDaemonWithClient(t, { + name: "default", + }); return { commonDb: db, exchange, merchant, - wallet, + walletClient, bank, exchangeBankAccount, faultyMerchant, @@ -648,51 +539,6 @@ export async function createFaultInjectedMerchantTestkudosEnvironment( }; } -/** - * Start withdrawing into the wallet. - * - * Only starts the operation, does not wait for it to finish. - */ -export async function startWithdrawViaBank( - t: GlobalTestState, - p: { - wallet: WalletCli; - bank: BankService; - exchange: ExchangeServiceInterface; - amount: AmountString; - restrictAge?: number; - }, -): Promise<void> { - const { wallet, bank, exchange, amount } = p; - - const user = await BankApi.createRandomBankUser(bank); - const wop = await BankAccessApi.createWithdrawalOperation(bank, user, amount); - - // Hand it to the wallet - - await wallet.client.call(WalletApiOperation.GetWithdrawalDetailsForUri, { - talerWithdrawUri: wop.taler_withdraw_uri, - restrictAge: p.restrictAge, - }); - - await wallet.runPending(); - - // Withdraw (AKA select) - - await wallet.client.call(WalletApiOperation.AcceptBankIntegratedWithdrawal, { - exchangeBaseUrl: exchange.baseUrl, - talerWithdrawUri: wop.taler_withdraw_uri, - restrictAge: p.restrictAge, - }); - - // Confirm it - - await BankApi.confirmWithdrawalOperation(bank, user, wop); - - // We do *not* call runPending / runUntilDone on the wallet here. - // Some tests rely on the final withdraw failing. -} - export interface WithdrawViaBankResult { withdrawalFinishedCond: Promise<true>; } @@ -714,8 +560,10 @@ export async function withdrawViaBankV2( ): Promise<WithdrawViaBankResult> { const { walletClient: wallet, bank, exchange, amount } = p; - const user = await BankApi.createRandomBankUser(bank); - const wop = await BankAccessApi.createWithdrawalOperation(bank, user, amount); + const bankClient = new BankAccessApiClient(bank.bankAccessApiBaseUrl); + + const user = await bankClient.createRandomBankUser(); + const wop = await bankClient.createWithdrawalOperation(user.username, amount); // Hand it to the wallet @@ -744,140 +592,44 @@ export async function withdrawViaBankV2( // Confirm it - await BankApi.confirmWithdrawalOperation(bank, user, wop); + await bankClient.confirmWithdrawalOperation(user.username, wop); return { withdrawalFinishedCond, }; } -/** - * Withdraw balance. - * - * @deprecated use {@link withdrawViaBankV2 instead} - */ -export async function withdrawViaBank( - t: GlobalTestState, - p: { - wallet: WalletCli; - bank: BankService; - exchange: ExchangeServiceInterface; - amount: AmountString; - restrictAge?: number; - }, -): Promise<void> { - const { wallet } = p; - - await startWithdrawViaBank(t, p); - - await wallet.runUntilDone(); - - // Check balance - - await wallet.client.call(WalletApiOperation.GetBalances, {}); -} - -export async function applyTimeTravel( - timetravelDuration: Duration, +export async function applyTimeTravelV2( + timetravelOffsetMs: number, s: { exchange?: ExchangeService; merchant?: MerchantService; - wallet?: WalletCli; + walletClient?: WalletClient; }, ): Promise<void> { if (s.exchange) { await s.exchange.stop(); - s.exchange.setTimetravel(timetravelDuration); + s.exchange.setTimetravel(timetravelOffsetMs); await s.exchange.start(); await s.exchange.pingUntilAvailable(); } if (s.merchant) { await s.merchant.stop(); - s.merchant.setTimetravel(timetravelDuration); + s.merchant.setTimetravel(timetravelOffsetMs); await s.merchant.start(); await s.merchant.pingUntilAvailable(); } - if (s.wallet) { - s.wallet.setTimetravel(timetravelDuration); + if (s.walletClient) { + await s.walletClient.call(WalletApiOperation.TestingSetTimetravel, { + offsetMs: timetravelOffsetMs, + }); } } /** * Make a simple payment and check that it succeeded. - * - * @deprecated - */ -export async function makeTestPayment( - t: GlobalTestState, - args: { - merchant: MerchantServiceInterface; - wallet: WalletCli; - order: Partial<MerchantContractTerms>; - instance?: string; - }, - auth: WithAuthorization = {}, -): Promise<void> { - // Set up order. - - const { wallet, merchant } = args; - const instance = args.instance ?? "default"; - - const orderResp = await MerchantPrivateApi.createOrder( - merchant, - instance, - { - order: args.order, - }, - auth, - ); - - let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus( - merchant, - { - orderId: orderResp.order_id, - }, - auth, - ); - - t.assertTrue(orderStatus.order_status === "unpaid"); - - // Make wallet pay for the order - - const preparePayResult = await wallet.client.call( - WalletApiOperation.PreparePayForUri, - { - talerPayUri: orderStatus.taler_pay_uri, - }, - ); - - t.assertTrue( - preparePayResult.status === PreparePayResultType.PaymentPossible, - ); - - const r2 = await wallet.client.call(WalletApiOperation.ConfirmPay, { - proposalId: preparePayResult.proposalId, - }); - - t.assertTrue(r2.type === ConfirmPayResultType.Done); - - // Check if payment was successful. - - orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus( - merchant, - { - orderId: orderResp.order_id, - instance, - }, - auth, - ); - - t.assertTrue(orderStatus.order_status === "paid"); -} - -/** - * Make a simple payment and check that it succeeded. */ export async function makeTestPaymentV2( t: GlobalTestState, @@ -891,25 +643,19 @@ export async function makeTestPaymentV2( ): Promise<void> { // Set up order. - const { walletClient, merchant } = args; - const instance = args.instance ?? "default"; + const { walletClient, merchant, instance } = args; - const orderResp = await MerchantPrivateApi.createOrder( - merchant, - instance, - { - order: args.order, - }, - auth, + const merchantClient = new MerchantApiClient( + merchant.makeInstanceBaseUrl(instance), ); - let orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus( - merchant, - { - orderId: orderResp.order_id, - }, - auth, - ); + const orderResp = await merchantClient.createOrder({ + order: args.order, + }); + + let orderStatus = await merchantClient.queryPrivateOrderStatus({ + orderId: orderResp.order_id, + }); t.assertTrue(orderStatus.order_status === "unpaid"); @@ -934,14 +680,10 @@ export async function makeTestPaymentV2( // Check if payment was successful. - orderStatus = await MerchantPrivateApi.queryPrivateOrderStatus( - merchant, - { - orderId: orderResp.order_id, - instance, - }, - auth, - ); + orderStatus = await merchantClient.queryPrivateOrderStatus({ + orderId: orderResp.order_id, + instance, + }); t.assertTrue(orderStatus.order_status === "paid"); } diff --git a/packages/taler-harness/src/harness/libeufin-apis.ts b/packages/taler-harness/src/harness/libeufin-apis.ts index 3c57eee07..0193f9252 100644 --- a/packages/taler-harness/src/harness/libeufin-apis.ts +++ b/packages/taler-harness/src/harness/libeufin-apis.ts @@ -176,7 +176,7 @@ export interface LibeufinSandboxAddIncomingRequest { direction: string; } -const libeufinHttpLib = createPlatformHttpLib(); +const libeufinHarnessHttpLib = createPlatformHttpLib(); /** * APIs spread across Legacy and Access, it is therefore @@ -192,7 +192,7 @@ export namespace LibeufinSandboxApi { iban: string | null = null, ): Promise<void> { let url = new URL("testing/register", libeufinSandboxService.baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", body: { username: username, @@ -211,7 +211,7 @@ export namespace LibeufinSandboxApi { ): Promise<void> { // baseUrl should already be pointed to one demobank. let url = new URL("ebics/subscribers", libeufinSandboxService.baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", body: { userID: req.userID, @@ -228,7 +228,7 @@ export namespace LibeufinSandboxApi { ): Promise<void> { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL(`admin/ebics/hosts/${hostID}/rotate-keys`, baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", body: {}, }); @@ -239,7 +239,7 @@ export namespace LibeufinSandboxApi { ): Promise<void> { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL("admin/ebics/hosts", baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", body: { hostID, @@ -255,7 +255,7 @@ export namespace LibeufinSandboxApi { ): Promise<void> { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL(`admin/bank-accounts/${req.label}`, baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", body: req, headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, @@ -272,7 +272,7 @@ export namespace LibeufinSandboxApi { ): Promise<void> { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL("admin/ebics/subscribers", baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", body: req, headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, @@ -289,7 +289,7 @@ export namespace LibeufinSandboxApi { ): Promise<void> { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL("admin/ebics/bank-accounts", baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", body: req, headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, @@ -306,7 +306,7 @@ export namespace LibeufinSandboxApi { `admin/bank-accounts/${accountLabel}/simulate-incoming-transaction`, baseUrl, ); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", body: req, headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, @@ -322,7 +322,7 @@ export namespace LibeufinSandboxApi { `admin/bank-accounts/${accountLabel}/transactions`, baseUrl, ); - const res = await libeufinHttpLib.fetch(url.href, { + const res = await libeufinHarnessHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return (await res.json()) as SandboxAccountTransactions; @@ -334,7 +334,7 @@ export namespace LibeufinSandboxApi { ): Promise<any> { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL("admin/payments/camt", baseUrl); - return await libeufinHttpLib.fetch(url.href, { + return await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: { @@ -350,7 +350,7 @@ export namespace LibeufinSandboxApi { ): Promise<LibeufinSandboxAdminBankAccountBalance> { const baseUrl = libeufinSandboxService.baseUrl; let url = new URL(`admin/bank-accounts/${accountLabel}`, baseUrl); - const res = await libeufinHttpLib.fetch(url.href, { + const res = await libeufinHarnessHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return res.json(); @@ -362,7 +362,7 @@ export namespace LibeufinNexusApi { nexus: LibeufinNexusServiceInterface, ): Promise<NexusBankConnections> { let url = new URL("bank-connections", nexus.baseUrl); - const res = await libeufinHttpLib.fetch(url.href, { + const res = await libeufinHarnessHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return res.json(); @@ -374,7 +374,7 @@ export namespace LibeufinNexusApi { ): Promise<void> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL("bank-connections/delete-connection", baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: req, @@ -387,7 +387,7 @@ export namespace LibeufinNexusApi { ): Promise<void> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL("bank-connections", baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: { @@ -411,7 +411,7 @@ export namespace LibeufinNexusApi { ): Promise<any> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`bank-accounts/${accountName}`, baseUrl); - const resp = await libeufinHttpLib.fetch(url.href, { + const resp = await libeufinHarnessHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return resp.json(); @@ -427,7 +427,7 @@ export namespace LibeufinNexusApi { `bank-accounts/${accountName}/payment-initiations/${paymentId}/submit`, baseUrl, ); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: {}, @@ -443,7 +443,7 @@ export namespace LibeufinNexusApi { `bank-connections/${connectionName}/fetch-accounts`, baseUrl, ); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: {}, @@ -461,7 +461,7 @@ export namespace LibeufinNexusApi { `bank-connections/${connectionName}/import-account`, baseUrl, ); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: { @@ -477,7 +477,7 @@ export namespace LibeufinNexusApi { ): Promise<void> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`bank-connections/${connectionName}/connect`, baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: {}, @@ -495,7 +495,7 @@ export namespace LibeufinNexusApi { `/bank-accounts/${accountName}/payment-initiations`, baseUrl, ); - let response = await libeufinHttpLib.fetch(url.href, { + let response = await libeufinHarnessHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); const respJson = await response.json(); @@ -518,7 +518,7 @@ export namespace LibeufinNexusApi { for (const [k, v] of Object.entries(params)) { url.searchParams.set(k, String(v)); } - let response = await libeufinHttpLib.fetch(url.href, { + let response = await libeufinHarnessHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return response.json(); @@ -534,7 +534,7 @@ export namespace LibeufinNexusApi { ): Promise<LibeufinNexusTransactions> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/bank-accounts/${accountName}/transactions`, baseUrl); - let response = await libeufinHttpLib.fetch(url.href, { + let response = await libeufinHarnessHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return response.json(); @@ -553,7 +553,7 @@ export namespace LibeufinNexusApi { `/bank-accounts/${accountName}/fetch-transactions`, baseUrl, ); - const resp = await libeufinHttpLib.fetch(url.href, { + const resp = await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: { @@ -572,7 +572,7 @@ export namespace LibeufinNexusApi { ): Promise<void> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/users/${username}/password`, baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: req, @@ -585,7 +585,7 @@ export namespace LibeufinNexusApi { ): Promise<NexusUserResponse> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/user`, baseUrl); - const resp = await libeufinHttpLib.fetch(url.href, { + const resp = await libeufinHarnessHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return resp.json(); @@ -597,7 +597,7 @@ export namespace LibeufinNexusApi { ): Promise<void> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/users`, baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: req, @@ -609,7 +609,7 @@ export namespace LibeufinNexusApi { ): Promise<NexusGetPermissionsResponse> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/permissions`, baseUrl); - const resp = await libeufinHttpLib.fetch(url.href, { + const resp = await libeufinHarnessHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return resp.json(); @@ -621,7 +621,7 @@ export namespace LibeufinNexusApi { ): Promise<void> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/permissions`, baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: req, @@ -634,7 +634,7 @@ export namespace LibeufinNexusApi { ): Promise<NexusTaskCollection> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/bank-accounts/${bankAccountName}/schedule`, baseUrl); - const resp = await libeufinHttpLib.fetch(url.href, { + const resp = await libeufinHarnessHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return resp.json(); @@ -653,7 +653,7 @@ export namespace LibeufinNexusApi { baseUrl, ); if (taskName) url = new URL(taskName, `${url.href}/`); - const resp = await libeufinHttpLib.fetch(url.href, { + const resp = await libeufinHarnessHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); return resp.json(); @@ -669,7 +669,7 @@ export namespace LibeufinNexusApi { `/bank-accounts/${bankAccountName}/schedule/${taskName}`, baseUrl, ); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "DELETE", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); @@ -682,7 +682,7 @@ export namespace LibeufinNexusApi { ): Promise<void> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`/bank-accounts/${bankAccountName}/schedule`, baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: req, @@ -695,7 +695,7 @@ export namespace LibeufinNexusApi { ): Promise<void> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL(`facades/${facadeName}`, baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "DELETE", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); @@ -706,7 +706,7 @@ export namespace LibeufinNexusApi { ): Promise<NexusFacadeListResponse> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL("facades", baseUrl); - const resp = await libeufinHttpLib.fetch(url.href, { + const resp = await libeufinHarnessHttpLib.fetch(url.href, { headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, }); // FIXME: Just return validated, typed response here! @@ -719,7 +719,7 @@ export namespace LibeufinNexusApi { ): Promise<void> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL("facades", baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: { @@ -741,7 +741,7 @@ export namespace LibeufinNexusApi { ): Promise<void> { const baseUrl = libeufinNexusService.baseUrl; let url = new URL("facades", baseUrl); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: { @@ -766,7 +766,7 @@ export namespace LibeufinNexusApi { `/bank-accounts/${accountId}/submit-all-payment-initiations`, baseUrl, ); - await libeufinHttpLib.fetch(url.href, { + await libeufinHarnessHttpLib.fetch(url.href, { method: "POST", headers: { Authorization: makeBasicAuthHeader("admin", "secret") }, body: {}, |
