gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: moving cli test to harness


From: gnunet
Subject: [taler-wallet-core] branch master updated: moving cli test to harness
Date: Thu, 26 Oct 2023 16:15:54 +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 a4c7bc4b2 moving cli test to harness
a4c7bc4b2 is described below

commit a4c7bc4b284fe7dc4c65ceaad96fc67c40c9a708
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Thu Oct 26 11:15:45 2023 -0300

    moving cli test to harness
---
 .../taler-harness/src/http-client/bank-core.ts     | 547 +++++++++++++++++++
 packages/taler-harness/src/index.ts                |  95 ++--
 .../taler-util/src/http-client/authentication.ts   |   5 +-
 packages/taler-util/src/http-client/bank-core.ts   | 580 +--------------------
 .../taler-util/src/http-client/bank-integration.ts |   2 +-
 packages/taler-util/src/http-client/test.cli.ts    |  77 ---
 packages/taler-util/src/http-client/types.ts       |  21 -
 packages/taler-util/src/http-client/utils.ts       |  35 +-
 packages/taler-util/src/http-impl.node.ts          |   2 +-
 packages/taler-util/src/index.ts                   |   1 +
 packages/taler-util/src/operation.ts               |  74 +++
 11 files changed, 691 insertions(+), 748 deletions(-)

