libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit afba15574c709cd8ff14672a4026f8e392cf8703
parent 0f3bbe826267e59f96c58b973316439fac7b3e58
Author: Antoine A <>
Date:   Wed, 15 Nov 2023 23:09:14 +0000

Clean tests

Diffstat:
Mbank/src/test/kotlin/BankIntegrationApiTest.kt | 46++++++++++++++++++++--------------------------
Mbank/src/test/kotlin/CoreBankApiTest.kt | 838+++++++++++++++++++++++++++++++++----------------------------------------------
Mbank/src/test/kotlin/DatabaseTest.kt | 4+---
Mbank/src/test/kotlin/RevenueApiTest.kt | 45++++++++++++++++++---------------------------
Mbank/src/test/kotlin/SecurityTest.kt | 17+++++++----------
Mbank/src/test/kotlin/StatsTest.kt | 5++---
Mbank/src/test/kotlin/WireGatewayApiTest.kt | 240++++++++++++++++++++++++++++++++-----------------------------------------------
Mbank/src/test/kotlin/helpers.kt | 108++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
8 files changed, 569 insertions(+), 734 deletions(-)

diff --git a/bank/src/test/kotlin/BankIntegrationApiTest.kt b/bank/src/test/kotlin/BankIntegrationApiTest.kt @@ -27,11 +27,9 @@ class BankIntegrationApiTest { @Test fun get() = bankSetup { _ -> // Check OK - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:9" } - }.assertOk().run { - val resp = json<BankAccountCreateWithdrawalResponse>() + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:9" } + }.assertOkJson<BankAccountCreateWithdrawalResponse> { resp -> val uuid = resp.taler_withdraw_uri.split("/").last() client.get("/taler-integration/withdrawal-operation/$uuid") .assertOk() @@ -50,65 +48,61 @@ class BankIntegrationApiTest { @Test fun select() = bankSetup { _ -> val reserve_pub = randEddsaPublicKey() - val req = json { + val req = obj { "reserve_pub" to reserve_pub "selected_exchange" to exchangePayto } // Check bad UUID client.post("/taler-integration/withdrawal-operation/chocolate") { - jsonBody(req) + json(req) }.assertBadRequest() // Check unknown client.post("/taler-integration/withdrawal-operation/${UUID.randomUUID()}") { - jsonBody(req) + json(req) }.assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:1" } - }.assertOk().run { - val resp = json<BankAccountCreateWithdrawalResponse>() - val uuid = resp.taler_withdraw_uri.split("/").last() + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:1" } + }.assertOkJson<BankAccountCreateWithdrawalResponse> { + val uuid = it.taler_withdraw_uri.split("/").last() // Check OK client.post("/taler-integration/withdrawal-operation/$uuid") { - jsonBody(req) + json(req) }.assertOk() // Check idempotence client.post("/taler-integration/withdrawal-operation/$uuid") { - jsonBody(req) + json(req) }.assertOk() // Check already selected client.post("/taler-integration/withdrawal-operation/$uuid") { - jsonBody(req) { + json(req) { "reserve_pub" to randEddsaPublicKey() } }.assertConflict(TalerErrorCode.BANK_WITHDRAWAL_OPERATION_RESERVE_SELECTION_CONFLICT) } - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:1" } - }.assertOk().run { - val resp = json<BankAccountCreateWithdrawalResponse>() - val uuid = resp.taler_withdraw_uri.split("/").last() + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:1" } + }.assertOkJson<BankAccountCreateWithdrawalResponse> { + val uuid = it.taler_withdraw_uri.split("/").last() // Check reserve_pub_reuse client.post("/taler-integration/withdrawal-operation/$uuid") { - jsonBody(req) + json(req) }.assertConflict(TalerErrorCode.BANK_DUPLICATE_RESERVE_PUB_SUBJECT) // Check unknown account client.post("/taler-integration/withdrawal-operation/$uuid") { - jsonBody { + json { "reserve_pub" to randEddsaPublicKey() "selected_exchange" to unknownPayto } }.assertConflict(TalerErrorCode.BANK_UNKNOWN_ACCOUNT) // Check account not exchange client.post("/taler-integration/withdrawal-operation/$uuid") { - jsonBody { + json { "reserve_pub" to randEddsaPublicKey() "selected_exchange" to merchantPayto } diff --git a/bank/src/test/kotlin/CoreBankApiTest.kt b/bank/src/test/kotlin/CoreBankApiTest.kt @@ -36,12 +36,12 @@ class CoreBankConfigTest { fun monitor() = bankSetup { _ -> // Check OK client.get("/monitor?timeframe=hour") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertOk() // Check only admin client.get("/monitor") { - basicAuth("exchange", "exchange-password") + pwAuth("exchange") }.assertUnauthorized() } } @@ -52,54 +52,48 @@ class CoreBankTokenApiTest { fun post() = bankSetup { db -> // Wrong user client.post("/accounts/merchant/token") { - basicAuth("exchange", "exchange-password") + pwAuth("exchange") }.assertUnauthorized() // New default token - client.post("/accounts/merchant/token") { - basicAuth("merchant", "merchant-password") - jsonBody { "scope" to "readonly" } - }.assertOk().run { + client.postA("/accounts/merchant/token") { + json { "scope" to "readonly" } + }.assertOkJson<TokenSuccessResponse> { // Checking that the token lifetime defaulted to 24 hours. - val resp = json<TokenSuccessResponse>() - val token = db.bearerTokenGet(Base32Crockford.decode(resp.access_token)) + val token = db.bearerTokenGet(Base32Crockford.decode(it.access_token)) val lifeTime = Duration.between(token!!.creationTime, token.expirationTime) assertEquals(Duration.ofDays(1), lifeTime) } // Check default duration - client.post("/accounts/merchant/token") { - basicAuth("merchant", "merchant-password") - jsonBody { "scope" to "readonly" } - }.assertOk().run { + client.postA("/accounts/merchant/token") { + json { "scope" to "readonly" } + }.assertOkJson<TokenSuccessResponse> { // Checking that the token lifetime defaulted to 24 hours. - val resp = json<TokenSuccessResponse>() - val token = db.bearerTokenGet(Base32Crockford.decode(resp.access_token)) + val token = db.bearerTokenGet(Base32Crockford.decode(it.access_token)) val lifeTime = Duration.between(token!!.creationTime, token.expirationTime) assertEquals(Duration.ofDays(1), lifeTime) } // Check refresh - client.post("/accounts/merchant/token") { - basicAuth("merchant", "merchant-password") - jsonBody { + client.postA("/accounts/merchant/token") { + json { "scope" to "readonly" "refreshable" to true } - }.assertOk().run { - val token = json<TokenSuccessResponse>().access_token + }.assertOkJson<TokenSuccessResponse> { + val token = it.access_token client.post("/accounts/merchant/token") { headers["Authorization"] = "Bearer secret-token:$token" - jsonBody { "scope" to "readonly" } + json { "scope" to "readonly" } }.assertOk() } // Check'forever' case. - client.post("/accounts/merchant/token") { - basicAuth("merchant", "merchant-password") - jsonBody { + client.postA("/accounts/merchant/token") { + json { "scope" to "readonly" - "duration" to json { + "duration" to obj { "d_us" to "forever" } } @@ -109,29 +103,26 @@ class CoreBankTokenApiTest { } // Check too big or invalid durations - client.post("/accounts/merchant/token") { - basicAuth("merchant", "merchant-password") - jsonBody { + client.postA("/accounts/merchant/token") { + json { "scope" to "readonly" - "duration" to json { + "duration" to obj { "d_us" to "invalid" } } }.assertBadRequest() - client.post("/accounts/merchant/token") { - basicAuth("merchant", "merchant-password") - jsonBody { + client.postA("/accounts/merchant/token") { + json { "scope" to "readonly" - "duration" to json { + "duration" to obj { "d_us" to Long.MAX_VALUE } } }.assertBadRequest() - client.post("/accounts/merchant/token") { - basicAuth("merchant", "merchant-password") - jsonBody { + client.postA("/accounts/merchant/token") { + json { "scope" to "readonly" - "duration" to json { + "duration" to obj { "d_us" to -1 } } @@ -143,11 +134,9 @@ class CoreBankTokenApiTest { fun delete() = bankSetup { _ -> // TODO test restricted val token = client.post("/accounts/merchant/token") { - basicAuth("merchant", "merchant-password") - jsonBody { "scope" to "readonly" } - }.assertOk().run { - json<TokenSuccessResponse>().access_token - } + pwAuth("merchant") + json { "scope" to "readonly" } + }.assertOkJson<TokenSuccessResponse>().access_token // Check OK client.delete("/accounts/merchant/token") { headers["Authorization"] = "Bearer secret-token:$token" @@ -159,7 +148,7 @@ class CoreBankTokenApiTest { // Checking merchant can still be served by basic auth, after token deletion. client.get("/accounts/merchant") { - basicAuth("merchant", "merchant-password") + pwAuth("merchant") }.assertOk() } } @@ -169,7 +158,7 @@ class CoreBankAccountsApiTest { @Test fun createAccountTest() = bankSetup { _ -> val ibanPayto = genIbanPaytoUri() - val req = json { + val req = obj { "username" to "foo" "password" to "password" "name" to "Jane" @@ -179,16 +168,16 @@ class CoreBankAccountsApiTest { } // Check Ok client.post("/accounts") { - jsonBody(req) + json(req) }.assertCreated() // Testing idempotency client.post("/accounts") { - jsonBody(req) + json(req) }.assertCreated() // Test generate payto_uri client.post("/accounts") { - jsonBody { + json { "username" to "jor" "password" to "password" "name" to "Joe" @@ -198,7 +187,7 @@ class CoreBankAccountsApiTest { // Reserved account reservedAccounts.forEach { client.post("/accounts") { - jsonBody { + json { "username" to it "password" to "password" "name" to "John Smith" @@ -208,25 +197,25 @@ class CoreBankAccountsApiTest { // Testing login conflict client.post("/accounts") { - jsonBody(req) { + json(req) { "name" to "Foo" } }.assertConflict(TalerErrorCode.BANK_REGISTER_USERNAME_REUSE) // Testing payto conflict client.post("/accounts") { - jsonBody(req) { + json(req) { "username" to "bar" } }.assertConflict(TalerErrorCode.BANK_REGISTER_PAYTO_URI_REUSE) client.get("/accounts/bar") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertNotFound(TalerErrorCode.BANK_UNKNOWN_ACCOUNT) } // Test account created with bonus @Test fun createAccountBonusTest() = bankSetup(conf = "test_bonus.conf") { _ -> - val req = json { + val req = obj { "username" to "foo" "password" to "xyz" "name" to "Mallory" @@ -235,8 +224,8 @@ class CoreBankAccountsApiTest { // Check ok repeat(100) { client.post("/accounts") { - basicAuth("admin", "admin-password") - jsonBody(req) { + pwAuth("admin") + json(req) { "username" to "foo$it" } }.assertCreated() @@ -246,32 +235,32 @@ class CoreBankAccountsApiTest { // Check unsufficient fund client.post("/accounts") { - basicAuth("admin", "admin-password") - jsonBody(req) { + pwAuth("admin") + json(req) { "username" to "bar" } }.assertConflict(TalerErrorCode.BANK_UNALLOWED_DEBIT) client.get("/accounts/bar") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertNotFound(TalerErrorCode.BANK_UNKNOWN_ACCOUNT) } // Test admin-only account creation @Test fun createAccountRestrictedTest() = bankSetup(conf = "test_restrict.conf") { _ -> - val req = json { + val req = obj { "username" to "baz" "password" to "xyz" "name" to "Mallory" } client.post("/accounts") { - basicAuth("merchant", "merchant-password") - jsonBody(req) + pwAuth("merchant") + json(req) }.assertUnauthorized() client.post("/accounts") { - basicAuth("admin", "admin-password") - jsonBody(req) + pwAuth("admin") + json(req) }.assertCreated() } @@ -280,41 +269,41 @@ class CoreBankAccountsApiTest { fun deleteAccount() = bankSetup { _ -> // Unknown account client.delete("/accounts/unknown") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertNotFound(TalerErrorCode.BANK_UNKNOWN_ACCOUNT) // Reserved account reservedAccounts.forEach { client.delete("/accounts/$it") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertConflict(TalerErrorCode.BANK_RESERVED_USERNAME_CONFLICT) } // successful deletion client.post("/accounts") { - jsonBody { + json { "username" to "john" "password" to "password" "name" to "John Smith" } }.assertCreated() client.delete("/accounts/john") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertNoContent() // Trying again must yield 404 client.delete("/accounts/john") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertNotFound(TalerErrorCode.BANK_UNKNOWN_ACCOUNT) // fail to delete, due to a non-zero balance. tx("exchange", "KUDOS:1", "merchant") client.delete("/accounts/merchant") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertConflict(TalerErrorCode.BANK_ACCOUNT_BALANCE_NOT_ZERO) tx("merchant", "KUDOS:1", "exchange") client.delete("/accounts/merchant") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertNoContent() } @@ -323,68 +312,62 @@ class CoreBankAccountsApiTest { fun accountReconfig() = bankSetup { _ -> // Successful attempt now. val cashout = IbanPayTo(genIbanPaytoUri()) - val req = json { + val req = obj { "cashout_payto_uri" to cashout.canonical - "challenge_contact_data" to json { + "challenge_contact_data" to obj { "phone" to "+99" "email" to "foo@example.com" } "is_taler_exchange" to true } - client.patch("/accounts/merchant") { - basicAuth("merchant", "merchant-password") - jsonBody(req) + client.patchA("/accounts/merchant") { + json(req) }.assertNoContent() // Checking idempotence. - client.patch("/accounts/merchant") { - basicAuth("merchant", "merchant-password") - jsonBody(req) + client.patchA("/accounts/merchant") { + json(req) }.assertNoContent() suspend fun checkAdminOnly(req: JsonElement, error: TalerErrorCode) { // Checking ordinary user doesn't get to patch - client.patch("/accounts/merchant") { - basicAuth("merchant", "merchant-password") - jsonBody(req) + client.patchA("/accounts/merchant") { + json(req) }.assertConflict(error) // Finally checking that admin does get to patch client.patch("/accounts/merchant") { - basicAuth("admin", "admin-password") - jsonBody(req) + pwAuth("admin") + json(req) }.assertNoContent() } checkAdminOnly( - json(req) { "name" to "Another Foo" }, + obj(req) { "name" to "Another Foo" }, TalerErrorCode.BANK_NON_ADMIN_PATCH_LEGAL_NAME ) checkAdminOnly( - json(req) { "debit_threshold" to "KUDOS:100" }, + obj(req) { "debit_threshold" to "KUDOS:100" }, TalerErrorCode.BANK_NON_ADMIN_PATCH_DEBT_LIMIT ) // Check admin account cannot be exchange - client.patch("/accounts/admin") { - basicAuth("admin", "admin-password") - jsonBody { "is_taler_exchange" to true } + client.patchA("/accounts/admin") { + json { "is_taler_exchange" to true } }.assertConflict(TalerErrorCode.BANK_PATCH_ADMIN_EXCHANGE) // But we can change its debt limit - client.patch("/accounts/admin") { - basicAuth("admin", "admin-password") - jsonBody { "debit_threshold" to "KUDOS:100" } + client.patchA("/accounts/admin") { + json { "debit_threshold" to "KUDOS:100" } }.assertNoContent() // Check currency client.patch("/accounts/merchant") { - basicAuth("admin", "admin-password") - jsonBody(req) { "debit_threshold" to "EUR:100" } + pwAuth("admin") + json(req) { "debit_threshold" to "EUR:100" } }.assertBadRequest() // Check patch client.get("/accounts/merchant") { - basicAuth("admin", "admin-password") - }.assertOk().run { - val obj: AccountData = json() + pwAuth("admin") + }.assertOkJson<AccountData> { obj -> assertEquals("Another Foo", obj.name) assertEquals(cashout.canonical, obj.cashout_payto_uri) assertEquals("+99", obj.contact_data?.phone) @@ -399,7 +382,7 @@ class CoreBankAccountsApiTest { // Changing the password. client.patch("/accounts/customer/auth") { basicAuth("customer", "customer-password") - jsonBody { + json { "old_password" to "customer-password" "new_password" to "new-password" } @@ -411,7 +394,7 @@ class CoreBankAccountsApiTest { // New password should succeed. client.patch("/accounts/customer/auth") { basicAuth("customer", "new-password") - jsonBody { + json { "old_password" to "new-password" "new_password" to "customer-password" } @@ -421,7 +404,7 @@ class CoreBankAccountsApiTest { // Check require test old password client.patch("/accounts/customer/auth") { basicAuth("customer", "customer-password") - jsonBody { + json { "old_password" to "bad-password" "new_password" to "new-password" } @@ -430,15 +413,15 @@ class CoreBankAccountsApiTest { // Check require old password for user client.patch("/accounts/customer/auth") { basicAuth("customer", "customer-password") - jsonBody { + json { "new_password" to "new-password" } }.assertConflict(TalerErrorCode.BANK_NON_ADMIN_PATCH_MISSING_OLD_PASSWORD) // Check admin client.patch("/accounts/customer/auth") { - basicAuth("admin", "admin-password") - jsonBody { + pwAuth("admin") + json { "new_password" to "new-password" } }.assertNoContent() @@ -450,19 +433,19 @@ class CoreBankAccountsApiTest { // Remove default accounts listOf("merchant", "exchange", "customer").forEach { client.delete("/accounts/$it") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertNoContent() } // Check error when no public accounts client.get("/public-accounts").assertNoContent() client.get("/accounts") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertOk() // Gen some public and private accounts repeat(5) { client.post("/accounts") { - jsonBody { + json { "username" to "$it" "password" to "password" "name" to "Mr $it" @@ -481,7 +464,7 @@ class CoreBankAccountsApiTest { } // All accounts client.get("/accounts?delta=10"){ - basicAuth("admin", "admin-password") + pwAuth("admin") }.run { assertOk() val obj = json<ListBankAccountsResponse>() @@ -496,7 +479,7 @@ class CoreBankAccountsApiTest { } // Filtering client.get("/accounts?filter_name=3"){ - basicAuth("admin", "admin-password") + pwAuth("admin") }.run { assertOk() val obj = json<ListBankAccountsResponse>() @@ -509,21 +492,18 @@ class CoreBankAccountsApiTest { @Test fun getAccountTest() = bankSetup { _ -> // Check ok - client.get("/accounts/merchant") { - basicAuth("merchant", "merchant-password") - }.assertOk().run { - val obj: AccountData = json() - assertEquals("Merchant", obj.name) + client.getA("/accounts/merchant").assertOkJson<AccountData> { + assertEquals("Merchant", it.name) } // Check admin ok client.get("/accounts/merchant") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertOk() // Check wrong user client.get("/accounts/exchange") { - basicAuth("merchant", "merchant-password") + pwAuth("merchant") }.assertUnauthorized() } } @@ -567,7 +547,7 @@ class CoreBankTransactionsApiTest { // Check error when no transactions client.get("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") + pwAuth("merchant") }.assertNoContent() // Gen three transactions from merchant to exchange @@ -582,30 +562,27 @@ class CoreBankTransactionsApiTest { // Check no useless polling assertTime(0, 200) { client.get("/accounts/merchant/transactions?delta=-6&start=11&long_poll_ms=1000") { - basicAuth("merchant", "merchant-password") + pwAuth("merchant") }.assertHistory(5) } // Check no polling when find transaction assertTime(0, 200) { - client.get("/accounts/merchant/transactions?delta=6&long_poll_ms=1000") { - basicAuth("merchant", "merchant-password") - }.assertHistory(5) + client.getA("/accounts/merchant/transactions?delta=6&long_poll_ms=1000") + .assertHistory(5) } coroutineScope { launch { // Check polling succeed assertTime(200, 1000) { - client.get("/accounts/merchant/transactions?delta=2&start=10&long_poll_ms=1000") { - basicAuth("merchant", "merchant-password") - }.assertHistory(1) + client.getA("/accounts/merchant/transactions?delta=2&start=10&long_poll_ms=1000") + .assertHistory(1) } } launch { // Check polling timeout assertTime(200, 400) { - client.get("/accounts/merchant/transactions?delta=1&start=11&long_poll_ms=300") { - basicAuth("merchant", "merchant-password") - }.assertNoContent() + client.getA("/accounts/merchant/transactions?delta=1&start=11&long_poll_ms=300") + .assertNoContent() } } delay(200) @@ -618,14 +595,12 @@ class CoreBankTransactionsApiTest { } // forward range: - client.get("/accounts/merchant/transactions?delta=10&start=20") { - basicAuth("merchant", "merchant-password") - }.assertHistory(10) + client.getA("/accounts/merchant/transactions?delta=10&start=20") + .assertHistory(10) // backward range: - client.get("/accounts/merchant/transactions?delta=-10&start=25") { - basicAuth("merchant", "merchant-password") - }.assertHistory(10) + client.getA("/accounts/merchant/transactions?delta=-10&start=25") + .assertHistory(10) } // GET /transactions/T_ID @@ -636,27 +611,23 @@ class CoreBankTransactionsApiTest { // Create transaction tx("merchant", "KUDOS:0.3", "exchange", "tx") // Check OK - client.get("/accounts/merchant/transactions/1") { - basicAuth("merchant", "merchant-password") - }.assertOk().run { - val tx: BankAccountTransactionInfo = json() + client.getA("/accounts/merchant/transactions/1") + .assertOkJson<BankAccountTransactionInfo> { tx -> assertEquals("tx", tx.subject) assertEquals(TalerAmount("KUDOS:0.3"), tx.amount) } // Check unknown transaction - client.get("/accounts/merchant/transactions/3") { - basicAuth("merchant", "merchant-password") - }.assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) + client.getA("/accounts/merchant/transactions/3") + .assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) // Check another user's transaction - client.get("/accounts/merchant/transactions/2") { - basicAuth("merchant", "merchant-password") - }.assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) + client.getA("/accounts/merchant/transactions/2") + .assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) } // POST /transactions @Test fun create() = bankSetup { _ -> - val valid_req = json { + val valid_req = obj { "payto_uri" to "$exchangePayto?message=payout" "amount" to "KUDOS:0.3" } @@ -664,90 +635,69 @@ class CoreBankTransactionsApiTest { authRoutine("/accounts/merchant/transactions") // Check OK - client.post("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") - jsonBody(valid_req) - }.assertOk().run { - val id = json<TransactionCreateResponse>().row_id - client.get("/accounts/merchant/transactions/$id") { - basicAuth("merchant", "merchant-password") - }.assertOk().run { - val tx: BankAccountTransactionInfo = json() + client.postA("/accounts/merchant/transactions") { + json(valid_req) + }.assertOkJson<TransactionCreateResponse> { + client.getA("/accounts/merchant/transactions/${it.row_id}") + .assertOkJson<BankAccountTransactionInfo> { tx -> assertEquals("payout", tx.subject) assertEquals(TalerAmount("KUDOS:0.3"), tx.amount) } } // Check amount in payto_uri - client.post("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") - jsonBody { + client.postA("/accounts/merchant/transactions") { + json { "payto_uri" to "$exchangePayto?message=payout2&amount=KUDOS:1.05" } - }.assertOk().run { - val id = json<TransactionCreateResponse>().row_id - client.get("/accounts/merchant/transactions/$id") { - basicAuth("merchant", "merchant-password") - }.assertOk().run { - val tx: BankAccountTransactionInfo = json() + }.assertOkJson <TransactionCreateResponse> { + client.getA("/accounts/merchant/transactions/${it.row_id}") + .assertOkJson<BankAccountTransactionInfo> { tx -> assertEquals("payout2", tx.subject) assertEquals(TalerAmount("KUDOS:1.05"), tx.amount) } } // Check amount in payto_uri precedence - client.post("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") - jsonBody { + client.postA("/accounts/merchant/transactions") { + json { "payto_uri" to "$exchangePayto?message=payout3&amount=KUDOS:1.05" "amount" to "KUDOS:10.003" } - }.assertOk().run { - val id = json<TransactionCreateResponse>().row_id - client.get("/accounts/merchant/transactions/$id") { - basicAuth("merchant", "merchant-password") - }.assertOk().run { - val tx: BankAccountTransactionInfo = json() + }.assertOkJson<TransactionCreateResponse> { + client.getA("/accounts/merchant/transactions/${it.row_id}") + .assertOkJson<BankAccountTransactionInfo> { tx -> assertEquals("payout3", tx.subject) assertEquals(TalerAmount("KUDOS:1.05"), tx.amount) } } // Testing the wrong currency - client.post("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") - jsonBody(valid_req) { + client.postA("/accounts/merchant/transactions") { + json(valid_req) { "amount" to "EUR:3.3" } }.assertBadRequest(TalerErrorCode.GENERIC_CURRENCY_MISMATCH) // Surpassing the debt limit - client.post("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") - contentType(ContentType.Application.Json) - jsonBody(valid_req) { + client.postA("/accounts/merchant/transactions") { + json(valid_req) { "amount" to "KUDOS:555" } }.assertConflict(TalerErrorCode.BANK_UNALLOWED_DEBIT) // Missing message - client.post("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") - contentType(ContentType.Application.Json) - jsonBody(valid_req) { + client.postA("/accounts/merchant/transactions") { + json(valid_req) { "payto_uri" to "$exchangePayto" } }.assertBadRequest() // Unknown creditor - client.post("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") - contentType(ContentType.Application.Json) - jsonBody(valid_req) { + client.postA("/accounts/merchant/transactions") { + json(valid_req) { "payto_uri" to "$unknownPayto?message=payout" } }.assertConflict(TalerErrorCode.BANK_UNKNOWN_CREDITOR) // Transaction to self - client.post("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") - contentType(ContentType.Application.Json) - jsonBody(valid_req) { + client.postA("/accounts/merchant/transactions") { + json(valid_req) { "payto_uri" to "$merchantPayto?message=payout" } }.assertConflict(TalerErrorCode.BANK_SAME_ACCOUNT) @@ -758,47 +708,34 @@ class CoreBankTransactionsApiTest { customerDebt: Boolean, customerAmount: String, ) { - client.get("/accounts/merchant") { - basicAuth("admin", "admin-password") - }.assertOk().run { - val obj: AccountData = json() - assertEquals( - if (merchantDebt) CreditDebitInfo.debit else CreditDebitInfo.credit, - obj.balance.credit_debit_indicator) - assertEquals(TalerAmount(merchantAmount), obj.balance.amount) - } - client.get("/accounts/customer") { - basicAuth("admin", "admin-password") - }.assertOk().run { - val obj: AccountData = json() - assertEquals( - if (customerDebt) CreditDebitInfo.debit else CreditDebitInfo.credit, - obj.balance.credit_debit_indicator) - assertEquals(TalerAmount(customerAmount), obj.balance.amount) - } + assertBalance("merchant", if (merchantDebt) CreditDebitInfo.debit else CreditDebitInfo.credit, merchantAmount) + assertBalance("customer", if (customerDebt) CreditDebitInfo.debit else CreditDebitInfo.credit, customerAmount) } // Init state - checkBalance(true, "KUDOS:2.4", false, "KUDOS:0") + assertBalance("merchant", CreditDebitInfo.debit, "KUDOS:2.4") + assertBalance("customer", CreditDebitInfo.credit, "KUDOS:0") // Send 2 times 3 repeat(2) { tx("merchant", "KUDOS:3", "customer") } client.post("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") - jsonBody { + pwAuth("merchant") + json { "payto_uri" to "$customerPayto?message=payout2&amount=KUDOS:3" } }.assertConflict(TalerErrorCode.BANK_UNALLOWED_DEBIT) - checkBalance(true, "KUDOS:8.4", false, "KUDOS:6") + assertBalance("merchant", CreditDebitInfo.debit, "KUDOS:8.4") + assertBalance("customer", CreditDebitInfo.credit, "KUDOS:6") // Send throught debt client.post("/accounts/customer/transactions") { - basicAuth("customer", "customer-password") - jsonBody { + pwAuth("customer") + json { "payto_uri" to "$merchantPayto?message=payout2&amount=KUDOS:10" } }.assertOk() - checkBalance(false, "KUDOS:1.6", true, "KUDOS:4") + assertBalance("merchant", CreditDebitInfo.credit, "KUDOS:1.6") + assertBalance("customer", CreditDebitInfo.debit, "KUDOS:4") } } @@ -807,21 +744,18 @@ class CoreBankWithdrawalApiTest { @Test fun create() = bankSetup { _ -> // Check OK - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:9.0" } + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:9.0" } }.assertOk() // Check exchange account - client.post("/accounts/exchange/withdrawals") { - basicAuth("exchange", "exchange-password") - jsonBody { "amount" to "KUDOS:9.0" } + client.postA("/accounts/exchange/withdrawals") { + json { "amount" to "KUDOS:9.0" } }.assertConflict(TalerErrorCode.BANK_ACCOUNT_IS_EXCHANGE) // Check insufficient fund - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:90" } + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:90" } }.assertConflict(TalerErrorCode.BANK_UNALLOWED_DEBIT) } @@ -829,13 +763,11 @@ class CoreBankWithdrawalApiTest { @Test fun get() = bankSetup { _ -> // Check OK - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:9.0" } - }.assertOk().run { - val opId = json<BankAccountCreateWithdrawalResponse>() - client.get("/withdrawals/${opId.withdrawal_id}") { - basicAuth("merchant", "merchant-password") + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:9.0" } + }.assertOkJson<BankAccountCreateWithdrawalResponse> { + client.get("/withdrawals/${it.withdrawal_id}") { + pwAuth("merchant") }.assertOk() } @@ -851,12 +783,10 @@ class CoreBankWithdrawalApiTest { @Test fun abort() = bankSetup { _ -> // Check abort created - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:1" } - }.assertOk().run { - val resp = json<BankAccountCreateWithdrawalResponse>() - val uuid = resp.taler_withdraw_uri.split("/").last() + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:1" } + }.assertOkJson<BankAccountCreateWithdrawalResponse> { + val uuid = it.taler_withdraw_uri.split("/").last() // Check OK client.post("/withdrawals/$uuid/abort").assertNoContent() @@ -865,14 +795,12 @@ class CoreBankWithdrawalApiTest { } // Check abort selected - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:1" } - }.assertOk().run { - val resp = json<BankAccountCreateWithdrawalResponse>() - val uuid = resp.taler_withdraw_uri.split("/").last() + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:1" } + }.assertOkJson<BankAccountCreateWithdrawalResponse> { + val uuid = it.taler_withdraw_uri.split("/").last() client.post("/taler-integration/withdrawal-operation/$uuid") { - jsonBody { + json { "reserve_pub" to randEddsaPublicKey() "selected_exchange" to exchangePayto } @@ -885,14 +813,12 @@ class CoreBankWithdrawalApiTest { } // Check abort confirmed - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:1" } - }.assertOk().run { - val resp = json<BankAccountCreateWithdrawalResponse>() - val uuid = resp.taler_withdraw_uri.split("/").last() + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:1" } + }.assertOkJson<BankAccountCreateWithdrawalResponse> { + val uuid = it.taler_withdraw_uri.split("/").last() client.post("/taler-integration/withdrawal-operation/$uuid") { - jsonBody { + json { "reserve_pub" to randEddsaPublicKey() "selected_exchange" to exchangePayto } @@ -916,12 +842,10 @@ class CoreBankWithdrawalApiTest { @Test fun confirm() = bankSetup { _ -> // Check confirm created - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:1" } - }.assertOk().run { - val resp = json<BankAccountCreateWithdrawalResponse>() - val uuid = resp.taler_withdraw_uri.split("/").last() + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:1" } + }.assertOkJson<BankAccountCreateWithdrawalResponse> { + val uuid = it.taler_withdraw_uri.split("/").last() // Check err client.post("/withdrawals/$uuid/confirm") @@ -929,14 +853,12 @@ class CoreBankWithdrawalApiTest { } // Check confirm selected - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:1" } - }.assertOk().run { - val resp = json<BankAccountCreateWithdrawalResponse>() - val uuid = resp.taler_withdraw_uri.split("/").last() + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:1" } + }.assertOkJson<BankAccountCreateWithdrawalResponse> { + val uuid = it.taler_withdraw_uri.split("/").last() client.post("/taler-integration/withdrawal-operation/$uuid") { - jsonBody { + json { "reserve_pub" to randEddsaPublicKey() "selected_exchange" to exchangePayto } @@ -949,14 +871,12 @@ class CoreBankWithdrawalApiTest { } // Check confirm aborted - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:1" } - }.assertOk().run { - val resp = json<BankAccountCreateWithdrawalResponse>() - val uuid = resp.taler_withdraw_uri.split("/").last() + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:1" } + }.assertOkJson<BankAccountCreateWithdrawalResponse> { + val uuid = it.taler_withdraw_uri.split("/").last() client.post("/taler-integration/withdrawal-operation/$uuid") { - jsonBody { + json { "reserve_pub" to randEddsaPublicKey() "selected_exchange" to exchangePayto } @@ -969,14 +889,12 @@ class CoreBankWithdrawalApiTest { } // Check balance insufficient - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:5" } - }.assertOk().run { - val resp = json<BankAccountCreateWithdrawalResponse>() - val uuid = resp.taler_withdraw_uri.split("/").last() + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:5" } + }.assertOkJson<BankAccountCreateWithdrawalResponse> { + val uuid = it.taler_withdraw_uri.split("/").last() client.post("/taler-integration/withdrawal-operation/$uuid") { - jsonBody { + json { "reserve_pub" to randEddsaPublicKey() "selected_exchange" to exchangePayto } @@ -1006,94 +924,82 @@ class CoreBankCashoutApiTest { @Test fun create() = bankSetup { _ -> // TODO auth routine - val req = json { + val req = obj { "request_uid" to randShortHashCode() "amount_debit" to "KUDOS:1" "amount_credit" to convert("KUDOS:1") } // Check missing TAN info - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) + client.postA("/accounts/customer/cashouts") { + json(req) }.assertConflict(TalerErrorCode.BANK_MISSING_TAN_INFO) - client.patch("/accounts/customer") { - basicAuth("customer", "customer-password") - jsonBody(json { - "challenge_contact_data" to json { + client.patchA("/accounts/customer") { + json { + "challenge_contact_data" to obj { "phone" to "+99" "email" to "foo@example.com" } - }) + } }.assertNoContent() // Check email TAN error - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) { + client.postA("/accounts/customer/cashouts") { + json(req) { "tan_channel" to "email" } }.assertStatus(HttpStatusCode.BadGateway, TalerErrorCode.BANK_TAN_CHANNEL_SCRIPT_FAILED) // Check OK - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) - }.assertOk().run { - val id = json<CashoutPending>().cashout_id + client.postA("/accounts/customer/cashouts") { + json(req) + }.assertOkJson<CashoutPending> { first -> smsCode("+99") // Check idempotency - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) - }.assertOk().run { - assertEquals(id, json<CashoutPending>().cashout_id) + client.postA("/accounts/customer/cashouts") { + json(req) + }.assertOkJson<CashoutPending> { second -> + assertEquals(first.cashout_id, second.cashout_id) assertNull(smsCode("+99")) } } // Trigger conflict due to reused request_uid - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) { + client.postA("/accounts/customer/cashouts") { + json(req) { "amount_debit" to "KUDOS:2" "amount_credit" to convert("KUDOS:2") } }.assertConflict(TalerErrorCode.BANK_TRANSFER_REQUEST_UID_REUSED) // Check exchange account - client.post("/accounts/exchange/cashouts") { - basicAuth("exchange", "exchange-password") - jsonBody(req) + client.postA("/accounts/exchange/cashouts") { + json(req) }.assertConflict(TalerErrorCode.BANK_ACCOUNT_IS_EXCHANGE) // Check insufficient fund - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) { + client.postA("/accounts/customer/cashouts") { + json(req) { "amount_debit" to "KUDOS:75" "amount_credit" to convert("KUDOS:75") } }.assertConflict(TalerErrorCode.BANK_UNALLOWED_DEBIT) // Check wrong conversion - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) { + client.postA("/accounts/customer/cashouts") { + json(req) { "amount_credit" to convert("KUDOS:2") } }.assertConflict(TalerErrorCode.BANK_BAD_CONVERSION) // Check wrong currency - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) { + client.postA("/accounts/customer/cashouts") { + json(req) { "amount_debit" to "EUR:1" } }.assertBadRequest(TalerErrorCode.GENERIC_CURRENCY_MISMATCH) - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) { + client.postA("/accounts/customer/cashouts") { + json(req) { "amount_credit" to "EUR:1" } }.assertBadRequest(TalerErrorCode.GENERIC_CURRENCY_MISMATCH) @@ -1102,16 +1008,15 @@ class CoreBankCashoutApiTest { // POST /accounts/{USERNAME}/cashouts @Test fun create_no_tan() = bankSetup("test_no_tan.conf") { _ -> - val req = json { + val req = obj { "request_uid" to randShortHashCode() "amount_debit" to "KUDOS:1" "amount_credit" to convert("KUDOS:1") } // Check unsupported TAN channel - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) + client.postA("/accounts/customer/cashouts") { + json(req) }.assertStatus(HttpStatusCode.NotImplemented, TalerErrorCode.BANK_TAN_CHANNEL_NOT_SUPPORTED) } @@ -1121,70 +1026,60 @@ class CoreBankCashoutApiTest { // TODO auth routine fillCashoutInfo("customer") - val req = json { + val req = obj { "request_uid" to randShortHashCode() "amount_debit" to "KUDOS:1" "amount_credit" to convert("KUDOS:1") } // Check abort created - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) - }.assertOk().run { - val id = json<CashoutPending>().cashout_id + client.postA("/accounts/customer/cashouts") { + json(req) + }.assertOkJson<CashoutPending> { + val id = it.cashout_id // Check OK - client.post("/accounts/customer/cashouts/$id/abort") { - basicAuth("customer", "customer-password") - }.assertNoContent() + client.postA("/accounts/customer/cashouts/$id/abort") + .assertNoContent() // Check idempotence - client.post("/accounts/customer/cashouts/$id/abort") { - basicAuth("customer", "customer-password") - }.assertNoContent() + client.postA("/accounts/customer/cashouts/$id/abort") + .assertNoContent() } // Check abort confirmed - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(json(req) { "request_uid" to randShortHashCode() }) - }.assertOk().run { - val id = json<CashoutPending>().cashout_id + client.postA("/accounts/customer/cashouts") { + json(req) { "request_uid" to randShortHashCode() } + }.assertOkJson<CashoutPending> { + val id = it.cashout_id - client.post("/accounts/customer/cashouts/$id/confirm") { - basicAuth("customer", "customer-password") - jsonBody { "tan" to smsCode("+99") } + client.postA("/accounts/customer/cashouts/$id/confirm") { + json { "tan" to smsCode("+99") } }.assertNoContent() // Check error - client.post("/accounts/customer/cashouts/$id/abort") { - basicAuth("customer", "customer-password") - }.assertConflict(TalerErrorCode.BANK_ABORT_CONFIRM_CONFLICT) + client.postA("/accounts/customer/cashouts/$id/abort") + .assertConflict(TalerErrorCode.BANK_ABORT_CONFIRM_CONFLICT) } // Check bad id - client.post("/accounts/customer/cashouts/chocolate/abort") { - basicAuth("customer", "customer-password") - jsonBody { "tan" to "code" } + client.postA("/accounts/customer/cashouts/chocolate/abort") { + json { "tan" to "code" } }.assertBadRequest() // Check unknown - client.post("/accounts/customer/cashouts/42/abort") { - basicAuth("customer", "customer-password") - jsonBody { "tan" to "code" } + client.postA("/accounts/customer/cashouts/42/abort") { + json { "tan" to "code" } }.assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) // Check abort another user's operation - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(json(req) { "request_uid" to randShortHashCode() }) - }.assertOk().run { - val id = json<CashoutPending>().cashout_id + client.postA("/accounts/customer/cashouts") { + json(req) { "request_uid" to randShortHashCode() } + }.assertOkJson<CashoutPending> { + val id = it.cashout_id // Check error - client.post("/accounts/merchant/cashouts/$id/abort") { - basicAuth("merchant", "merchant-password") - }.assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) + client.postA("/accounts/merchant/cashouts/$id/abort") + .assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) } } @@ -1192,78 +1087,69 @@ class CoreBankCashoutApiTest { @Test fun confirm() = bankSetup { db -> // TODO auth routine - client.patch("/accounts/customer") { - basicAuth("customer", "customer-password") - jsonBody(json { - "challenge_contact_data" to json { + client.patchA("/accounts/customer") { + json { + "challenge_contact_data" to obj { "phone" to "+99" } - }) + } }.assertNoContent() - val req = json { + val req = obj { "request_uid" to randShortHashCode() "amount_debit" to "KUDOS:1" "amount_credit" to convert("KUDOS:1") } // Check confirm - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) { "request_uid" to randShortHashCode() } - }.assertOk().run { - val id = json<CashoutPending>().cashout_id + client.postA("/accounts/customer/cashouts") { + json(req) { "request_uid" to randShortHashCode() } + }.assertOkJson<CashoutPending> { + val id = it.cashout_id // Check missing cashout address - client.post("/accounts/customer/cashouts/$id/confirm") { - basicAuth("customer", "customer-password") - jsonBody { "tan" to "code" } + client.postA("/accounts/customer/cashouts/$id/confirm") { + json { "tan" to "code" } }.assertConflict(TalerErrorCode.BANK_CONFIRM_INCOMPLETE) fillCashoutInfo("customer") // Check bad TAN code - client.post("/accounts/customer/cashouts/$id/confirm") { - basicAuth("customer", "customer-password") - jsonBody { "tan" to "nice-try" } + client.postA("/accounts/customer/cashouts/$id/confirm") { + json { "tan" to "nice-try" } }.assertConflict(TalerErrorCode.BANK_TAN_CHALLENGE_FAILED) val code = smsCode("+99") // Check OK - client.post("/accounts/customer/cashouts/$id/confirm") { - basicAuth("customer", "customer-password") - jsonBody { "tan" to code } + client.postA("/accounts/customer/cashouts/$id/confirm") { + json { "tan" to code } }.assertNoContent() // Check idempotence - client.post("/accounts/customer/cashouts/$id/confirm") { - basicAuth("customer", "customer-password") - jsonBody { "tan" to code } + client.postA("/accounts/customer/cashouts/$id/confirm") { + json { "tan" to code } }.assertNoContent() } // Check confirm another user's operation - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(json(req) { + client.postA("/accounts/customer/cashouts") { + json(req) { "request_uid" to randShortHashCode() "amount_credit" to convert("KUDOS:1") - }) - }.assertOk().run { - val id = json<CashoutPending>().cashout_id + } + }.assertOkJson<CashoutPending> { + val id = it.cashout_id // Check error - client.post("/accounts/merchant/cashouts/$id/confirm") { - basicAuth("merchant", "merchant-password") - jsonBody { "tan" to "unused" } + client.postA("/accounts/merchant/cashouts/$id/confirm") { + json { "tan" to "unused" } }.assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) } // Check bad conversion - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(json(req) { "request_uid" to randShortHashCode() }) - }.assertOk().run { - val id = json<CashoutPending>().cashout_id + client.postA("/accounts/customer/cashouts") { + json(req) { "request_uid" to randShortHashCode() } + }.assertOkJson<CashoutPending> { + val id = it.cashout_id db.conversion.updateConfig(ConversionInfo( buy_ratio = DecimalNumber("1"), @@ -1278,49 +1164,42 @@ class CoreBankCashoutApiTest { sell_min_amount = TalerAmount("KUDOS:0.0001"), )) - client.post("/accounts/customer/cashouts/$id/confirm"){ - basicAuth("customer", "customer-password") - jsonBody { "tan" to smsCode("+99") } + client.postA("/accounts/customer/cashouts/$id/confirm"){ + json { "tan" to smsCode("+99") } }.assertConflict(TalerErrorCode.BANK_BAD_CONVERSION) // Check can abort because not confirmed - client.post("/accounts/customer/cashouts/$id/abort") { - basicAuth("customer", "customer-password") - }.assertNoContent() + client.postA("/accounts/customer/cashouts/$id/abort") + .assertNoContent() } // Check balance insufficient - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(json(req) { + client.postA("/accounts/customer/cashouts") { + json(req) { "request_uid" to randShortHashCode() "amount_credit" to convert("KUDOS:1") - }) - }.assertOk().run { - val id = json<CashoutPending>().cashout_id + } + }.assertOkJson<CashoutPending> { + val id = it.cashout_id // Send too much money tx("customer", "KUDOS:9", "merchant") - client.post("/accounts/customer/cashouts/$id/confirm"){ - basicAuth("customer", "customer-password") - jsonBody { "tan" to smsCode("+99") } + client.postA("/accounts/customer/cashouts/$id/confirm"){ + json { "tan" to smsCode("+99") } }.assertConflict(TalerErrorCode.BANK_UNALLOWED_DEBIT) // Check can abort because not confirmed - client.post("/accounts/customer/cashouts/$id/abort") { - basicAuth("customer", "customer-password") - }.assertNoContent() + client.postA("/accounts/customer/cashouts/$id/abort") + .assertNoContent() } // Check bad UUID - client.post("/accounts/customer/cashouts/chocolate/confirm") { - basicAuth("customer", "customer-password") - jsonBody { "tan" to "code" } + client.postA("/accounts/customer/cashouts/chocolate/confirm") { + json { "tan" to "code" } }.assertBadRequest() // Check unknown - client.post("/accounts/customer/cashouts/42/confirm") { - basicAuth("customer", "customer-password") - jsonBody { "tan" to "code" } + client.postA("/accounts/customer/cashouts/42/confirm") { + json { "tan" to "code" } }.assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) } @@ -1332,80 +1211,67 @@ class CoreBankCashoutApiTest { val amountDebit = TalerAmount("KUDOS:1.5") val amountCredit = convert("KUDOS:1.5") - val req = json { + val req = obj { "amount_debit" to amountDebit "amount_credit" to amountCredit } // Check confirm - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) { "request_uid" to randShortHashCode() } - }.assertOk().run { - val id = json<CashoutPending>().cashout_id - client.get("/accounts/customer/cashouts/$id") { - basicAuth("customer", "customer-password") - }.assertOk().run { - val res = json<CashoutStatusResponse>() - assertEquals(CashoutStatus.pending, res.status) - assertEquals(amountDebit, res.amount_debit) - assertEquals(amountCredit, res.amount_credit) + client.postA("/accounts/customer/cashouts") { + json(req) { "request_uid" to randShortHashCode() } + }.assertOkJson<CashoutPending> { + val id = it.cashout_id + client.getA("/accounts/customer/cashouts/$id") + .assertOkJson<CashoutStatusResponse> { + assertEquals(CashoutStatus.pending, it.status) + assertEquals(amountDebit, it.amount_debit) + assertEquals(amountCredit, it.amount_credit) } - client.post("/accounts/customer/cashouts/$id/confirm") { - basicAuth("customer", "customer-password") - jsonBody { "tan" to smsCode("+99") } + client.postA("/accounts/customer/cashouts/$id/confirm") { + json { "tan" to smsCode("+99") } }.assertNoContent() - client.get("/accounts/customer/cashouts/$id") { - basicAuth("customer", "customer-password") - }.assertOk().run { - assertEquals(CashoutStatus.confirmed, json<CashoutStatusResponse>().status) + client.getA("/accounts/customer/cashouts/$id") + .assertOkJson<CashoutStatusResponse> { + assertEquals(CashoutStatus.confirmed, it.status) } } // Check abort - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(req) { "request_uid" to randShortHashCode() } - }.assertOk().run { - val id = json<CashoutPending>().cashout_id - client.get("/accounts/customer/cashouts/$id") { - basicAuth("customer", "customer-password") - }.assertOk().run { - assertEquals(CashoutStatus.pending, json<CashoutStatusResponse>().status) + client.postA("/accounts/customer/cashouts") { + json(req) { "request_uid" to randShortHashCode() } + }.assertOkJson<CashoutPending> { + val id = it.cashout_id + client.getA("/accounts/customer/cashouts/$id") + .assertOkJson<CashoutStatusResponse> { + assertEquals(CashoutStatus.pending, it.status) } - client.post("/accounts/customer/cashouts/$id/abort") { - basicAuth("customer", "customer-password") - }.assertNoContent() - client.get("/accounts/customer/cashouts/$id") { - basicAuth("customer", "customer-password") - }.assertOk().run { - assertEquals(CashoutStatus.aborted, json<CashoutStatusResponse>().status) + client.postA("/accounts/customer/cashouts/$id/abort") + .assertNoContent() + client.getA("/accounts/customer/cashouts/$id") + .assertOkJson<CashoutStatusResponse> { + assertEquals(CashoutStatus.aborted, it.status) } } // Check bad UUID - client.get("/accounts/customer/cashouts/chocolate") { - basicAuth("customer", "customer-password") - }.assertBadRequest() + client.getA("/accounts/customer/cashouts/chocolate") + .assertBadRequest() // Check unknown - client.get("/accounts/customer/cashouts/42") { - basicAuth("customer", "customer-password") - }.assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) + client.getA("/accounts/customer/cashouts/42") + .assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) // Check get another user's operation - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(json(req) { "request_uid" to randShortHashCode() }) - }.assertOk().run { - val id = json<CashoutPending>().cashout_id + client.postA("/accounts/customer/cashouts") { + json(req) { "request_uid" to randShortHashCode() } + }.assertOkJson<CashoutPending> { + val id = it.cashout_id // Check error - client.get("/accounts/merchant/cashouts/$id") { - basicAuth("merchant", "merchant-password") - }.assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) + client.getA("/accounts/merchant/cashouts/$id") + .assertNotFound(TalerErrorCode.BANK_TRANSACTION_NOT_FOUND) } } @@ -1423,9 +1289,8 @@ class CoreBankCashoutApiTest { } // Empty - client.get("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - }.assertNoContent() + client.getA("/accounts/customer/cashouts") + .assertNoContent() // Testing ranges. repeat(30) { @@ -1433,19 +1298,16 @@ class CoreBankCashoutApiTest { } // Default - client.get("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - }.assertHistory(20) + client.getA("/accounts/customer/cashouts") + .assertHistory(20) // Forward range: - client.get("/accounts/customer/cashouts?delta=10&start=20") { - basicAuth("customer", "customer-password") - }.assertHistory(10) + client.getA("/accounts/customer/cashouts?delta=10&start=20") + .assertHistory(10) // Fackward range: - client.get("/accounts/customer/cashouts?delta=-10&start=25") { - basicAuth("customer", "customer-password") - }.assertHistory(10) + client.getA("/accounts/customer/cashouts?delta=-10&start=25") + .assertHistory(10) } // GET /cashouts @@ -1463,7 +1325,7 @@ class CoreBankCashoutApiTest { // Empty client.get("/cashouts") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertNoContent() // Testing ranges. @@ -1473,17 +1335,17 @@ class CoreBankCashoutApiTest { // Default client.get("/cashouts") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertHistory(20) // Forward range: client.get("/cashouts?delta=10&start=20") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertHistory(10) // Fackward range: client.get("/cashouts?delta=-10&start=25") { - basicAuth("admin", "admin-password") + pwAuth("admin") }.assertHistory(10) } @@ -1491,9 +1353,8 @@ class CoreBankCashoutApiTest { @Test fun cashoutRate() = bankSetup { _ -> // Check conversion - client.get("/cashout-rate?amount_debit=KUDOS:1").assertOk().run { - val resp = json<ConversionResponse>() - assertEquals(TalerAmount("FIAT:1.247"), resp.amount_credit) + client.get("/cashout-rate?amount_debit=KUDOS:1").assertOkJson<ConversionResponse> { + assertEquals(TalerAmount("FIAT:1.247"), it.amount_credit) } // Not implemented (yet) client.get("/cashout-rate?amount_credit=FIAT:1") @@ -1527,9 +1388,8 @@ class CoreBankCashoutApiTest { for ((amount, converted) in listOf( Pair(0.75, 0.58), Pair(0.33, 0.24), Pair(0.66, 0.51) )) { - client.get("/cashin-rate?amount_debit=FIAT:$amount").assertOk().run { - val resp = json<ConversionResponse>() - assertEquals(TalerAmount("KUDOS:$converted"), resp.amount_credit) + client.get("/cashin-rate?amount_debit=FIAT:$amount").assertOkJson<ConversionResponse> { + assertEquals(TalerAmount("KUDOS:$converted"), it.amount_credit) } } // Not implemented (yet) diff --git a/bank/src/test/kotlin/DatabaseTest.kt b/bank/src/test/kotlin/DatabaseTest.kt @@ -74,9 +74,7 @@ class DatabaseTest { tx("customer", "KUDOS:0.0$it", "merchant", "concurrent 0$it") } launch { - client.get("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") - }.assertOk() + client.getA("/accounts/merchant/transactions").assertOk() } } } diff --git a/bank/src/test/kotlin/RevenueApiTest.kt b/bank/src/test/kotlin/RevenueApiTest.kt @@ -31,9 +31,8 @@ class RevenueApiTest { // TODO auth routine // Check error when no transactions - client.get("/accounts/merchant/taler-revenue/history?delta=7") { - basicAuth("merchant", "merchant-password") - }.assertNoContent() + client.getA("/accounts/merchant/taler-revenue/history?delta=7") + .assertNoContent() // Gen three transactions using clean transfer logic repeat(3) { @@ -49,42 +48,36 @@ class RevenueApiTest { } // Check ignore bogus subject - client.get("/accounts/merchant/taler-revenue/history?delta=7") { - basicAuth("merchant", "merchant-password") - }.assertHistory(5) + client.getA("/accounts/merchant/taler-revenue/history?delta=7") + .assertHistory(5) // Check skip bogus subject - client.get("/accounts/merchant/taler-revenue/history?delta=5") { - basicAuth("merchant", "merchant-password") - }.assertHistory(5) + client.getA("/accounts/merchant/taler-revenue/history?delta=5") + .assertHistory(5) // Check no useless polling assertTime(0, 200) { - client.get("/accounts/merchant/taler-revenue/history?delta=-6&start=14&long_poll_ms=1000") { - basicAuth("merchant", "merchant-password") - }.assertHistory(5) + client.getA("/accounts/merchant/taler-revenue/history?delta=-6&start=14&long_poll_ms=1000") + .assertHistory(5) } // Check no polling when find transaction assertTime(0, 200) { - client.get("/accounts/merchant/taler-revenue/history?delta=6&long_poll_ms=1000") { - basicAuth("merchant", "merchant-password") - }.assertHistory(5) + client.getA("/accounts/merchant/taler-revenue/history?delta=6&long_poll_ms=1000") + .assertHistory(5) } coroutineScope { launch { // Check polling succeed forward assertTime(200, 300) { - client.get("/accounts/merchant/taler-revenue/history?delta=2&start=13&long_poll_ms=1000") { - basicAuth("merchant", "merchant-password") - }.assertHistory(1) + client.getA("/accounts/merchant/taler-revenue/history?delta=2&start=13&long_poll_ms=1000") + .assertHistory(1) } } launch { // Check polling timeout forward assertTime(200, 400) { - client.get("/accounts/merchant/taler-revenue/history?delta=1&start=15&long_poll_ms=300") { - basicAuth("merchant", "merchant-password") - }.assertNoContent() + client.getA("/accounts/merchant/taler-revenue/history?delta=1&start=15&long_poll_ms=300") + .assertNoContent() } } delay(200) @@ -97,13 +90,11 @@ class RevenueApiTest { } // forward range: - client.get("/accounts/merchant/taler-revenue/history?delta=10&start=20") { - basicAuth("merchant", "merchant-password") - }.assertHistory(10) + client.getA("/accounts/merchant/taler-revenue/history?delta=10&start=20") + .assertHistory(10) // backward range: - client.get("/accounts/merchant/taler-revenue/history?delta=-10&start=25") { - basicAuth("merchant", "merchant-password") - }.assertHistory(10) + client.getA("/accounts/merchant/taler-revenue/history?delta=-10&start=25") + .assertHistory(10) } } \ No newline at end of file diff --git a/bank/src/test/kotlin/SecurityTest.kt b/bank/src/test/kotlin/SecurityTest.kt @@ -34,27 +34,24 @@ import kotlinx.coroutines.* class SecurityTest { @Test fun bodySizeLimit() = bankSetup { _ -> - val valid_req = json { + val valid_req = obj { "payto_uri" to "$exchangePayto?message=payout" "amount" to "KUDOS:0.3" } - client.post("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") - jsonBody(valid_req) + client.postA("/accounts/merchant/transactions") { + json(valid_req) }.assertOk() // Check body too big - client.post("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") - jsonBody(valid_req) { + client.postA("/accounts/merchant/transactions") { + json(valid_req) { "payto_uri" to "$exchangePayto?message=payout${"A".repeat(4100)}" } }.assertBadRequest() // Check body too big even after compression - client.post("/accounts/merchant/transactions") { - basicAuth("merchant", "merchant-password") - jsonBody(valid_req, deflate = true) { + client.postA("/accounts/merchant/transactions") { + json(valid_req, deflate = true) { "payto_uri" to "$exchangePayto?message=payout${"A".repeat(4100)}" } }.assertBadRequest() diff --git a/bank/src/test/kotlin/StatsTest.kt b/bank/src/test/kotlin/StatsTest.kt @@ -63,9 +63,8 @@ class StatsTest { fiatAmount: String? = null ) { Timeframe.entries.forEach { timestamp -> - client.get("/monitor?timestamp=${timestamp.name}") { basicAuth("admin", "admin-password") }.assertOk().run { - println(bodyAsText()) - val resp = json<MonitorResponse>() as MonitorWithConversion + client.get("/monitor?timestamp=${timestamp.name}") { pwAuth("admin") }.assertOkJson<MonitorResponse> { + val resp = it as MonitorWithConversion assertEquals(count, dbCount(resp)) assertEquals(TalerAmount(regionalAmount), regionalVolume(resp)) fiatVolume?.run { assertEquals(TalerAmount(fiatAmount!!), this(resp)) } diff --git a/bank/src/test/kotlin/WireGatewayApiTest.kt b/bank/src/test/kotlin/WireGatewayApiTest.kt @@ -43,25 +43,25 @@ class WireGatewayApiTest { // Not exchange account client.request(path) { this.method = method - if (body != null) jsonBody(body) - basicAuth("merchant", "merchant-password") + if (body != null) json(body) + pwAuth("merchant") }.assertUnauthorized() } // Not exchange account client.request(path) { this.method = method - if (body != null) jsonBody(body) + if (body != null) json(body) if (requireAdmin) - basicAuth("admin", "admin-password") - else basicAuth("merchant", "merchant-password") + pwAuth("admin") + else pwAuth("merchant") }.assertConflict(TalerErrorCode.BANK_ACCOUNT_IS_NOT_EXCHANGE) } // Testing the POST /transfer call from the TWG API. @Test fun transfer() = bankSetup { _ -> - val valid_req = json { + val valid_req = obj { "request_uid" to randHashCode() "amount" to "KUDOS:55" "exchange_base_url" to "http://exchange.example.com/" @@ -72,45 +72,39 @@ class WireGatewayApiTest { authRoutine("/accounts/merchant/taler-wire-gateway/transfer", valid_req) // Checking exchange debt constraint. - client.post("/accounts/exchange/taler-wire-gateway/transfer") { - basicAuth("exchange", "exchange-password") - jsonBody(valid_req) + client.postA("/accounts/exchange/taler-wire-gateway/transfer") { + json(valid_req) }.assertConflict(TalerErrorCode.BANK_UNALLOWED_DEBIT) // Giving debt allowance and checking the OK case. setMaxDebt("exchange", TalerAmount("KUDOS:1000")) - client.post("/accounts/exchange/taler-wire-gateway/transfer") { - basicAuth("exchange", "exchange-password") - jsonBody(valid_req) + client.postA("/accounts/exchange/taler-wire-gateway/transfer") { + json(valid_req) }.assertOk() // check idempotency - client.post("/accounts/exchange/taler-wire-gateway/transfer") { - basicAuth("exchange", "exchange-password") - jsonBody(valid_req) + client.postA("/accounts/exchange/taler-wire-gateway/transfer") { + json(valid_req) }.assertOk() // Trigger conflict due to reused request_uid - client.post("/accounts/exchange/taler-wire-gateway/transfer") { - basicAuth("exchange", "exchange-password") - jsonBody(valid_req) { + client.postA("/accounts/exchange/taler-wire-gateway/transfer") { + json(valid_req) { "wtid" to randShortHashCode() "exchange_base_url" to "http://different-exchange.example.com/" } }.assertConflict(TalerErrorCode.BANK_TRANSFER_REQUEST_UID_REUSED) // Currency mismatch - client.post("/accounts/exchange/taler-wire-gateway/transfer") { - basicAuth("exchange", "exchange-password") - jsonBody(valid_req) { + client.postA("/accounts/exchange/taler-wire-gateway/transfer") { + json(valid_req) { "amount" to "EUR:33" } }.assertBadRequest(TalerErrorCode.GENERIC_CURRENCY_MISMATCH) // Unknown account - client.post("/accounts/exchange/taler-wire-gateway/transfer") { - basicAuth("exchange", "exchange-password") - jsonBody(valid_req) { + client.postA("/accounts/exchange/taler-wire-gateway/transfer") { + json(valid_req) { "request_uid" to randHashCode() "wtid" to randShortHashCode() "credit_account" to unknownPayto @@ -118,9 +112,8 @@ class WireGatewayApiTest { }.assertConflict(TalerErrorCode.BANK_UNKNOWN_CREDITOR) // Same account - client.post("/accounts/exchange/taler-wire-gateway/transfer") { - basicAuth("exchange", "exchange-password") - jsonBody(valid_req) { + client.postA("/accounts/exchange/taler-wire-gateway/transfer") { + json(valid_req) { "request_uid" to randHashCode() "wtid" to randShortHashCode() "credit_account" to exchangePayto @@ -128,33 +121,29 @@ class WireGatewayApiTest { }.assertConflict(TalerErrorCode.BANK_ACCOUNT_IS_EXCHANGE) // Bad BASE32 wtid - client.post("/accounts/exchange/taler-wire-gateway/transfer") { - basicAuth("exchange", "exchange-password") - jsonBody(valid_req) { + client.postA("/accounts/exchange/taler-wire-gateway/transfer") { + json(valid_req) { "wtid" to "I love chocolate" } }.assertBadRequest() // Bad BASE32 len wtid - client.post("/accounts/exchange/taler-wire-gateway/transfer") { - basicAuth("exchange", "exchange-password") - jsonBody(valid_req) { + client.postA("/accounts/exchange/taler-wire-gateway/transfer") { + json(valid_req) { "wtid" to randBase32Crockford(31) } }.assertBadRequest() // Bad BASE32 request_uid - client.post("/accounts/exchange/taler-wire-gateway/transfer") { - basicAuth("exchange", "exchange-password") - jsonBody(valid_req) { + client.postA("/accounts/exchange/taler-wire-gateway/transfer") { + json(valid_req) { "request_uid" to "I love chocolate" } }.assertBadRequest() // Bad BASE32 len wtid - client.post("/accounts/exchange/taler-wire-gateway/transfer") { - basicAuth("exchange", "exchange-password") - jsonBody(valid_req) { + client.postA("/accounts/exchange/taler-wire-gateway/transfer") { + json(valid_req) { "request_uid" to randBase32Crockford(65) } }.assertBadRequest() @@ -177,9 +166,8 @@ class WireGatewayApiTest { authRoutine("/accounts/merchant/taler-wire-gateway/history/incoming?delta=7", method = HttpMethod.Get) // Check error when no transactions - client.get("/accounts/exchange/taler-wire-gateway/history/incoming?delta=7") { - basicAuth("exchange", "exchange-password") - }.assertNoContent() + client.getA("/accounts/exchange/taler-wire-gateway/history/incoming?delta=7") + .assertNoContent() // Gen three transactions using clean add incoming logic repeat(3) { @@ -192,60 +180,52 @@ class WireGatewayApiTest { // Gen one transaction using raw bank transaction logic tx("merchant", "KUDOS:10", "exchange", IncomingTxMetadata(randShortHashCode()).encode()) // Gen one transaction using withdraw logic - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:9" } - }.assertOk().run { - val resp = json<BankAccountCreateWithdrawalResponse>() - val uuid = resp.taler_withdraw_uri.split("/").last() + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:9" } + }.assertOkJson<BankAccountCreateWithdrawalResponse> { + val uuid = it.taler_withdraw_uri.split("/").last() client.post("/taler-integration/withdrawal-operation/${uuid}") { - jsonBody { + json { "reserve_pub" to randEddsaPublicKey() "selected_exchange" to exchangePayto } }.assertOk() client.post("/withdrawals/${uuid}/confirm") { - basicAuth("merchant", "merchant-password") + pwAuth("merchant") }.assertNoContent() } // Check ignore bogus subject - client.get("/accounts/exchange/taler-wire-gateway/history/incoming?delta=7") { - basicAuth("exchange", "exchange-password") - }.assertHistory(5) + client.getA("/accounts/exchange/taler-wire-gateway/history/incoming?delta=7") + .assertHistory(5) // Check skip bogus subject - client.get("/accounts/exchange/taler-wire-gateway/history/incoming?delta=5") { - basicAuth("exchange", "exchange-password") - }.assertHistory(5) + client.getA("/accounts/exchange/taler-wire-gateway/history/incoming?delta=5") + .assertHistory(5) // Check no useless polling assertTime(0, 200) { - client.get("/accounts/exchange/taler-wire-gateway/history/incoming?delta=-6&start=15&long_poll_ms=1000") { - basicAuth("exchange", "exchange-password") - }.assertHistory(5) + client.getA("/accounts/exchange/taler-wire-gateway/history/incoming?delta=-6&start=15&long_poll_ms=1000") + .assertHistory(5) } // Check no polling when find transaction assertTime(0, 200) { - client.get("/accounts/exchange/taler-wire-gateway/history/incoming?delta=6&long_poll_ms=60") { - basicAuth("exchange", "exchange-password") - }.assertHistory(5) + client.getA("/accounts/exchange/taler-wire-gateway/history/incoming?delta=6&long_poll_ms=60") + .assertHistory(5) } coroutineScope { launch { // Check polling succeed assertTime(200, 300) { - client.get("/accounts/exchange/taler-wire-gateway/history/incoming?delta=2&start=14&long_poll_ms=1000") { - basicAuth("exchange", "exchange-password") - }.assertHistory(1) + client.getA("/accounts/exchange/taler-wire-gateway/history/incoming?delta=2&start=14&long_poll_ms=1000") + .assertHistory(1) } } launch { // Check polling timeout assertTime(200, 400) { - client.get("/accounts/exchange/taler-wire-gateway/history/incoming?delta=1&start=16&long_poll_ms=300") { - basicAuth("exchange", "exchange-password") - }.assertNoContent() + client.getA("/accounts/exchange/taler-wire-gateway/history/incoming?delta=1&start=16&long_poll_ms=300") + .assertNoContent() } } delay(200) @@ -256,9 +236,8 @@ class WireGatewayApiTest { coroutineScope { launch { assertTime(200, 300) { - client.get("/accounts/exchange/taler-wire-gateway/history/incoming?delta=7&start=16&long_poll_ms=1000") { - basicAuth("exchange", "exchange-password") - }.assertHistory(1) + client.getA("/accounts/exchange/taler-wire-gateway/history/incoming?delta=7&start=16&long_poll_ms=1000") + .assertHistory(1) } } delay(200) @@ -269,27 +248,23 @@ class WireGatewayApiTest { coroutineScope { launch { assertTime(200, 300) { - client.get("/accounts/exchange/taler-wire-gateway/history/incoming?delta=7&start=18&long_poll_ms=1000") { - basicAuth("exchange", "exchange-password") - }.assertHistory(1) + client.getA("/accounts/exchange/taler-wire-gateway/history/incoming?delta=7&start=18&long_poll_ms=1000") + .assertHistory(1) } } delay(200) - client.post("/accounts/merchant/withdrawals") { - basicAuth("merchant", "merchant-password") - jsonBody { "amount" to "KUDOS:9" } - }.assertOk().run { - val resp = json<BankAccountCreateWithdrawalResponse>() - val uuid = resp.taler_withdraw_uri.split("/").last() + client.postA("/accounts/merchant/withdrawals") { + json { "amount" to "KUDOS:9" } + }.assertOkJson<BankAccountCreateWithdrawalResponse> { + val uuid = it.taler_withdraw_uri.split("/").last() client.post("/taler-integration/withdrawal-operation/${uuid}") { - jsonBody { + json { "reserve_pub" to randEddsaPublicKey() "selected_exchange" to exchangePayto } }.assertOk() - client.post("/withdrawals/${uuid}/confirm") { - basicAuth("merchant", "merchant-password") - }.assertNoContent() + client.postA("/withdrawals/${uuid}/confirm") + .assertNoContent() } } @@ -299,14 +274,12 @@ class WireGatewayApiTest { } // forward range: - client.get("/accounts/exchange/taler-wire-gateway/history/incoming?delta=10&start=20") { - basicAuth("exchange", "exchange-password") - }.assertHistory(10) + client.getA("/accounts/exchange/taler-wire-gateway/history/incoming?delta=10&start=20") + .assertHistory(10) // backward range: - client.get("/accounts/exchange/taler-wire-gateway/history/incoming?delta=-10&start=25") { - basicAuth("exchange", "exchange-password") - }.assertHistory(10) + client.getA("/accounts/exchange/taler-wire-gateway/history/incoming?delta=-10&start=25") + .assertHistory(10) } @@ -326,9 +299,8 @@ class WireGatewayApiTest { authRoutine("/accounts/merchant/taler-wire-gateway/history/outgoing?delta=7", method = HttpMethod.Get) // Check error when no transactions - client.get("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=7") { - basicAuth("exchange", "exchange-password") - }.assertNoContent() + client.getA("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=7") + .assertNoContent() // Gen three transactions using clean transfer logic repeat(3) { @@ -344,42 +316,36 @@ class WireGatewayApiTest { } // Check ignore bogus subject - client.get("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=7") { - basicAuth("exchange", "exchange-password") - }.assertHistory(5) + client.getA("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=7") + .assertHistory(5) // Check skip bogus subject - client.get("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=5") { - basicAuth("exchange", "exchange-password") - }.assertHistory(5) + client.getA("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=5") + .assertHistory(5) // Check no useless polling assertTime(0, 200) { - client.get("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=-6&start=15&long_poll_ms=1000") { - basicAuth("exchange", "exchange-password") - }.assertHistory(5) + client.getA("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=-6&start=15&long_poll_ms=1000") + .assertHistory(5) } // Check no polling when find transaction assertTime(0, 200) { - client.get("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=6&long_poll_ms=1000") { - basicAuth("exchange", "exchange-password") - }.assertHistory(5) + client.getA("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=6&long_poll_ms=1000") + .assertHistory(5) } coroutineScope { launch { // Check polling succeed forward assertTime(200, 300) { - client.get("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=2&start=14&long_poll_ms=1000") { - basicAuth("exchange", "exchange-password") - }.assertHistory(1) + client.getA("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=2&start=14&long_poll_ms=1000") + .assertHistory(1) } } launch { // Check polling timeout forward assertTime(200, 400) { - client.get("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=1&start=16&long_poll_ms=300") { - basicAuth("exchange", "exchange-password") - }.assertNoContent() + client.getA("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=1&start=16&long_poll_ms=300") + .assertNoContent() } } delay(200) @@ -392,20 +358,18 @@ class WireGatewayApiTest { } // forward range: - client.get("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=10&start=20") { - basicAuth("exchange", "exchange-password") - }.assertHistory(10) + client.getA("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=10&start=20") + .assertHistory(10) // backward range: - client.get("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=-10&start=25") { - basicAuth("exchange", "exchange-password") - }.assertHistory(10) + client.getA("/accounts/exchange/taler-wire-gateway/history/outgoing?delta=-10&start=25") + .assertHistory(10) } // Testing the /admin/add-incoming call from the TWG API. @Test fun addIncoming() = bankSetup { _ -> - val valid_req = json { + val valid_req = obj { "amount" to "KUDOS:44" "reserve_pub" to randEddsaPublicKey() "debit_account" to merchantPayto @@ -414,60 +378,52 @@ class WireGatewayApiTest { authRoutine("/accounts/merchant/taler-wire-gateway/admin/add-incoming", valid_req, requireAdmin = true) // Checking exchange debt constraint. - client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("admin", "admin-password") - jsonBody(valid_req) + client.postA("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { + json(valid_req) }.assertConflict(TalerErrorCode.BANK_UNALLOWED_DEBIT) // Giving debt allowance and checking the OK case. setMaxDebt("merchant", TalerAmount("KUDOS:1000")) - client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("admin", "admin-password") - jsonBody(valid_req, deflate = true) + client.postA("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { + json(valid_req, deflate = true) }.assertOk() // Trigger conflict due to reused reserve_pub - client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("admin", "admin-password") - jsonBody(valid_req) + client.postA("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { + json(valid_req) }.assertConflict(TalerErrorCode.BANK_DUPLICATE_RESERVE_PUB_SUBJECT) // Currency mismatch - client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("admin", "admin-password") - jsonBody(valid_req) { "amount" to "EUR:33" } + client.postA("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { + json(valid_req) { "amount" to "EUR:33" } }.assertBadRequest(TalerErrorCode.GENERIC_CURRENCY_MISMATCH) // Unknown account - client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("admin", "admin-password") - jsonBody(valid_req) { + client.postA("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { + json(valid_req) { "reserve_pub" to randEddsaPublicKey() "debit_account" to unknownPayto } }.assertConflict(TalerErrorCode.BANK_UNKNOWN_DEBTOR) // Same account - client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("admin", "admin-password") - jsonBody(valid_req) { + client.postA("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { + json(valid_req) { "reserve_pub" to randEddsaPublicKey() "debit_account" to exchangePayto } }.assertConflict(TalerErrorCode.BANK_ACCOUNT_IS_EXCHANGE) // Bad BASE32 reserve_pub - client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("admin", "admin-password") - jsonBody(valid_req) { + client.postA("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { + json(valid_req) { "reserve_pub" to "I love chocolate" } }.assertBadRequest() // Bad BASE32 len reserve_pub - client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("admin", "admin-password") - jsonBody(valid_req) { + client.postA("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { + json(valid_req) { "reserve_pub" to randBase32Crockford(31) } }.assertBadRequest() diff --git a/bank/src/test/kotlin/helpers.kt b/bank/src/test/kotlin/helpers.kt @@ -1,6 +1,7 @@ import io.ktor.http.* import io.ktor.client.statement.* import io.ktor.client.request.* +import io.ktor.client.HttpClient import io.ktor.server.testing.* import kotlinx.coroutines.* import kotlinx.serialization.json.* @@ -98,16 +99,16 @@ fun dbSetup(lambda: suspend (Database) -> Unit) { suspend fun ApplicationTestBuilder.setMaxDebt(account: String, maxDebt: TalerAmount) { client.patch("/accounts/$account") { - basicAuth("admin", "admin-password") - jsonBody { "debit_threshold" to maxDebt } + pwAuth("admin") + json { "debit_threshold" to maxDebt } }.assertNoContent() } suspend fun ApplicationTestBuilder.assertBalance(account: String, info: CreditDebitInfo, amount: String) { client.get("/accounts/$account") { - basicAuth("admin", "admin-password") - }.assertOk().run { - val balance = json<AccountData>().balance; + pwAuth("admin") + }.assertOkJson<AccountData> { + val balance = it.balance; assertEquals(info, balance.credit_debit_indicator) assertEquals(TalerAmount(amount), balance.amount) } @@ -116,18 +117,16 @@ suspend fun ApplicationTestBuilder.assertBalance(account: String, info: CreditDe suspend fun ApplicationTestBuilder.tx(from: String, amount: String, to: String, subject: String = "payout"): Long { return client.post("/accounts/$from/transactions") { basicAuth("$from", "$from-password") - jsonBody { + json { "payto_uri" to "${paytos[to]}?message=${subject.encodeURLQueryComponent()}&amount=$amount" } - }.assertOk().run { - json<TransactionCreateResponse>().row_id - } + }.assertOkJson<TransactionCreateResponse>().row_id } suspend fun ApplicationTestBuilder.transfer(amount: String) { client.post("/accounts/exchange/taler-wire-gateway/transfer") { - basicAuth("exchange", "exchange-password") - jsonBody { + pwAuth("exchange") + json { "request_uid" to randHashCode() "amount" to TalerAmount(amount) "exchange_base_url" to "http://exchange.example.com/" @@ -139,8 +138,8 @@ suspend fun ApplicationTestBuilder.transfer(amount: String) { suspend fun ApplicationTestBuilder.addIncoming(amount: String) { client.post("/accounts/exchange/taler-wire-gateway/admin/add-incoming") { - basicAuth("admin", "admin-password") - jsonBody { + pwAuth("admin") + json { "amount" to TalerAmount(amount) "reserve_pub" to randEddsaPublicKey() "debit_account" to merchantPayto @@ -149,38 +148,33 @@ suspend fun ApplicationTestBuilder.addIncoming(amount: String) { } suspend fun ApplicationTestBuilder.cashout(amount: String) { - client.post("/accounts/customer/cashouts") { - basicAuth("customer", "customer-password") - jsonBody(json { + client.postA("/accounts/customer/cashouts") { + json { "request_uid" to randShortHashCode() "amount_debit" to amount "amount_credit" to convert(amount) - }) - }.assertOk().run { - val id = json<CashoutPending>().cashout_id - client.post("/accounts/customer/cashouts/$id/confirm") { - basicAuth("customer", "customer-password") - jsonBody { "tan" to smsCode("+99") } + } + }.assertOkJson<CashoutPending> { + client.postA("/accounts/customer/cashouts/${it.cashout_id}/confirm") { + json { "tan" to smsCode("+99") } }.assertNoContent() } } suspend fun ApplicationTestBuilder.fillCashoutInfo(account: String) { - client.patch("/accounts/$account") { - basicAuth("$account", "$account-password") - jsonBody(json { + client.patchA("/accounts/$account") { + json { "cashout_payto_uri" to unknownPayto - "challenge_contact_data" to json { + "challenge_contact_data" to obj { "phone" to "+99" } - }) + } }.assertNoContent() } suspend fun ApplicationTestBuilder.convert(amount: String): TalerAmount { - client.get("/cashout-rate?amount_debit=$amount").assertOk().run { - return json<ConversionResponse>().amount_credit - } + return client.get("/cashout-rate?amount_debit=$amount") + .assertOkJson<ConversionResponse>().amount_credit } suspend fun smsCode(info: String): String? { @@ -277,7 +271,7 @@ inline suspend fun <reified B> HttpResponse.assertHistoryIds(size: Int, ids: (B) /* ----- Body helper ----- */ -inline fun <reified B> HttpRequestBuilder.jsonBody(b: B, deflate: Boolean = false) { +inline fun <reified B> HttpRequestBuilder.json(b: B, deflate: Boolean = false) { val json = Json.encodeToString(kotlinx.serialization.serializer<B>(), b); contentType(ContentType.Application.Json) if (deflate) { @@ -292,20 +286,66 @@ inline fun <reified B> HttpRequestBuilder.jsonBody(b: B, deflate: Boolean = fals } } -inline fun HttpRequestBuilder.jsonBody( +inline fun HttpRequestBuilder.json( from: JsonObject = JsonObject(emptyMap()), deflate: Boolean = false, builderAction: JsonBuilder.() -> Unit ) { - jsonBody(json(from, builderAction), deflate) + json(obj(from, builderAction), deflate) } inline suspend fun <reified B> HttpResponse.json(): B = Json.decodeFromString(kotlinx.serialization.serializer<B>(), bodyAsText()) +inline suspend fun <reified B> HttpResponse.assertOkJson(lambda: (B) -> Unit = {}): B { + assertOk() + val body = json<B>() + lambda(body) + return body +} + +/* ----- Auth ----- */ + +/** Auto auth get request */ +inline suspend fun HttpClient.getA(url: String, builder: HttpRequestBuilder.() -> Unit = {}): HttpResponse { + return get(url) { + pwAuth() + builder(this) + } +} + +/** Auto auth post request */ +inline suspend fun HttpClient.postA(url: String, builder: HttpRequestBuilder.() -> Unit = {}): HttpResponse { + return post(url) { + pwAuth() + builder(this) + } +} + +/** Auto auth patch request */ +inline suspend fun HttpClient.patchA(url: String, builder: HttpRequestBuilder.() -> Unit = {}): HttpResponse { + return patch(url) { + pwAuth() + builder(this) + } +} + +fun HttpRequestBuilder.pwAuth(username: String? = null) { + if (username != null) { + basicAuth("$username", "$username-password") + } else if (url.pathSegments.contains("admin")) { + basicAuth("admin", "admin-password") + } else if (url.pathSegments[1] == "accounts") { + // Extract username from path + val login = url.pathSegments[2] + basicAuth("$login", "$login-password") + } + +} + /* ----- Json DSL ----- */ -inline fun json(from: JsonObject = JsonObject(emptyMap()), builderAction: JsonBuilder.() -> Unit): JsonObject { +inline fun obj(from: JsonObject = JsonObject(emptyMap()), builderAction: JsonBuilder.() -> Unit): JsonObject { val builder = JsonBuilder(from) builder.apply(builderAction) return JsonObject(builder.content)