gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: testing bank api


From: gnunet
Subject: [taler-wallet-core] branch master updated: testing bank api
Date: Wed, 25 Oct 2023 20:44:21 +0200

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new c31ce9bab testing bank api
c31ce9bab is described below

commit c31ce9bab8592c31b7c7d879f6daad7886b7486d
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Wed Oct 25 15:44:14 2023 -0300

    testing bank api
---
 packages/demobank-ui/src/demobank-ui-settings.js   |   2 -
 packages/taler-util/src/http-client/bank-core.ts   | 594 ++++++++++++++++++++-
 .../taler-util/src/http-client/bank-integration.ts |  10 +-
 packages/taler-util/src/http-client/test.cli.ts    |  77 +++
 packages/taler-util/src/http-impl.node.ts          |  18 +-
 5 files changed, 687 insertions(+), 14 deletions(-)

diff --git a/packages/demobank-ui/src/demobank-ui-settings.js 
b/packages/demobank-ui/src/demobank-ui-settings.js
index 8a0961831..99c6f3873 100644
--- a/packages/demobank-ui/src/demobank-ui-settings.js
+++ b/packages/demobank-ui/src/demobank-ui-settings.js
@@ -3,8 +3,6 @@
 /**
  * Global settings for the demobank UI.
  */
-localStorage.setItem("bank-base-url", "http://bank.taler.test/";);
-
 globalThis.talerDemobankSettings = {
   backendBaseURL: "http://bank.taler.test/";,
   allowRegistrations: true,
diff --git a/packages/taler-util/src/http-client/bank-core.ts 
b/packages/taler-util/src/http-client/bank-core.ts
index b9cce6c72..de3622b8e 100644
--- a/packages/taler-util/src/http-client/bank-core.ts
+++ b/packages/taler-util/src/http-client/bank-core.ts
@@ -18,18 +18,25 @@ import {
   AmountJson,
   Amounts,
   HttpStatusCode,
-  LibtoolVersion
+  LibtoolVersion,
+  TalerError,
+  TalerErrorDetail,
+  encodeCrock,
+  getRandomBytes,
+  parsePaytoUri,
+  stringifyPaytoUri
 } from "@gnu-taler/taler-util";
 import {
   HttpRequestLibrary,
   createPlatformHttpLib
 } from "@gnu-taler/taler-util/http";
+import { setShowCurlRequest } from "../http-impl.node.js";
+import { TalerAuthenticationHttpClient } from "./authentication.js";
 import { TalerBankIntegrationHttpClient } from "./bank-integration.js";
 import { TalerRevenueHttpClient } from "./bank-revenue.js";
 import { TalerWireGatewayHttpClient } from "./bank-wire.js";
-import { AccessToken, OperationOk, PaginationParams, TalerCorebankApi, 
UserAndToken, codecForAccountData, codecForBankAccountCreateWithdrawalResponse, 
codecForBankAccountGetWithdrawalResponse, codecForBankAccountTransactionInfo, 
codecForBankAccountTransactionsResponse, codecForCashoutConversionResponse, 
codecForCashoutPending, codecForCashoutStatusResponse, codecForCashouts, 
codecForConversionRatesResponse, codecForCoreBankConfig, 
codecForGlobalCashouts, codecForListBankAccountsResponse, [...]
-import { addPaginationParams, opFixedSuccess, opEmptySuccess, opSuccess, 
opKnownFailure, makeBearerTokenAuthHeader, opUnknownFailure } from "./utils.js";
-import { TalerAuthenticationHttpClient } from "./authentication.js";
+import { AccessToken, OperationOk, OperationResult, PaginationParams, 
TalerCorebankApi, UserAndToken, codecForAccountData, 
codecForBankAccountCreateWithdrawalResponse, 
codecForBankAccountGetWithdrawalResponse, codecForBankAccountTransactionInfo, 
codecForBankAccountTransactionsResponse, codecForCashoutConversionResponse, 
codecForCashoutPending, codecForCashoutStatusResponse, codecForCashouts, 
codecForCoreBankConfig, codecForGlobalCashouts, 
codecForListBankAccountsResponse, codecForMonitor [...]
+import { addPaginationParams, makeBearerTokenAuthHeader, opEmptySuccess, 
opFixedSuccess, opKnownFailure, opSuccess, opUnknownFailure } from "./utils.js";
 
 
 type props = keyof TalerCoreBankHttpClient
@@ -134,7 +141,11 @@ export class TalerCoreBankHttpClient {
           return opKnownFailure("unauthorized", resp);
         }
       }
-      case HttpStatusCode.PreconditionFailed: return 
opKnownFailure("balance-not-zero", resp);
+      //FIXME: this should be forbidden
+      case HttpStatusCode.Unauthorized: {
+        return opKnownFailure("unauthorized", resp);
+      }
+      case HttpStatusCode.Conflict: return opKnownFailure("balance-not-zero", 
resp);
       default: return opUnknownFailure(resp, await resp.text())
     }
   }