diff --git a/packages/taler-harness/src/http-client/bank-core.ts 
b/packages/taler-harness/src/http-client/bank-core.ts
new file mode 100644
index 000000000..c67ff1bf8
--- /dev/null
+++ b/packages/taler-harness/src/http-client/bank-core.ts
@@ -0,0 +1,547 @@
+import { AccessToken, Amounts, TalerCoreBankHttpClient, TalerCorebankApi, 
encodeCrock, failOrThrow, getRandomBytes, parsePaytoUri, stringifyPaytoUri, 
succeedOrThrow } from "@gnu-taler/taler-util"
+
+export class BankCoreSmokeTest {
+  constructor(readonly api:TalerCoreBankHttpClient) {
+
+  }
+
+async testConfig() {
+  const config = await this.api.getConfig()
+  if (!this.api.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.api.getAuthenticationAPI("admin").createAccessToken(adminPassword, {
+      scope: "readwrite"
+    })
+  )
+
+  await succeedOrThrow(() => (
+    this.api.getMonitor()
+  ))
+
+  await succeedOrThrow(() => (
+    this.api.getMonitor({
+      timeframe: TalerCorebankApi.MonitorTimeframeParam.day,
+      which: (new Date()).getDate() -1
+    })
+  ))
+}
+
+async testAccountManagement(adminPassword: string) {
+
+  const { access_token: adminToken } = await succeedOrThrow(() =>
+    this.api.getAuthenticationAPI("admin").createAccessToken(adminPassword, {
+      scope: "readwrite"
+    })
+  )
+
+  /**
+   * Create account
+  */
+  {
+    const username = "user-" + encodeCrock(getRandomBytes(10)).toLowerCase();
+
+    // await failOrThrow("invalid-input",() =>
+    //   this.api.createAccount(adminToken, {
+    //     name: username,
+    //     username, password: "123",
+    //     challenge_contact_data: {
+    //       email: "invalid email",
+    //       phone: "invalid phone",
+    //     }
+    //   })
+    // )
+
+    // await failOrThrow("unable-to-create",() =>
+    //   this.api.createAccount(adminToken, {
+    //     name: "admin",
+    //     username, password: "123"
+    //   })
+    // )
+
+    // await failOrThrow("unable-to-create",() =>
+    //   this.api.createAccount(adminToken, {
+    //     name: "bank",
+    //     username, password: "123"
+    //   })
+    // )
+
+    await succeedOrThrow(() =>
+      this.api.createAccount(adminToken, {
+        name: username,
+        username, password: "123"
+      })
+    )
+
+    await failOrThrow("already-exist", () =>
+      this.api.createAccount(adminToken, {
+        name: username,
+        username, password: "123"
+      })
+    );
+  }
+
+  /**
+   * Delete account
+   */
+  {
+    const { username, token } = await createRandomTestUser(this.api, 
adminToken)
+
+    await failOrThrow("not-found", () =>
+      this.api.deleteAccount({ username: "not-found", token: adminToken })
+    )
+    await failOrThrow("unable-to-delete", () =>
+      this.api.deleteAccount({ username: "admin", token: adminToken })
+    )
+    await failOrThrow("unable-to-delete", () =>
+      this.api.deleteAccount({ username: "bank", token: adminToken })
+    )
+
+    await failOrThrow("balance-not-zero", () =>
+      this.api.deleteAccount({ username, token: adminToken })
+    )
+
+    const userInfo = await succeedOrThrow(() =>
+      this.api.getAccount({ username, token })
+    )
+
+    const adminInfo = await succeedOrThrow(() =>
+      this.api.getAccount({ username: "admin", token: adminToken })
+    )
+
+    const adminAccount = parsePaytoUri(adminInfo.payto_uri)!
+    adminAccount.params["message"] = "all my money"
+    const withSubject = stringifyPaytoUri(adminAccount)
+
+    await succeedOrThrow(() =>
+      this.api.createTransaction({ username, token }, {
+        payto_uri: withSubject,
+        amount: userInfo.balance.amount
+      })
+    )
+
+
+    const otherUsername = "user-" + 
encodeCrock(getRandomBytes(10)).toLowerCase();
+
+    await succeedOrThrow(() =>
+      this.api.createAccount(adminToken, {
+        name: otherUsername,
+        username: otherUsername, password: "123"
+      })
+    )
+
+    await failOrThrow("unauthorized", () =>
+      this.api.deleteAccount({ username: otherUsername, token })
+    )
+
+    await succeedOrThrow(() =>
+      this.api.deleteAccount({ username, token: adminToken })
+    )
+  }
+
+  /**
+   * Update account
+   */
+  {
+    const { username, token } = await createRandomTestUser(this.api, 
adminToken)
+
+    await failOrThrow("cant-change-legal-name-or-admin", () =>
+      this.api.updateAccount({ username, token }, {
+        name: "something else",
+      })
+    )
+
+    // await failOrThrow("not-found", () =>
+    //   this.api.updateAccount({ username: "notfound", token }, {
+    //     challenge_contact_data: {
+    //       email: "asd@Aasd.com"
+    //     }
+    //   })
+    // )
+
+    await failOrThrow("unauthorized", () =>
+      this.api.updateAccount({ username: "notfound", token: "wrongtoken" as 
AccessToken }, {
+        challenge_contact_data: {
+          email: "asd@Aasd.com"
+        }
+      })
+    )
+
+    await succeedOrThrow(() =>
+      this.api.updateAccount({ username, token }, {
+        challenge_contact_data: {
+          email: "asd@Aasd.com"
+        }
+      })
+    )
+  }
+
+  /**
+   * Update password
+   */
+  {
+    const { username, token } = await createRandomTestUser(this.api, 
adminToken)
+
+    await succeedOrThrow(() =>
+      this.api.updatePassword({ username, token }, {
+        old_password: "123",
+        new_password: "234"
+      })
+    )
+    // await failOrThrow("not-found",() =>
+    //   this.api.updatePassword({ username:"notfound", token: userTempToken 
}, {
+    //     old_password: "123",
+    //     new_password: "234"
+    //   })
+    // )
+    await failOrThrow("unauthorized", () =>
+      this.api.updatePassword({ username: "admin", token }, {
+        old_password: "123",
+        new_password: "234"
+      })
+    )
+    // await failOrThrow("old-password-invalid-or-not-allowed",() =>
+    //   this.api.updatePassword({ username, token: userTempToken }, {
+    //     old_password: "123",
+    //     new_password: "234"
+    //   })
+    // )
+
+  }
+
+  /**
+   * public accounts
+   */
+  {
+    const acs = await succeedOrThrow(() => this.api.getPublicAccounts())
+
+  }
+  /**
+   * get accounts
+   */
+  {
+    const { username, token } = await createRandomTestUser(this.api, 
adminToken)
+    // await failOrThrow("no-rights",() => 
+    //   this.api.getAccounts(token)
+    // )
+    await failOrThrow("unauthorized", () =>
+      this.api.getAccounts("ASDASD" as AccessToken)
+    )
+
+    const acs = await succeedOrThrow(() =>
+      this.api.getAccounts(adminToken)
+    )
+  }
+
+}
+
+async testWithdrawals(adminPassword: string) {
+  const { access_token: adminToken } = await succeedOrThrow(() =>
+    this.api.getAuthenticationAPI("admin").createAccessToken(adminPassword, {
+      scope: "readwrite"
+    })
+  )
+  /**
+   * create withdrawals
+   */
+  {
+    const { username, token } = await createRandomTestUser(this.api, 
adminToken)
+
+    const userInfo = await succeedOrThrow(() =>
+      this.api.getAccount({ username, token })
+    )
+    
+    const balance = Amounts.parseOrThrow(userInfo.balance.amount)
+    const moreThanBalance = Amounts.stringify(Amounts.mult(balance, 5).amount)
+    await failOrThrow("insufficient-funds", () =>
+      this.api.createWithdrawal({ username, token }, {
+        amount: moreThanBalance
+      })
+    )
+
+    await failOrThrow("unauthorized", () =>
+      this.api.createWithdrawal({ username, token: "wrongtoken" as AccessToken 
}, {
+        amount: userInfo.balance.amount
+      })
+    )
+
+    await succeedOrThrow(() =>
+      this.api.createWithdrawal({ username, token }, {
+        amount: userInfo.balance.amount
+      })
+    )
+  }
+
+  /**
+   * get withdrawal
+   */
+  {
+    const { username, token } = await createRandomTestUser(this.api, 
adminToken)
+
+    const userInfo = await succeedOrThrow(() =>
+      this.api.getAccount({ username, token })
+    )
+
+    const { withdrawal_id } = await succeedOrThrow(() =>
+      this.api.createWithdrawal({ username, token }, {
+        amount: userInfo.balance.amount
+      })
+    )
+
+    await succeedOrThrow(() =>
+      this.api.getWithdrawalById(withdrawal_id)
+    )
+
+    await failOrThrow("invalid-id", () =>
+      this.api.getWithdrawalById("invalid")
+    )
+    await failOrThrow("not-found", () =>
+      this.api.getWithdrawalById("11111111-1111-1111-1111-111111111111")
+    )
+  }
+
+  /**
+   * abort withdrawal
+   */
+  {
+    const { username:exchangeUser, token: exchangeToken } = await 
createRandomTestUser(this.api, adminToken, {is_taler_exchange: true})
+    const { username, token } = await createRandomTestUser(this.api, 
adminToken)
+
+    const userInfo = await succeedOrThrow(() =>
+      this.api.getAccount({ username, token })
+    )
+    const exchangeInfo = await succeedOrThrow(() =>
+      this.api.getAccount({ username:exchangeUser, token:exchangeToken })
+    )
+
+    await failOrThrow("invalid-id", () =>
+      this.api.abortWithdrawalById("invalid")
+    )
+    await failOrThrow("not-found", () =>
+      this.api.abortWithdrawalById("11111111-1111-1111-1111-111111111111")
+    )
+
+    const { withdrawal_id:firstWithdrawal } = await succeedOrThrow(() =>
+      this.api.createWithdrawal({ username, token }, {
+        amount: userInfo.balance.amount
+      })
+    )
+
+    await succeedOrThrow(() =>
+      this.api.abortWithdrawalById(firstWithdrawal)
+    )
+
+    const { taler_withdraw_uri: uri, withdrawal_id:secondWithdrawal } = await 
succeedOrThrow(() =>
+      this.api.createWithdrawal({ username, token }, {
+        amount: userInfo.balance.amount
+      })
+    )
+
+    await succeedOrThrow(() =>
+      
this.api.getIntegrationAPI().completeWithdrawalOperationById(secondWithdrawal, {
+        reserve_pub: encodeCrock(getRandomBytes(32)),
+        selected_exchange: exchangeInfo.payto_uri,
+      })
+    )
+    await succeedOrThrow(() =>
+      this.api.confirmWithdrawalById(secondWithdrawal)
+    )
+    await failOrThrow("previously-confirmed", () =>
+      this.api.abortWithdrawalById(secondWithdrawal)
+    ) 
+  }
+
+  /**
+   * confirm withdrawal
+   */
+  {
+    const { username:exchangeUser, token: exchangeToken } = await 
createRandomTestUser(this.api, adminToken, {is_taler_exchange: true})
+    const { username, token } = await createRandomTestUser(this.api, 
adminToken)
+
+    const userInfo = await succeedOrThrow(() =>
+      this.api.getAccount({ username, token })
+    )
+    const exchangeInfo = await succeedOrThrow(() =>
+      this.api.getAccount({ username:exchangeUser, token:exchangeToken })
+    )
+
+    await failOrThrow("invalid-id", () =>
+      this.api.confirmWithdrawalById("invalid")
+    )
+    await failOrThrow("not-found", () =>
+      this.api.confirmWithdrawalById("11111111-1111-1111-1111-111111111111")
+    )
+
+    const { withdrawal_id:firstWithdrawal } = await succeedOrThrow(() =>
+      this.api.createWithdrawal({ username, token }, {
+        amount: userInfo.balance.amount
+      })
+    )
+
+    await failOrThrow("no-exchange-or-reserve-selected", () =>
+      this.api.confirmWithdrawalById(firstWithdrawal)
+    )
+
+    await succeedOrThrow(() =>
+      
this.api.getIntegrationAPI().completeWithdrawalOperationById(firstWithdrawal, {
+        reserve_pub: encodeCrock(getRandomBytes(32)),
+        selected_exchange: exchangeInfo.payto_uri,
+      })
+    )
+
+    await succeedOrThrow(() =>
+      this.api.confirmWithdrawalById(firstWithdrawal)
+    )
+
+    const { withdrawal_id:secondWithdrawal } = await succeedOrThrow(() =>
+      this.api.createWithdrawal({ username, token }, {
+        amount: userInfo.balance.amount
+      })
+    )
+
+    await succeedOrThrow(() =>
+      this.api.abortWithdrawalById(secondWithdrawal)
+    )
+    await failOrThrow("previously-aborted", () =>
+      this.api.confirmWithdrawalById(secondWithdrawal)
+    ) 
+  }
+}
+
+async testTransactions(adminPassword: string) {
+  const { access_token: adminToken } = await succeedOrThrow(() =>
+    this.api.getAuthenticationAPI("admin").createAccessToken(adminPassword, {
+      scope: "readwrite"
+    })
+  )
+  // get transactions
+  {
+    const { username, token } = await createRandomTestUser(this.api, 
adminToken)
+    // await succeedOrThrow(() => this.api.getTransactions(creds))
+    const txs = await succeedOrThrow(() => this.api.getTransactions({ 
username, token }, {
+      limit: 5,
+      order: "asc"
+    }))
+    // await failOrThrow("not-found",() => this.api.getTransactions({
+    //   username:"not-found",
+    //   token: creds.token,
+    // }))
+    await failOrThrow("unauthorized", () => this.api.getTransactions({
+      username: username,
+      token: "wrongtoken" as AccessToken,
+    }))
+  }
+
+  /**
+   * getTxby id
+   */
+  {
+    const { username, token } = await createRandomTestUser(this.api, 
adminToken)
+    const { username: otherUser, token: otherToken } = await 
createRandomTestUser(this.api, adminToken)
+
+    const userInfo = await succeedOrThrow(() =>
+      this.api.getAccount({ username, token })
+    )
+    const otherInfo = await succeedOrThrow(() =>
+      this.api.getAccount({ username: otherUser, token: otherToken })
+    )
+    const otherAccount = parsePaytoUri(otherInfo.payto_uri)!
+    otherAccount.params["message"] = "all"
+
+    await succeedOrThrow(() =>
+      this.api.createTransaction({ username, token }, {
+        payto_uri: stringifyPaytoUri(otherAccount),
+        amount: userInfo.balance.amount
+      })
+    )
+
+    const txs = await succeedOrThrow(() => this.api.getTransactions({ 
username, token }, {
+      limit: 5,
+      order: "asc"
+    }))
+    const rowId = txs.transactions[0].row_id
+
+    await succeedOrThrow(() =>
+      this.api.getTransactionById({ username, token }, rowId)
+    )
+
+    await failOrThrow("not-found", () =>
+      this.api.getTransactionById({ username, token }, 123123123)
+    )
+
+    await failOrThrow("unauthorized", () =>
+      this.api.getTransactionById({ username, token: "wrongtoken" as 
AccessToken }, 123123123)
+    )
+  }
+
+  /**
+   * create transactions
+   */
+  {
+    const { username, token } = await createRandomTestUser(this.api, 
adminToken)
+    const { username: otherUser, token: otherToken } = await 
createRandomTestUser(this.api, adminToken)
+
+    const userInfo = await succeedOrThrow(() =>
+      this.api.getAccount({ username, token })
+    )
+    const otherInfo = await succeedOrThrow(() =>
+      this.api.getAccount({ username: otherUser, token: otherToken })
+    )
+    const otherAccount = parsePaytoUri(otherInfo.payto_uri)!
+    otherAccount.params["message"] = "all"
+
+    await succeedOrThrow(() =>
+      this.api.createTransaction({ username, token }, {
+        payto_uri: stringifyPaytoUri(otherAccount),
+        amount: userInfo.balance.amount
+      })
+    )
+    //missing amount
+    await failOrThrow("invalid-input", () =>
+      this.api.createTransaction({ username, token }, {
+        payto_uri: stringifyPaytoUri(otherAccount),
+        // amount: userInfo.balance.amount
+      })
+    )
+    //missing subject
+    await failOrThrow("invalid-input", () =>
+      this.api.createTransaction({ username, token }, {
+        payto_uri: otherInfo.payto_uri,
+        amount: userInfo.balance.amount
+      })
+    )
+    await failOrThrow("unauthorized", () =>
+      this.api.createTransaction({ username, token: "wrongtoken" as 
AccessToken }, {
+        payto_uri: otherInfo.payto_uri,
+        amount: userInfo.balance.amount
+      })
+    )
+  }
+}
+
+
+}
+
+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 }
+}
diff --git a/packages/taler-harness/src/index.ts 
b/packages/taler-harness/src/index.ts
index 0f93abdbe..c83457be4 100644
--- a/packages/taler-harness/src/index.ts
+++ b/packages/taler-harness/src/index.ts
@@ -31,6 +31,7 @@ import {
   RegisterAccountRequest,
   TalerCoreBankHttpClient,
   TalerCorebankApiClient,
+  TalerError,
   addPaytoQueryParams,
   decodeCrock,
   encodeCrock,
@@ -67,6 +68,7 @@ import {
 } from "./harness/harness.js";
 import { getTestInfo, runTests } from "./integrationtests/testrunner.js";
 import { lintExchangeDeployment } from "./lint.js";
+import { BankCoreSmokeTest } from "http-client/bank-core.js";
 
 const logger = new Logger("taler-harness:index.ts");
 
@@ -657,7 +659,7 @@ deploymentCli
     process.exit(2);
   });
 
