import { AccessToken, AmountJson, Amounts, TalerCoreBankHttpClient, TalerCorebankApi, TalerRevenueHttpClient, TalerWireGatewayApi, TalerWireGatewayHttpClient, TestForApi, buildPayto, encodeCrock, failOrThrow, getRandomBytes, parsePaytoUri, stringifyPaytoUri, succeedOrThrow } from "@gnu-taler/taler-util" export function createTestForBankCore(api: TalerCoreBankHttpClient, adminToken: AccessToken): TestForApi { return { test_abortCashoutById: { success: undefined, "already-confirmed": undefined, "cashout-not-supported": undefined, "not-found": undefined, }, test_createCashout: { "account-not-found": undefined, "incorrect-exchange-rate": undefined, "no-contact-info": undefined, "no-enough-balance": undefined, "cashout-not-supported": undefined, success: undefined, }, test_confirmCashoutById: { "cashout-address-changed": undefined, "cashout-not-supported": undefined, "not-found": undefined, "wrong-tan-or-credential": undefined, success: undefined, }, test_getAccountCashouts: { "cashout-not-supported": undefined, "account-not-found": undefined, success: undefined, }, test_getCashoutById: { "cashout-not-supported": undefined, success: undefined, "not-found": undefined, }, test_getCashoutRate: { "cashout-not-supported": undefined, "not-supported": undefined, "wrong-calculation": undefined, success: undefined, }, test_getGlobalCashouts: { "cashout-not-supported": undefined, success: undefined, }, test_abortWithdrawalById: { "invalid-id": async () => { await failOrThrow("invalid-id", () => api.abortWithdrawalById("invalid") ) }, "not-found": async () => { await failOrThrow("not-found", () => api.abortWithdrawalById("11111111-1111-1111-1111-111111111111") ) }, "previously-confirmed": async () => { const { username: exchangeUser, token: exchangeToken } = await createRandomTestUser(api, adminToken, { is_taler_exchange: true }) const { username, token } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const exchangeInfo = await succeedOrThrow(() => api.getAccount({ username: exchangeUser, token: exchangeToken }) ) const { withdrawal_id } = await succeedOrThrow(() => api.createWithdrawal({ username, token }, { amount: userInfo.balance.amount }) ) await succeedOrThrow(() => api.getIntegrationAPI().completeWithdrawalOperationById(withdrawal_id, { reserve_pub: encodeCrock(getRandomBytes(32)), selected_exchange: exchangeInfo.payto_uri, }) ) await succeedOrThrow(() => api.confirmWithdrawalById(withdrawal_id) ) await failOrThrow("previously-confirmed", () => api.abortWithdrawalById(withdrawal_id) ) }, success: async () => { const { username, token } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const { withdrawal_id: firstWithdrawal } = await succeedOrThrow(() => api.createWithdrawal({ username, token }, { amount: userInfo.balance.amount }) ) await succeedOrThrow(() => api.abortWithdrawalById(firstWithdrawal) ) }, }, test_confirmWithdrawalById: { "insufficient-funds": async () => { }, "invalid-id": async () => { await failOrThrow("invalid-id", () => api.confirmWithdrawalById("invalid") ) }, "no-exchange-or-reserve-selected": async () => { const { username, token } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const { withdrawal_id } = await succeedOrThrow(() => api.createWithdrawal({ username, token }, { amount: userInfo.balance.amount }) ) await failOrThrow("no-exchange-or-reserve-selected", () => api.confirmWithdrawalById(withdrawal_id) ) }, "not-found": async () => { await failOrThrow("not-found", () => api.confirmWithdrawalById("11111111-1111-1111-1111-111111111111") ) }, "previously-aborted": async () => { const { username, token } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const { withdrawal_id } = await succeedOrThrow(() => api.createWithdrawal({ username, token }, { amount: userInfo.balance.amount }) ) await succeedOrThrow(() => api.abortWithdrawalById(withdrawal_id) ) await failOrThrow("previously-aborted", () => api.confirmWithdrawalById(withdrawal_id) ) }, success: async () => { const { username: exchangeUser, token: exchangeToken } = await createRandomTestUser(api, adminToken, { is_taler_exchange: true }) const { username, token } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const exchangeInfo = await succeedOrThrow(() => api.getAccount({ username: exchangeUser, token: exchangeToken }) ) const { withdrawal_id } = await succeedOrThrow(() => api.createWithdrawal({ username, token }, { amount: userInfo.balance.amount }) ) await succeedOrThrow(() => api.getIntegrationAPI().completeWithdrawalOperationById(withdrawal_id, { reserve_pub: encodeCrock(getRandomBytes(32)), selected_exchange: exchangeInfo.payto_uri, }) ) await succeedOrThrow(() => api.confirmWithdrawalById(withdrawal_id) ) }, }, test_createAccount: { "insufficient-funds": undefined, "payto-already-exists": async () => { const { username, token } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const anotherUsername = "harness-" + encodeCrock(getRandomBytes(10)).toLowerCase(); await failOrThrow("payto-already-exists", () => api.createAccount(adminToken, { name: anotherUsername, username: anotherUsername, password: "123", internal_payto_uri: userInfo.payto_uri, }) ); }, "username-reserved": async () => { await failOrThrow("username-reserved", () => api.createAccount(adminToken, { name: "admin", username: "admin", password: "123" }) ) }, "username-already-exists": async () => { const username = "harness-" + encodeCrock(getRandomBytes(10)).toLowerCase(); await succeedOrThrow(() => api.createAccount(adminToken, { name: username, username, password: "123" }) ) await failOrThrow("username-already-exists", () => api.createAccount(adminToken, { name: username, username, password: "123" }) ); }, "invalid-phone-or-email": async () => { const username = "harness-" + encodeCrock(getRandomBytes(10)).toLowerCase(); await failOrThrow("invalid-input", () => api.createAccount(adminToken, { name: username, username, password: "123", challenge_contact_data: { email: "invalid email", phone: "invalid phone", } }) ) }, success: async () => { const username = "harness-" + encodeCrock(getRandomBytes(10)).toLowerCase(); await succeedOrThrow(() => api.createAccount(adminToken, { name: username, username, password: "123" }) ) }, unauthorized: async () => { const username = "harness-" + encodeCrock(getRandomBytes(10)).toLowerCase(); await succeedOrThrow(() => api.createAccount(adminToken, { name: username, username, password: "123" }) ) const { access_token } = await succeedOrThrow(() => api.getAuthenticationAPI(username).createAccessToken("123", { scope: "readwrite" }) ) const anotherUser = "harness-" + encodeCrock(getRandomBytes(10)).toLowerCase(); await failOrThrow("unauthorized", () => api.createAccount(access_token, { name: anotherUser, username: anotherUser, password: "123" }) ) }, }, test_createTransaction: { "creditor-not-found": async () => { const { username, token } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const notFoundAccount = buildPayto("iban", "DE1231231231", undefined) notFoundAccount.params["message"] = "not-found" await failOrThrow("creditor-not-found", () => api.createTransaction({ username, token }, { payto_uri: stringifyPaytoUri(notFoundAccount), amount: userInfo.balance.amount }) ) }, "creditor-same": async () => { const { username, token } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const account = parsePaytoUri(userInfo.payto_uri)! account.params["message"] = "myaccount" await failOrThrow("creditor-same", () => api.createTransaction({ username, token }, { payto_uri: stringifyPaytoUri(account), amount: userInfo.balance.amount }) ) }, "insufficient-funds": async () => { const { username, token } = await createRandomTestUser(api, adminToken) const { username: otherUser, token: otherToken } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const otherInfo = await succeedOrThrow(() => api.getAccount({ username: otherUser, token: otherToken }) ) const otherAccount = parsePaytoUri(otherInfo.payto_uri)! otherAccount.params["message"] = "all" await failOrThrow("insufficient-funds", () => api.createTransaction({ username, token }, { payto_uri: stringifyPaytoUri(otherAccount), amount: Amounts.stringify(Amounts.mult(userInfo.balance.amount, 20).amount) }) ) }, "not-found": async () => { const { username, token } = await createRandomTestUser(api, adminToken) const { username: otherUser, token: otherToken } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const otherInfo = await succeedOrThrow(() => api.getAccount({ username: otherUser, token: otherToken }) ) const otherAccount = parsePaytoUri(otherInfo.payto_uri)! otherAccount.params["message"] = "all" await succeedOrThrow(() => api.createTransaction({ username: "notfound", token }, { payto_uri: stringifyPaytoUri(otherAccount), amount: userInfo.balance.amount }) ) }, "invalid-input": async () => { const { username, token } = await createRandomTestUser(api, adminToken) const { username: otherUser, token: otherToken } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const otherInfo = await succeedOrThrow(() => api.getAccount({ username: otherUser, token: otherToken }) ) const otherAccount = parsePaytoUri(otherInfo.payto_uri)! otherAccount.params["message"] = "all" //missing amount await failOrThrow("invalid-input", () => api.createTransaction({ username, token }, { payto_uri: stringifyPaytoUri(otherAccount), // amount: userInfo.balance.amount }) ) //missing subject await failOrThrow("invalid-input", () => api.createTransaction({ username, token }, { payto_uri: otherInfo.payto_uri, amount: userInfo.balance.amount }) ) }, success: async () => { const { username, token } = await createRandomTestUser(api, adminToken) const { username: otherUser, token: otherToken } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const otherInfo = await succeedOrThrow(() => api.getAccount({ username: otherUser, token: otherToken }) ) const otherAccount = parsePaytoUri(otherInfo.payto_uri)! otherAccount.params["message"] = "all" await succeedOrThrow(() => api.createTransaction({ username, token }, { payto_uri: stringifyPaytoUri(otherAccount), amount: userInfo.balance.amount }) ) }, unauthorized: async () => { const { username, token } = await createRandomTestUser(api, adminToken) const { username: otherUser, token: otherToken } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const otherInfo = await succeedOrThrow(() => api.getAccount({ username: otherUser, token: otherToken }) ) const otherAccount = parsePaytoUri(otherInfo.payto_uri)! otherAccount.params["message"] = "all" await failOrThrow("unauthorized", () => api.createTransaction({ username, token: "wrongtoken" as AccessToken }, { payto_uri: stringifyPaytoUri(otherAccount), amount: userInfo.balance.amount }) ) }, }, test_createWithdrawal: { "account-not-found": async () => { const userInfo = await succeedOrThrow(() => api.getAccount({ username: "admin", token: adminToken }) ) await succeedOrThrow(() => api.createWithdrawal({ username: "notfound", token: adminToken }, { amount: userInfo.balance.amount }) ) }, "insufficient-funds": async () => { const { username, token } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const balance = Amounts.parseOrThrow(userInfo.balance.amount) const moreThanBalance = Amounts.stringify(Amounts.mult(balance, 5).amount) await failOrThrow("insufficient-funds", () => api.createWithdrawal({ username, token }, { amount: moreThanBalance }) ) }, success: async () => { const { username, token } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) await succeedOrThrow(() => api.createWithdrawal({ username, token }, { amount: userInfo.balance.amount }) ) }, unauthorized: async () => { const { username, token } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) await failOrThrow("unauthorized", () => api.createWithdrawal({ username, token: "wrongtoken" as AccessToken }, { amount: userInfo.balance.amount }) ) }, }, test_deleteAccount: { "balance-not-zero": async () => { const { username, token } = await createRandomTestUser(api, adminToken) await failOrThrow("balance-not-zero", () => api.deleteAccount({ username, token: adminToken }) ) }, "not-found": async () => { await failOrThrow("not-found", () => api.deleteAccount({ username: "not-found", token: adminToken }) ) }, "username-reserved": async () => { await failOrThrow("username-reserved", () => api.deleteAccount({ username: "admin", token: adminToken }) ) await failOrThrow("username-reserved", () => api.deleteAccount({ username: "bank", token: adminToken }) ) }, success: async () => { const { username, token } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const adminInfo = await succeedOrThrow(() => api.getAccount({ username: "admin", token: adminToken }) ) const adminAccount = parsePaytoUri(adminInfo.payto_uri)! adminAccount.params["message"] = "all my money" const withSubject = stringifyPaytoUri(adminAccount) await succeedOrThrow(() => api.createTransaction({ username, token }, { payto_uri: withSubject, amount: userInfo.balance.amount }) ) }, unauthorized: async () => { const username = "harness-" + encodeCrock(getRandomBytes(10)).toLowerCase(); await succeedOrThrow(() => api.createAccount(adminToken, { name: username, username, password: "123" }) ) const { token } = await createRandomTestUser(api, adminToken) await failOrThrow("unauthorized", () => api.deleteAccount({ username: username, token }) ) }, }, test_getAccount: { "not-found": async () => { await failOrThrow("not-found", () => api.getAccount({ username: "not-found", token: adminToken }) ) }, success: async () => { const { username, token } = await createRandomTestUser(api, adminToken) await succeedOrThrow(() => api.getAccount({ username, token }) ) }, unauthorized: async () => { const { username, token } = await createRandomTestUser(api, adminToken) await failOrThrow("unauthorized", () => api.getAccount({ username, token: "wrongtoken" as AccessToken }) ) }, }, test_getAccounts: { success: async () => { await succeedOrThrow(() => api.getAccounts(adminToken) ) await succeedOrThrow(() => api.getAccounts(adminToken, { account: "admin" }) ) await succeedOrThrow(() => api.getAccounts(adminToken, undefined, { order: "dec", limit: 10, offset: "1" }) ) }, unauthorized: async () => { await failOrThrow("unauthorized", () => api.getAccounts("ASDASD" as AccessToken) ) }, }, test_getConfig: { success: async () => { const config = await succeedOrThrow(() => api.getConfig()) if (!api.isCompatible(config.version)) { throw Error(`not compatible with server ${config.version}`) } }, }, test_getMonitor: { "unauthorized": async () => { await failOrThrow("unauthorized", () => ( api.getMonitor("wrongtoken" as AccessToken) )) }, "invalid-input": async () => { await failOrThrow("invalid-input", () => ( api.getMonitor(adminToken, { timeframe: TalerCorebankApi.MonitorTimeframeParam.day, which: 100 }) )) }, "monitor-not-supported": undefined, success: async () => { await succeedOrThrow(() => ( api.getMonitor(adminToken) )) await succeedOrThrow(() => ( api.getMonitor(adminToken, { timeframe: TalerCorebankApi.MonitorTimeframeParam.day, which: (new Date()).getDate() - 1 }) )) }, }, test_getPublicAccounts: { success: async () => { await succeedOrThrow(() => ( api.getPublicAccounts() )) await succeedOrThrow(() => ( api.getPublicAccounts({ order: "asc" }) )) await succeedOrThrow(() => ( api.getPublicAccounts({ order: "dec" }) )) await succeedOrThrow(() => ( api.getPublicAccounts({ order: "dec", limit: 10, offset: String(1) }) )) }, }, test_getTransactionById: { "not-found": async () => { const { username, token } = await createRandomTestUser(api, adminToken) await failOrThrow("not-found", () => api.getTransactionById({ username, token }, 123123123) ) }, success: async () => { const { username, token } = await createRandomTestUser(api, adminToken) const { username: otherUser, token: otherToken } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const otherInfo = await succeedOrThrow(() => api.getAccount({ username: otherUser, token: otherToken }) ) const otherAccount = parsePaytoUri(otherInfo.payto_uri)! otherAccount.params["message"] = "all" await succeedOrThrow(() => api.createTransaction({ username, token }, { payto_uri: stringifyPaytoUri(otherAccount), amount: userInfo.balance.amount }) ) const txs = await succeedOrThrow(() => api.getTransactions({ username, token }, { limit: 5, order: "asc" })) const rowId = txs.transactions[0].row_id await succeedOrThrow(() => api.getTransactionById({ username, token }, rowId) ) }, unauthorized: async () => { const { username, token } = await createRandomTestUser(api, adminToken) await failOrThrow("unauthorized", () => api.getTransactionById({ username, token: "wrongtoken" as AccessToken }, 123123123) ) }, }, test_getTransactions: { "not-found": async () => { await failOrThrow("not-found", () => api.getTransactions({ username: "not-found", token: adminToken, })) }, success: async () => { const { username, token } = await createRandomTestUser(api, adminToken) // await succeedOrThrow(() => api.getTransactions(creds)) const txs = await succeedOrThrow(() => api.getTransactions({ username, token }, { limit: 5, order: "asc" })) }, unauthorized: async () => { const { username, token } = await createRandomTestUser(api, adminToken) await failOrThrow("unauthorized", () => api.getTransactions({ username: username, token: "wrongtoken" as AccessToken, })) }, }, test_getWithdrawalById: { "invalid-id": async () => { await failOrThrow("invalid-id", () => api.getWithdrawalById("invalid") ) }, "not-found": async () => { await failOrThrow("not-found", () => api.getWithdrawalById("11111111-1111-1111-1111-111111111111") ) }, success: async () => { const { username, token } = await createRandomTestUser(api, adminToken) const userInfo = await succeedOrThrow(() => api.getAccount({ username, token }) ) const { withdrawal_id } = await succeedOrThrow(() => api.createWithdrawal({ username, token }, { amount: userInfo.balance.amount }) ) await succeedOrThrow(() => api.getWithdrawalById(withdrawal_id) ) }, }, test_updateAccount: { "cant-change-legal-name-or-admin": async () => { const { username, token } = await createRandomTestUser(api, adminToken) await failOrThrow("cant-change-legal-name-or-admin", () => api.updateAccount({ username, token }, { name: "something else", }) ) }, "not-found": async () => { const { username, token } = await createRandomTestUser(api, adminToken) await failOrThrow("not-found", () => api.updateAccount({ username: "notfound", token }, { challenge_contact_data: { email: "asd@Aasd.com" } }) ) }, success: async () => { const { username, token } = await createRandomTestUser(api, adminToken) await succeedOrThrow(() => api.updateAccount({ username, token }, { challenge_contact_data: { email: "asd@Aasd.com" } }) ) }, unauthorized: async () => { await failOrThrow("unauthorized", () => api.updateAccount({ username: "notfound", token: "wrongtoken" as AccessToken }, { challenge_contact_data: { email: "asd@Aasd.com" } }) ) }, }, test_updatePassword: { "not-found": async () => { await failOrThrow("not-found", () => api.updatePassword({ username: "notfound", token: adminToken }, { old_password: "123", new_password: "234" }) ) }, "old-password-invalid-or-not-allowed": async () => { const { username, token } = await createRandomTestUser(api, adminToken) await failOrThrow("old-password-invalid-or-not-allowed", () => api.updatePassword({ username, token }, { old_password: "1233", new_password: "234" }) ) }, success: async () => { const { username, token } = await createRandomTestUser(api, adminToken) await succeedOrThrow(() => api.updatePassword({ username, token }, { old_password: "123", new_password: "234" }) ) }, unauthorized: async () => { const { username, token } = await createRandomTestUser(api, adminToken) await failOrThrow("unauthorized", () => api.updatePassword({ username: "admin", token }, { old_password: "123", new_password: "234" }) ) }, }, } } export function createTestForBankRevenue(bank: TalerCoreBankHttpClient, adminToken: AccessToken): TestForApi { return { test_getHistory: { "endpoint-wrong-or-username-wrong": async () => { const history = await failOrThrow("endpoint-wrong-or-username-wrong", () => bank.getRevenueAPI("notfound").getHistory("wrongtoken" as AccessToken) ) }, "invalid-input": undefined, success: async () => { const { token: exchangeToken, username: exchangeUsername } = await createRandomTestUser(bank, adminToken, { is_taler_exchange: true }) const { token: merchantToken, username: merchantUsername } = await createRandomTestUser(bank, adminToken) const config = await succeedOrThrow(() => bank.getConfig()) const merchantinfo = await succeedOrThrow(() => bank.getAccount({ username: merchantUsername, token: merchantToken }) ) const account = parsePaytoUri(merchantinfo.payto_uri)! account.params["message"] = "all" const amount = Amounts.stringify({ currency: config.currency.name, fraction: 0, value: 1 }) await succeedOrThrow(() => bank.createTransaction({ username: exchangeUsername, token: exchangeToken }, { payto_uri: stringifyPaytoUri(account), amount }) ) const history = await succeedOrThrow(() => bank.getRevenueAPI(merchantUsername).getHistory(merchantToken) ) }, unauthorized: async () => { const { token: merchantToken, username: merchantUsername } = await createRandomTestUser(bank, adminToken) const history = await failOrThrow("unauthorized", () => bank.getRevenueAPI(merchantUsername).getHistory("wrongtoken" as AccessToken) ) }, } } } export function createTestForBankWireGateway(bank: TalerCoreBankHttpClient, adminToken: AccessToken): TestForApi { return { //not used in production test_addIncoming: { "invalid-input": undefined, "not-found": undefined, "reserve-id-already-used": undefined, success: undefined, unauthorized: undefined, }, test_getHistoryIncoming: { "invalid-input": async () => { }, "not-found": async () => { }, success: async () => { }, unauthorized: async () => { }, }, test_getHistoryOutgoing: { "invalid-input": async () => { }, "not-found": async () => { }, success: async () => { }, unauthorized: async () => { }, }, test_transfer: { "invalid-input": async () => { }, "not-found": async () => { }, "request-uid-already-used": async () => { }, success: async () => { const { token: exchangeToken, username: exchangeUsername } = await createRandomTestUser(bank, adminToken, { is_taler_exchange: true }) const { token: merchantToken, username: merchantUsername } = await createRandomTestUser(bank, adminToken) const config = await succeedOrThrow(() => bank.getConfig()) const merchantInfo = await succeedOrThrow(() => bank.getAccount({ username: merchantUsername, token: merchantToken }) ) const account = parsePaytoUri(merchantInfo.payto_uri)! account.params["message"] = "all" const amount = Amounts.stringify({ currency: config.currency.name, fraction: 0, value: 1 }) const resp = await succeedOrThrow(() => bank.getWireGatewayAPI(merchantUsername).transfer(exchangeToken, { amount, credit_account: merchantInfo.payto_uri, exchange_base_url: "", request_uid: "", wtid: "" }) ) }, unauthorized: async () => { }, } } } export async function createRandomTestUser(api: TalerCoreBankHttpClient, adminToken: AccessToken, options: Partial = {}) { const username = "harness-" + 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 } }