@@ -155,7 +166,7 @@ export class TalerCoreBankHttpClient {
     switch (resp.status) {
       case HttpStatusCode.NoContent: return opEmptySuccess()
       case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp);
-      case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", 
resp);      
+      case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", 
resp);
       //FIXME: missing error code for cases:
       // * change legal name
       // * admin tries to change its own account
@@ -210,7 +221,7 @@ export class TalerCoreBankHttpClient {
    * https://docs.taler.net/core/api-corebank.html#get--accounts
    * 
    */
-  async getAccounts(auth: AccessToken, filter: {account? : string} = {}, 
pagination?: PaginationParams) {
+  async getAccounts(auth: AccessToken, filter: { account?: string } = {}, 
pagination?: PaginationParams) {
     const url = new URL(`accounts`, this.baseUrl);
     addPaginationParams(url, pagination)
     if (filter.account) {
@@ -226,7 +237,9 @@ export class TalerCoreBankHttpClient {
       case HttpStatusCode.Ok: return opSuccess(resp, 
codecForListBankAccountsResponse())
       case HttpStatusCode.NoContent: return opFixedSuccess({ accounts: [] })
       case HttpStatusCode.Forbidden: return opKnownFailure("no-rights", resp);
-      case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", 
resp);
+      case HttpStatusCode.Unauthorized: {
+        return opKnownFailure("unauthorized", resp);
+      }
       default: return opUnknownFailure(resp, await resp.text())
     }
   }
@@ -312,6 +325,7 @@ export class TalerCoreBankHttpClient {
       body,
     });
     switch (resp.status) {
+      //FIXME: return txid
       //FIXME: remove this after server has been updated 
       case HttpStatusCode.Ok: return opEmptySuccess()
       case HttpStatusCode.NoContent: return opEmptySuccess()
@@ -342,6 +356,7 @@ export class TalerCoreBankHttpClient {
     switch (resp.status) {
       case HttpStatusCode.Ok: return opSuccess(resp, 
codecForBankAccountCreateWithdrawalResponse())
       case HttpStatusCode.PreconditionFailed: return 
opKnownFailure("insufficient-funds", resp);
+      case HttpStatusCode.Unauthorized: return opKnownFailure("unauthorized", 
resp);
       //FIXME: remove when server is updated
       case HttpStatusCode.Forbidden: return 
opKnownFailure("insufficient-funds", resp);
       default: return opUnknownFailure(resp, await resp.text())
@@ -359,6 +374,7 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok: return opSuccess(resp, 
codecForBankAccountGetWithdrawalResponse())
+      case HttpStatusCode.BadRequest: return opKnownFailure("invalid-id", resp)
       case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp)
       default: return opUnknownFailure(resp, await resp.text())
     }
@@ -377,6 +393,8 @@ export class TalerCoreBankHttpClient {
       //FIXME: remove when the server is fixed
       case HttpStatusCode.Ok: return opEmptySuccess()
       case HttpStatusCode.NoContent: return opEmptySuccess()
+      case HttpStatusCode.BadRequest: return opKnownFailure("invalid-id", resp)
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp)
       case HttpStatusCode.Conflict: return 
opKnownFailure("previously-confirmed", resp);
       default: return opUnknownFailure(resp, await resp.text())
     }
@@ -395,6 +413,8 @@ export class TalerCoreBankHttpClient {
       //FIXME: remove when the server is fixed
       case HttpStatusCode.Ok: return opEmptySuccess()
       case HttpStatusCode.NoContent: return opEmptySuccess()
+      case HttpStatusCode.BadRequest: return opKnownFailure("invalid-id", resp)
+      case HttpStatusCode.NotFound: return opKnownFailure("not-found", resp)
       case HttpStatusCode.Conflict: return 
opKnownFailure("previously-aborted", resp);
       case HttpStatusCode.UnprocessableEntity: return 
opKnownFailure("no-exchange-or-reserve-selected", resp);
       default: return opUnknownFailure(resp, await resp.text())
@@ -570,7 +590,7 @@ export class TalerCoreBankHttpClient {
    * https://docs.taler.net/core/api-corebank.html#get--monitor
    * 
    */
-  async getMonitor(params: { timeframe?: 
TalerCorebankApi.MonitorTimeframeParam, which?: number }) {
+  async getMonitor(params: { timeframe?: 
TalerCorebankApi.MonitorTimeframeParam, which?: number } = {}) {
     const url = new URL(`monitor`, this.baseUrl);
     if (params.timeframe) {
       url.searchParams.set("timeframe", params.timeframe.toString())
@@ -630,4 +650,560 @@ export class TalerCoreBankHttpClient {
     const url = new URL(`accounts/${username}/`, this.baseUrl);
     return new TalerAuthenticationHttpClient(url.href, username, this.httpLib,)
   }
+
+  async testConfig() {
+    const config = await this.getConfig()
+    if (!this.isCompatible(config.body.version)) {
+      throw Error(`not compatible with server ${config.body.version}`)
+    }
+    return config.body
+  }
+
+  async testCashouts(adminPassword: string) {
+  }
+  async testMonitor(adminPassword: string) {
+    const { access_token: adminToken } = await succeedOrThrow(() =>
+      this.getAuthenticationAPI("admin").createAccessToken(adminPassword, {
+        scope: "readwrite"
+      })
+    )
+
+    await succeedOrThrow(() => (
+      this.getMonitor()
+    ))
+
+    await succeedOrThrow(() => (
+      this.getMonitor({
+        timeframe: TalerCorebankApi.MonitorTimeframeParam.day,
+        which: (new Date()).getDate() -1
+      })
+    ))
+  }
+
+  async testAccountManagement(adminPassword: string) {
+
+    const { access_token: adminToken } = await succeedOrThrow(() =>
+      this.getAuthenticationAPI("admin").createAccessToken(adminPassword, {
+        scope: "readwrite"
+      })
+    )
+
+    /**
+     * Create account
+    */
+    {
+      const username = "user-" + encodeCrock(getRandomBytes(10)).toLowerCase();
+
+      // await failOrThrow("invalid-input",() =>
+      //   this.createAccount(adminToken, {
+      //     name: username,
+      //     username, password: "123",
+      //     challenge_contact_data: {
+      //       email: "invalid email",
+      //       phone: "invalid phone",
+      //     }
+      //   })
+      // )
+
+      // await failOrThrow("unable-to-create",() =>
+      //   this.createAccount(adminToken, {
+      //     name: "admin",
+      //     username, password: "123"
+      //   })
+      // )
+
+      // await failOrThrow("unable-to-create",() =>
+      //   this.createAccount(adminToken, {
+      //     name: "bank",
+      //     username, password: "123"
+      //   })
+      // )
+
+      await succeedOrThrow(() =>
+        this.createAccount(adminToken, {
+          name: username,
+          username, password: "123"
+        })
+      )
+
+      await failOrThrow("already-exist", () =>
+        this.createAccount(adminToken, {
+          name: username,
+          username, password: "123"
+        })
+      );
+    }
+
+    /**
+     * Delete account
+     */
+    {
+      const { username, token } = await createRandomTestUser(this, adminToken)
+
+      await failOrThrow("not-found", () =>
+        this.deleteAccount({ username: "not-found", token: adminToken })
+      )
+      await failOrThrow("unable-to-delete", () =>
+        this.deleteAccount({ username: "admin", token: adminToken })
+      )
+      await failOrThrow("unable-to-delete", () =>
+        this.deleteAccount({ username: "bank", token: adminToken })
+      )
+
+      await failOrThrow("balance-not-zero", () =>
+        this.deleteAccount({ username, token: adminToken })
+      )
+
+      const userInfo = await succeedOrThrow(() =>
+        this.getAccount({ username, token })
+      )
+
+      const adminInfo = await succeedOrThrow(() =>
+        this.getAccount({ username: "admin", token: adminToken })
+      )
+
+      const adminAccount = parsePaytoUri(adminInfo.payto_uri)!
+      adminAccount.params["message"] = "all my money"
+      const withSubject = stringifyPaytoUri(adminAccount)
+
+      await succeedOrThrow(() =>
+        this.createTransaction({ username, token }, {
+          payto_uri: withSubject,
+          amount: userInfo.balance.amount
+        })
+      )
+
+
+      const otherUsername = "user-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
+
+      await succeedOrThrow(() =>
+        this.createAccount(adminToken, {
+          name: otherUsername,
+          username: otherUsername, password: "123"
+        })
+      )
+
+      await failOrThrow("unauthorized", () =>
+        this.deleteAccount({ username: otherUsername, token })
+      )
+
+      await succeedOrThrow(() =>
+        this.deleteAccount({ username, token: adminToken })
+      )
+    }
+
+    /**
+     * Update account
+     */
+    {
+      const { username, token } = await createRandomTestUser(this, adminToken)
+
+      await failOrThrow("cant-change-legal-name-or-admin", () =>
+        this.updateAccount({ username, token }, {
+          name: "something else",
+        })
+      )
+
+      // await failOrThrow("not-found", () =>
+      //   this.updateAccount({ username: "notfound", token }, {
+      //     challenge_contact_data: {
+      //       email: "asd@Aasd.com"
+      //     }
+      //   })
+      // )
+
+      await failOrThrow("unauthorized", () =>
+        this.updateAccount({ username: "notfound", token: "wrongtoken" as 
AccessToken }, {
+          challenge_contact_data: {
+            email: "asd@Aasd.com"
+          }
+        })
+      )
+
+      await succeedOrThrow(() =>
+        this.updateAccount({ username, token }, {
+          challenge_contact_data: {
+            email: "asd@Aasd.com"
+          }
+        })
+      )
+    }
+
+    /**
+     * Update password
+     */
+    {
+      const { username, token } = await createRandomTestUser(this, adminToken)
+
+      await succeedOrThrow(() =>
+        this.updatePassword({ username, token }, {
+          old_password: "123",
+          new_password: "234"
+        })
+      )
+      // await failOrThrow("not-found",() =>
+      //   this.updatePassword({ username:"notfound", token: userTempToken }, {
+      //     old_password: "123",
+      //     new_password: "234"
+      //   })
+      // )
+      await failOrThrow("unauthorized", () =>
+        this.updatePassword({ username: "admin", token }, {
+          old_password: "123",
+          new_password: "234"
+        })
+      )
+      // await failOrThrow("old-password-invalid-or-not-allowed",() =>
+      //   this.updatePassword({ username, token: userTempToken }, {
+      //     old_password: "123",
+      //     new_password: "234"
+      //   })
+      // )
+
+    }
+
+    /**
+     * public accounts
+     */
+    {
+      const acs = await succeedOrThrow(() => this.getPublicAccounts())
+
+    }
+    /**
+     * get accounts
+     */
+    {
+      const { username, token } = await createRandomTestUser(this, adminToken)
+      // await failOrThrow("no-rights",() => 
+      //   this.getAccounts(token)
+      // )
+      await failOrThrow("unauthorized", () =>
+        this.getAccounts("ASDASD" as AccessToken)
+      )
+
+      const acs = await succeedOrThrow(() =>
+        this.getAccounts(adminToken)
+      )
+    }
+
+  }
+
+  async testWithdrawals(adminPassword: string) {
+    const { access_token: adminToken } = await succeedOrThrow(() =>
+      this.getAuthenticationAPI("admin").createAccessToken(adminPassword, {
+        scope: "readwrite"
+      })
+    )
+    /**
+     * create withdrawals
+     */
+    {
+      const { username, token } = await createRandomTestUser(this, adminToken)
+
+      const userInfo = await succeedOrThrow(() =>
+        this.getAccount({ username, token })
+      )
+      
+      // FIXME: it shoulw warn about not enough balance
+      // const balance = Amounts.parseOrThrow(userInfo.balance.amount)
+      // const moreThanBalance = Amounts.stringify(Amounts.add(balance, 
balance).amount)
+      // setShowCurlRequest(true)
+      // await failOrThrow("insufficient-funds", () =>
+      //   this.createWithdrawal({ username, token }, {
+      //     amount: moreThanBalance
+      //   })
+      // )
+
+      await failOrThrow("unauthorized", () =>
+        this.createWithdrawal({ username, token: "wrongtoken" as AccessToken 
}, {
+          amount: userInfo.balance.amount
+        })
+      )
+
+      await succeedOrThrow(() =>
+        this.createWithdrawal({ username, token }, {
+          amount: userInfo.balance.amount
+        })
+      )
+    }
+
+    /**
+     * get withdrawal
+     */
+    {
+      const { username, token } = await createRandomTestUser(this, adminToken)
+
+      const userInfo = await succeedOrThrow(() =>
+        this.getAccount({ username, token })
+      )
+
+      const { withdrawal_id } = await succeedOrThrow(() =>
+        this.createWithdrawal({ username, token }, {
+          amount: userInfo.balance.amount
+        })
+      )
+
+      await succeedOrThrow(() =>
+        this.getWithdrawalById(withdrawal_id)
+      )
+
+      await failOrThrow("invalid-id", () =>
+        this.getWithdrawalById("invalid")
+      )
+      await failOrThrow("not-found", () =>
+        this.getWithdrawalById("11111111-1111-1111-1111-111111111111")
+      )
+    }
+
+    /**
+     * abort withdrawal
+     */
+    {
+      const { username:exchangeUser, token: exchangeToken } = await 
createRandomTestUser(this, adminToken, {is_taler_exchange: true})
+      const { username, token } = await createRandomTestUser(this, adminToken)
+
+      const userInfo = await succeedOrThrow(() =>
+        this.getAccount({ username, token })
+      )
+      const exchangeInfo = await succeedOrThrow(() =>
+        this.getAccount({ username:exchangeUser, token:exchangeToken })
+      )
+
+      await failOrThrow("invalid-id", () =>
+        this.abortWithdrawalById("invalid")
+      )
+      await failOrThrow("not-found", () =>
+        this.abortWithdrawalById("11111111-1111-1111-1111-111111111111")
+      )
+
+      const { withdrawal_id:firstWithdrawal } = await succeedOrThrow(() =>
+        this.createWithdrawal({ username, token }, {
+          amount: userInfo.balance.amount
+        })
+      )
+
+      await succeedOrThrow(() =>
+        this.abortWithdrawalById(firstWithdrawal)
+      )
+
+      const { taler_withdraw_uri: uri, withdrawal_id:secondWithdrawal } = 
await succeedOrThrow(() =>
+        this.createWithdrawal({ username, token }, {
+          amount: userInfo.balance.amount
+        })
+      )
+
+      await succeedOrThrow(() =>
+        
this.getIntegrationAPI().completeWithdrawalOperationById(secondWithdrawal, {
+          reserve_pub: encodeCrock(getRandomBytes(32)),
+          selected_exchange: exchangeInfo.payto_uri,
+        })
+      )
+      await succeedOrThrow(() =>
+        this.confirmWithdrawalById(secondWithdrawal)
+      )
+      await failOrThrow("previously-confirmed", () =>
+        this.abortWithdrawalById(secondWithdrawal)
+      ) 
+    }
+
+    /**
+     * confirm withdrawal
+     */
+    {
+      const { username:exchangeUser, token: exchangeToken } = await 
createRandomTestUser(this, adminToken, {is_taler_exchange: true})
+      const { username, token } = await createRandomTestUser(this, adminToken)
+
+      const userInfo = await succeedOrThrow(() =>
+        this.getAccount({ username, token })
+      )
+      const exchangeInfo = await succeedOrThrow(() =>
+        this.getAccount({ username:exchangeUser, token:exchangeToken })
+      )
+
+      await failOrThrow("invalid-id", () =>
+        this.confirmWithdrawalById("invalid")
+      )
+      await failOrThrow("not-found", () =>
+        this.confirmWithdrawalById("11111111-1111-1111-1111-111111111111")
+      )
+
+      const { withdrawal_id:firstWithdrawal } = await succeedOrThrow(() =>
+        this.createWithdrawal({ username, token }, {
+          amount: userInfo.balance.amount
+        })
+      )
+
+      await failOrThrow("no-exchange-or-reserve-selected", () =>
+        this.confirmWithdrawalById(firstWithdrawal)
+      )
+
+      await succeedOrThrow(() =>
+        
this.getIntegrationAPI().completeWithdrawalOperationById(firstWithdrawal, {
+          reserve_pub: encodeCrock(getRandomBytes(32)),
+          selected_exchange: exchangeInfo.payto_uri,
+        })
+      )
+
+      await succeedOrThrow(() =>
+        this.confirmWithdrawalById(firstWithdrawal)
+      )
+
+      const { withdrawal_id:secondWithdrawal } = await succeedOrThrow(() =>
+        this.createWithdrawal({ username, token }, {
+          amount: userInfo.balance.amount
+        })
+      )
+
+      await succeedOrThrow(() =>
+        this.abortWithdrawalById(secondWithdrawal)
+      )
+      await failOrThrow("previously-aborted", () =>
+        this.confirmWithdrawalById(secondWithdrawal)
+      ) 
+    }
+  }
+
+  async testTransactions(adminPassword: string) {
+    const { access_token: adminToken } = await succeedOrThrow(() =>
+      this.getAuthenticationAPI("admin").createAccessToken(adminPassword, {
+        scope: "readwrite"
+      })
+    )
+    // get transactions
+    {
+      const { username, token } = await createRandomTestUser(this, adminToken)
+      // await succeedOrThrow(() => this.getTransactions(creds))
+      const txs = await succeedOrThrow(() => this.getTransactions({ username, 
token }, {
+        limit: 5,
+        order: "asc"
+      }))
+      // await failOrThrow("not-found",() => this.getTransactions({
+      //   username:"not-found",
+      //   token: creds.token,
+      // }))
+      await failOrThrow("unauthorized", () => this.getTransactions({
+        username: username,
+        token: "wrongtoken" as AccessToken,
+      }))
+    }
+
+    /**
+     * getTxby id
+     */
+    {
+      const { username, token } = await createRandomTestUser(this, adminToken)
+      const { username: otherUser, token: otherToken } = await 
createRandomTestUser(this, adminToken)
+
+      const userInfo = await succeedOrThrow(() =>
+        this.getAccount({ username, token })
+      )
+      const otherInfo = await succeedOrThrow(() =>
+        this.getAccount({ username: otherUser, token: otherToken })
+      )
+      const otherAccount = parsePaytoUri(otherInfo.payto_uri)!
+      otherAccount.params["message"] = "all"
+
+      await succeedOrThrow(() =>
+        this.createTransaction({ username, token }, {
+          payto_uri: stringifyPaytoUri(otherAccount),
+          amount: userInfo.balance.amount
+        })
+      )
+
+      const txs = await succeedOrThrow(() => this.getTransactions({ username, 
token }, {
+        limit: 5,
+        order: "asc"
+      }))
+      const rowId = txs.transactions[0].row_id
+
+      await succeedOrThrow(() =>
+        this.getTransactionById({ username, token }, rowId)
+      )
+
+      await failOrThrow("not-found", () =>
+        this.getTransactionById({ username, token }, 123123123)
+      )
+
+      await failOrThrow("unauthorized", () =>
+        this.getTransactionById({ username, token: "wrongtoken" as AccessToken 
}, 123123123)
+      )
+    }
+
+    /**
+     * create transactions
+     */
+    {
+      const { username, token } = await createRandomTestUser(this, adminToken)
+      const { username: otherUser, token: otherToken } = await 
createRandomTestUser(this, adminToken)
+
+      const userInfo = await succeedOrThrow(() =>
+        this.getAccount({ username, token })
+      )
+      const otherInfo = await succeedOrThrow(() =>
+        this.getAccount({ username: otherUser, token: otherToken })
+      )
+      const otherAccount = parsePaytoUri(otherInfo.payto_uri)!
+      otherAccount.params["message"] = "all"
+
+      await succeedOrThrow(() =>
+        this.createTransaction({ username, token }, {
+          payto_uri: stringifyPaytoUri(otherAccount),
+          amount: userInfo.balance.amount
+        })
+      )
+      //missing amount
+      await failOrThrow("invalid-input", () =>
+        this.createTransaction({ username, token }, {
+          payto_uri: stringifyPaytoUri(otherAccount),
+          // amount: userInfo.balance.amount
+        })
+      )
+      //missing subject
+      await failOrThrow("invalid-input", () =>
+        this.createTransaction({ username, token }, {
+          payto_uri: otherInfo.payto_uri,
+          amount: userInfo.balance.amount
+        })
+      )
+      await failOrThrow("unauthorized", () =>
+        this.createTransaction({ username, token: "wrongtoken" as AccessToken 
}, {
+          payto_uri: otherInfo.payto_uri,
+          amount: userInfo.balance.amount
+        })
+      )
+    }
+  }
 }
+
+export async function succeedOrThrow<R, E>(cb: () => 
Promise<OperationResult<R, E>>): Promise<R> {
+  const resp = await cb()
+  if (resp.type === "ok") return resp.body
+  throw TalerError.fromUncheckedDetail({ ...resp.detail, case: resp.case })
+}
+export async function failOrThrow<E>(s: E, cb: () => 
Promise<OperationResult<unknown, E>>): Promise<TalerErrorDetail> {
+  const resp = await cb()
+  if (resp.type === "ok") {
+    throw TalerError.fromException(new Error(`request succeed but failure 
"${s}" was expected`))
+  }
+  if (resp.case === s) {
+    return resp.detail
+  }
+  throw TalerError.fromException(new Error(`request failed but case "${s}" was 
expected`))
+}
+
+export async function createRandomTestUser(api: TalerCoreBankHttpClient, 
adminToken: AccessToken, options: 
Partial<TalerCorebankApi.RegisterAccountRequest> = {}) {
+  const username = "user-" + encodeCrock(getRandomBytes(10)).toLowerCase();
+  await succeedOrThrow(() =>
+    api.createAccount(adminToken, {
+      name: username,
+      username, password: "123",
+      ...options
+    })
+  )
+  const { access_token } = await succeedOrThrow(() =>
+    api.getAuthenticationAPI(username).createAccessToken("123", {
+      scope: "readwrite"
+    })
+  )
+  return { username, token: access_token }
+}
\ No newline at end of file
diff --git a/packages/taler-util/src/http-client/bank-integration.ts 
b/packages/taler-util/src/http-client/bank-integration.ts
index 521b6e34c..887dbed1b 100644
--- a/packages/taler-util/src/http-client/bank-integration.ts
+++ b/packages/taler-util/src/http-client/bank-integration.ts
@@ -1,10 +1,12 @@
 import { HttpRequestLibrary, readSuccessResponseJsonOrThrow } from 
"../http-common.js";
+import { HttpStatusCode } from "../http-status-codes.js";
 import { createPlatformHttpLib } from "../http.js";
 import {
   TalerBankIntegrationApi,
   codecForBankWithdrawalOperationPostResponse,
   codecForBankWithdrawalOperationStatus
 } from "./types.js";
+import { opSuccess, opUnknownFailure } from "./utils.js";
 
 export class TalerBankIntegrationHttpClient {
   httpLib: HttpRequestLibrary;
@@ -35,12 +37,16 @@ export class TalerBankIntegrationHttpClient {
    * 
https://docs.taler.net/core/api-bank-integration.html#post-$BANK_API_BASE_URL-withdrawal-operation-$wopid
    * 
    */
-  async completeWithdrawalOperationById(woid: string): 
Promise<TalerBankIntegrationApi.BankWithdrawalOperationPostResponse> {
+  async completeWithdrawalOperationById(woid: string, body: 
TalerBankIntegrationApi.BankWithdrawalOperationPostRequest) {
     const url = new URL(`withdrawal-operation/${woid}`, this.baseUrl);
     const resp = await this.httpLib.fetch(url.href, {
       method: "POST",
+      body,
     });
-    return readSuccessResponseJsonOrThrow(resp, 
codecForBankWithdrawalOperationPostResponse());
+    switch (resp.status) {
+      case HttpStatusCode.Ok: return opSuccess(resp, 
codecForBankWithdrawalOperationPostResponse())
+      default: return opUnknownFailure(resp, await resp.text())
+    }
   }
 
 }
diff --git a/packages/taler-util/src/http-client/test.cli.ts 
b/packages/taler-util/src/http-client/test.cli.ts
new file mode 100644
index 000000000..08a0c5fd3
--- /dev/null
+++ b/packages/taler-util/src/http-client/test.cli.ts
@@ -0,0 +1,77 @@
+import { HttpLibImpl, setShowCurlRequest as setPrintHttpRequestAsCurl } from 
"../http-impl.node.js"
+import { AccessToken, TalerError } from "../index.js"
+import { TalerCoreBankHttpClient, createRandomTestUser, succeedOrThrow } from 
"./bank-core.js"
+
+
+const baseUrl = process.argv[2]
+const admin = process.argv[3] as AccessToken
+// const usrpwd = process.argv[4]
+
+if (!baseUrl) {
+  console.error("missing baseUrl")
+  process.exit(1)
+}
+
+console.log("trying against ", baseUrl)
+
+const api = new TalerCoreBankHttpClient(baseUrl, new HttpLibImpl())
+try {
+  process.stdout.write("config: ");
+  const config = await api.testConfig()
+  console.log("ok")
+
+  setPrintHttpRequestAsCurl(true)
+
+  process.stdout.write("account management: ");
+  const withAdmin = !!admin && admin !== "-"
+  if (withAdmin) {
+    await api.testAccountManagement(admin)
+    console.log("ok")
+  } else {
+    console.log("skipped")
+  }
+
+  process.stdout.write("transactions: ");
+  if (withAdmin) {
+    await api.testTransactions(admin)
+    console.log("ok")
+  } else {
+    console.log("skipped")
+  }
+  
+  process.stdout.write("withdrawals: ");
+  if (withAdmin) {
+    await api.testWithdrawals(admin)
+    console.log("ok")
+  } else {
+    console.log("skipped")
+  }
+
+  process.stdout.write("monitor: ");
+  if (withAdmin && config.have_cashout) {
+    await api.testMonitor(admin)
+    console.log("ok")
+  } else {
+    console.log("skipped")
+  }
+
+  process.stdout.write("cashout: ");
+  if (withAdmin && config.have_cashout) {
+    await api.testCashouts(admin)
+    console.log("ok")
+  } else {
+    console.log("skipped")
+  }
+
+} catch (e: any) {
+  console.log("")
+  if (e instanceof TalerError) {
+    console.error("FAILED", JSON.stringify(e.errorDetail, undefined, 2))
+    console.error(e.stack)
+  } else if (e instanceof Error) {
+    console.error(`FAILED: ${e.message}`)
+    console.error(e.stack)
+  } else {
+    console.error(`FAILED: ${e}`)
+  }
+}
diff --git a/packages/taler-util/src/http-impl.node.ts 
b/packages/taler-util/src/http-impl.node.ts
index 528d303be..11ae9480c 100644
--- a/packages/taler-util/src/http-impl.node.ts
+++ b/packages/taler-util/src/http-impl.node.ts
@@ -56,6 +56,10 @@ if (
 const logger = new Logger("http-impl.node.ts");
 
 const textDecoder = new TextDecoder();
+let SHOW_CURL_HTTP_REQUEST = false;
+export function setShowCurlRequest(b: boolean) {
+  SHOW_CURL_HTTP_REQUEST = b
+}
 
 /**
  * Implementation of the HTTP request library interface for node.
@@ -115,7 +119,7 @@ export class HttpLibImpl implements HttpRequestLibrary {
 
     let reqBody: ArrayBuffer | undefined;
 
-    if (opt?.method == "POST") {
+    if (opt?.method == "POST" || opt?.method == "PATCH" || opt?.method == 
"PUT") {
       reqBody = encodeBody(opt.body);
     }
 
@@ -145,6 +149,18 @@ export class HttpLibImpl implements HttpRequestLibrary {
 
     const chunks: Uint8Array[] = [];
 
+    if (SHOW_CURL_HTTP_REQUEST) {
+      const payload = !reqBody || reqBody.byteLength === 0 ? undefined : 
textDecoder.decode(reqBody)
+      const headers = Object.entries(requestHeadersMap).reduce((prev, [key, 
value]) => {
+        return `${prev} -H "${key}: ${value}"`
+      }, "")
+      function ifUndefined<T>(arg: string, v: undefined | T): string {
+        if (v === undefined) return ""
+        return arg + " '" + String(v) + "'"
+      }
+      console.log(`curl -X ${options.method} ${parsedUrl.href} 
${ifUndefined("-d", payload)} ${headers}`)
+    }
+
     return new Promise((resolve, reject) => {
       const handler = (res: http.IncomingMessage) => {
         res.on("data", (d) => {

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]