-  deploymentCli
+deploymentCli
   .subcommand("testBankAPI", "test-bank-api", {
     help: "test api compatibility.",
   })
@@ -666,53 +668,66 @@ deploymentCli
   .action(async (args) => {
     const httpLib = createPlatformHttpLib();
     const api = new 
TalerCoreBankHttpClient(args.testBankAPI.corebankApiBaseUrl, httpLib);
-    
-    {
-      logger.info("compatibility")
-      const resp = await api.getConfig()
-      if (!LibtoolVersion.compare(resp.body.version, 
api.PROTOCOL_VERSION)?.compatible) {
-        logger.error("The API client is not compatible with the server", 
api.PROTOCOL_VERSION, resp.body.version)
-        process.exit(1)
+
+    const tester = new BankCoreSmokeTest(api)
+    try {
+      process.stdout.write("config: ");
+      const config = await tester.testConfig()
+      console.log("ok")
+      const admin = args.testBankAPI.adminPwd
+      process.stdout.write("account management: ");
+      const withAdmin = !!admin && admin !== "-"
+      if (withAdmin) {
+        await tester.testAccountManagement(admin)
+        console.log("ok")
+      } else {
+        console.log("skipped")
       }
-    }
-    if (!args.testBankAPI.adminPwd) {
-      logger.info("test completed")
-      process.exit(0)
-    }
 
-    let token: AccessToken;
-    {
-      logger.info("login admin")
-      const resp = await 
api.getAuthenticationAPI("admin").createAccessToken(args.testBankAPI.adminPwd, {
-        scope: "readwrite"
-      })
-      if (resp.type === "fail") {
-        logger.error("login failed", resp.detail)
-        process.exit(1)
+      process.stdout.write("transactions: ");
+      if (withAdmin) {
+        await tester.testTransactions(admin)
+        console.log("ok")
+      } else {
+        console.log("skipped")
       }
-      token = resp.body.access_token;
-    }
 
-    logger.info("account management")
-    const username = "user-" + encodeCrock(getRandomBytes(10)).toLowerCase();
-    {
-      const resp = await api.createAccount(token, { name: username, password: 
"123", username })
-      if (resp.type === "fail") {
-        logger.error("create account failed", resp.detail)
-        process.exit(1)
+      process.stdout.write("withdrawals: ");
+      if (withAdmin) {
+        await tester.testWithdrawals(admin)
+        console.log("ok")
+      } else {
+        console.log("skipped")
       }
-    }
-    {
-      const resp = await api.updateAccount({username, token}, { 
challenge_contact_data: {email: "asd"} })
-      if (resp.type === "fail") {
-        logger.error("create account failed", resp.detail)
-        process.exit(1)
+
+      process.stdout.write("monitor: ");
+      if (withAdmin && config.have_cashout) {
+        await tester.testMonitor(admin)
+        console.log("ok")
+      } else {
+        console.log("skipped")
       }
-    }
 
+      process.stdout.write("cashout: ");
+      if (withAdmin && config.have_cashout) {
+        await tester.testCashouts(admin)
+        console.log("ok")
+      } else {
+        console.log("skipped")
+      }
 
-    logger.info("test completed")
-    
+    } 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-client/authentication.ts 
b/packages/taler-util/src/http-client/authentication.ts
index 0c59c9308..b27a266e9 100644
--- a/packages/taler-util/src/http-client/authentication.ts
+++ b/packages/taler-util/src/http-client/authentication.ts
@@ -17,8 +17,9 @@
 import { HttpStatusCode } from "../http-status-codes.js";
 import { HttpRequestLibrary, createPlatformHttpLib, makeBasicAuthHeader } from 
"../http.js";
 import { LibtoolVersion } from "../libtool-version.js";
-import { AccessToken, TalerAuthentication, UserAndPassword, UserAndToken, 
codecForTokenSuccessResponse } from "./types.js";
-import { makeBearerTokenAuthHeader, opEmptySuccess, opKnownFailure, opSuccess, 
opUnknownFailure } from "./utils.js";
+import { opEmptySuccess, opKnownFailure, opSuccess, opUnknownFailure } from 
"../operation.js";
+import { AccessToken, TalerAuthentication, codecForTokenSuccessResponse } from 
"./types.js";
+import { makeBearerTokenAuthHeader } from "./utils.js";
 
 export class TalerAuthenticationHttpClient {
   public readonly PROTOCOL_VERSION = "0:0:0";
diff --git a/packages/taler-util/src/http-client/bank-core.ts 
b/packages/taler-util/src/http-client/bank-core.ts
index de3622b8e..e214f6dcd 100644
--- a/packages/taler-util/src/http-client/bank-core.ts
+++ b/packages/taler-util/src/http-client/bank-core.ts
@@ -18,36 +18,23 @@ import {
   AmountJson,
   Amounts,
   HttpStatusCode,
-  LibtoolVersion,
-  TalerError,
-  TalerErrorDetail,
-  encodeCrock,
-  getRandomBytes,
-  parsePaytoUri,
-  stringifyPaytoUri
+  LibtoolVersion
 } from "@gnu-taler/taler-util";
 import {
   HttpRequestLibrary,
   createPlatformHttpLib
 } from "@gnu-taler/taler-util/http";
-import { setShowCurlRequest } from "../http-impl.node.js";
+import { FailCasesByMethod, ResultByMethod, opEmptySuccess, opFixedSuccess, 
opKnownFailure, opSuccess, opUnknownFailure } from "../operation.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, 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";
+import { AccessToken, PaginationParams, TalerCorebankApi, UserAndToken, 
codecForAccountData, codecForBankAccountCreateWithdrawalResponse, 
codecForBankAccountGetWithdrawalResponse, codecForBankAccountTransactionInfo, 
codecForBankAccountTransactionsResponse, codecForCashoutConversionResponse, 
codecForCashoutPending, codecForCashoutStatusResponse, codecForCashouts, 
codecForCoreBankConfig, codecForGlobalCashouts, 
codecForListBankAccountsResponse, codecForMonitorResponse, codecForPublicAccoun 
[...]
+import { addPaginationParams, makeBearerTokenAuthHeader } from "./utils.js";
 
 
-type props = keyof TalerCoreBankHttpClient
-
-export type TalerCoreBankResultByMethod<p extends props> = 
TalerCoreBankHttpClient[p] extends (...args: any[]) => infer Ret ?
-  Ret extends Promise<infer Result> ?
-  Result :
-  never : //api always use Promises
-  never; //error cases just for functions
-
-export type TalerCoreBankErrorsByMethod<p extends props> = 
Exclude<TalerCoreBankResultByMethod<p>, OperationOk<any>>
+export type TalerCoreBankResultByMethod<prop extends keyof 
TalerCoreBankHttpClient> = ResultByMethod<TalerCoreBankHttpClient, prop>
+export type TalerCoreBankErrorsByMethod<prop extends keyof 
TalerCoreBankHttpClient> = FailCasesByMethod<TalerCoreBankHttpClient, prop>
 
 /**
  * Protocol version spoken with the bank.
@@ -355,7 +342,7 @@ export class TalerCoreBankHttpClient {
     });
     switch (resp.status) {
       case HttpStatusCode.Ok: return opSuccess(resp, 
codecForBankAccountCreateWithdrawalResponse())
-      case HttpStatusCode.PreconditionFailed: return 
opKnownFailure("insufficient-funds", resp);
+      case HttpStatusCode.Conflict: 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);
@@ -651,559 +638,6 @@ export class TalerCoreBankHttpClient {
     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 887dbed1b..e59f6086a 100644
--- a/packages/taler-util/src/http-client/bank-integration.ts
+++ b/packages/taler-util/src/http-client/bank-integration.ts
@@ -1,12 +1,12 @@
 import { HttpRequestLibrary, readSuccessResponseJsonOrThrow } from 
"../http-common.js";
 import { HttpStatusCode } from "../http-status-codes.js";
 import { createPlatformHttpLib } from "../http.js";
+import { opSuccess, opUnknownFailure } from "../operation.js";
 import {
   TalerBankIntegrationApi,
   codecForBankWithdrawalOperationPostResponse,
   codecForBankWithdrawalOperationStatus
 } from "./types.js";
-import { opSuccess, opUnknownFailure } from "./utils.js";
 
 export class TalerBankIntegrationHttpClient {
   httpLib: HttpRequestLibrary;
diff --git a/packages/taler-util/src/http-client/test.cli.ts 
b/packages/taler-util/src/http-client/test.cli.ts
deleted file mode 100644
index 08a0c5fd3..000000000
--- a/packages/taler-util/src/http-client/test.cli.ts
+++ /dev/null
@@ -1,77 +0,0 @@
-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-client/types.ts 
b/packages/taler-util/src/http-client/types.ts
index 8af134280..b9a5032d1 100644
--- a/packages/taler-util/src/http-client/types.ts
+++ b/packages/taler-util/src/http-client/types.ts
@@ -35,27 +35,6 @@ export type PaginationParams = {
   order: "asc" | "dec"
 }
 
-export type OperationResult<Body, ErrorEnum> =
-  | OperationOk<Body>
-  | OperationFail<ErrorEnum>;
-
-export interface OperationOk<T> {
-  type: "ok",
-  body: T;
-}
-export function isOperationOk<T, E>(c: OperationResult<T, E>): c is 
OperationOk<T> {
-  return c.type === "ok"
-}
-export function isOperationFail<T, E>(c: OperationResult<T, E>): c is 
OperationFail<E> {
-  return c.type === "fail"
-}
-export interface OperationFail<T> {
-  type: "fail",
-  case: T,
-  detail: TalerErrorDetail,
-}
-
-
 ///
 /// HASH
 ///
diff --git a/packages/taler-util/src/http-client/utils.ts 
b/packages/taler-util/src/http-client/utils.ts
index 3be5d6e89..32a9c4009 100644
--- a/packages/taler-util/src/http-client/utils.ts
+++ b/packages/taler-util/src/http-client/utils.ts
@@ -1,8 +1,8 @@
 import { base64FromArrayBuffer } from "../base64.js";
 import { HttpResponse, readSuccessResponseJsonOrThrow, readTalerErrorResponse 
} from "../http-common.js";
-import { Codec, TalerError, TalerErrorCode } from "../index.js";
+import { Codec, TalerError, TalerErrorCode, TalerErrorDetail } from 
"../index.js";
 import { stringToBytes } from "../taler-crypto.js";
-import { AccessToken, OperationFail, OperationOk, PaginationParams } from 
"./types.js";
+import { AccessToken, PaginationParams } from "./types.js";
 
 /**
  * Helper function to generate the "Authorization" HTTP header.
@@ -38,34 +38,3 @@ export function addPaginationParams(url: URL, pagination?: 
PaginationParams) {
   //always send delta
   url.searchParams.set("delta", String(order * limit))
 }
-
-
-//////
-// Operation Helper Constructors
-//////
-export async function opSuccess<T>(resp: HttpResponse, codec: Codec<T>): 
Promise<OperationOk<T>> {
-  const body = await readSuccessResponseJsonOrThrow(resp, codec)
-  return { type: "ok" as const, body }
-}
-export function opFixedSuccess<T>(body: T): OperationOk<T> {
-  return { type: "ok" as const, body }
-}
-export function opEmptySuccess(): OperationOk<void> {
-  return { type: "ok" as const, body: void 0 }
-}
-export async function opKnownFailure<T extends string>(s: T, resp: 
HttpResponse): Promise<OperationFail<T>> {
-  const detail = await readTalerErrorResponse(resp)
-  return { type: "fail", case: s, detail }
-}
-export function opUnknownFailure(resp: HttpResponse, text: string): never {
-  throw TalerError.fromDetail(
-    TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
-    {
-      requestUrl: resp.requestUrl,
-      requestMethod: resp.requestMethod,
-      httpStatusCode: resp.status,
-      errorResponse: text,
-    },
-    `Unexpected HTTP status ${resp.status} in response`,
-  );
-}
diff --git a/packages/taler-util/src/http-impl.node.ts 
b/packages/taler-util/src/http-impl.node.ts
index 11ae9480c..5aca6e99d 100644
--- a/packages/taler-util/src/http-impl.node.ts
+++ b/packages/taler-util/src/http-impl.node.ts
@@ -57,7 +57,7 @@ const logger = new Logger("http-impl.node.ts");
 
 const textDecoder = new TextDecoder();
 let SHOW_CURL_HTTP_REQUEST = false;
-export function setShowCurlRequest(b: boolean) {
+export function setPrintHttpRequestAsCurl(b: boolean) {
   SHOW_CURL_HTTP_REQUEST = b
 }
 
diff --git a/packages/taler-util/src/index.ts b/packages/taler-util/src/index.ts
index 71d4253f0..ea5a805a0 100644
--- a/packages/taler-util/src/index.ts
+++ b/packages/taler-util/src/index.ts
@@ -47,3 +47,4 @@ export * from "./http-client/bank-integration.js";
 export * from "./http-client/bank-revenue.js";
 export * from "./http-client/bank-wire.js";
 export * from "./http-client/types.js";
+export * from "./operation.js";
diff --git a/packages/taler-util/src/operation.ts 
b/packages/taler-util/src/operation.ts
new file mode 100644
index 000000000..aab5dc022
--- /dev/null
+++ b/packages/taler-util/src/operation.ts
@@ -0,0 +1,74 @@
+import { HttpResponse, readSuccessResponseJsonOrThrow, readTalerErrorResponse 
} from "./http-common.js";
+import { Codec, TalerError, TalerErrorCode, TalerErrorDetail } from 
"./index.js";
+
+export type OperationResult<Body, ErrorEnum> =
+  | OperationOk<Body>
+  | OperationFail<ErrorEnum>;
+
+export interface OperationOk<T> {
+  type: "ok",
+  body: T;
+}
+export function isOperationOk<T, E>(c: OperationResult<T, E>): c is 
OperationOk<T> {
+  return c.type === "ok"
+}
+export function isOperationFail<T, E>(c: OperationResult<T, E>): c is 
OperationFail<E> {
+  return c.type === "fail"
+}
+export interface OperationFail<T> {
+  type: "fail",
+  case: T,
+  detail: TalerErrorDetail,
+}
+
+export async function opSuccess<T>(resp: HttpResponse, codec: Codec<T>): 
Promise<OperationOk<T>> {
+  const body = await readSuccessResponseJsonOrThrow(resp, codec)
+  return { type: "ok" as const, body }
+}
+export function opFixedSuccess<T>(body: T): OperationOk<T> {
+  return { type: "ok" as const, body }
+}
+export function opEmptySuccess(): OperationOk<void> {
+  return { type: "ok" as const, body: void 0 }
+}
+export async function opKnownFailure<T extends string>(s: T, resp: 
HttpResponse): Promise<OperationFail<T>> {
+  const detail = await readTalerErrorResponse(resp)
+  return { type: "fail", case: s, detail }
+}
+export function opUnknownFailure(resp: HttpResponse, text: string): never {
+  throw TalerError.fromDetail(
+    TalerErrorCode.WALLET_UNEXPECTED_REQUEST_ERROR,
+    {
+      requestUrl: resp.requestUrl,
+      requestMethod: resp.requestMethod,
+      httpStatusCode: resp.status,
+      errorResponse: text,
+    },
+    `Unexpected HTTP status ${resp.status} in response`,
+  );
+}
+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 type ResultByMethod<TT extends object, p extends keyof TT> = TT[p] 
extends (...args: any[]) => infer Ret ?
+  Ret extends Promise<infer Result> ?
+  Result :
+  never : //api always use Promises
+  never; //error cases just for functions
+
+export type FailCasesByMethod<TT extends object, p extends keyof TT> = 
Exclude<ResultByMethod<TT, p>, OperationOk<any>>

-- 
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]