taler-rust

GNU Taler code in Rust. Largely core banking integrations.
Log | Files | Refs | Submodules | README | LICENSE

api.rs (5122B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2024, 2025, 2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 
     17 use std::sync::LazyLock;
     18 
     19 use axum::http::StatusCode;
     20 use common::setup;
     21 use sqlx::{PgPool, Row, postgres::PgRow};
     22 use taler_api::db::TypeHelper as _;
     23 use taler_common::{
     24     api_common::{HashCode, ShortHashCode},
     25     api_revenue::RevenueConfig,
     26     api_transfer::PreparedTransferConfig,
     27     api_wire::{OutgoingHistory, TransferResponse, TransferState, WireConfig},
     28     db::IncomingType,
     29     error_code::ErrorCode,
     30     types::{
     31         amount::amount,
     32         payto::{PaytoURI, payto},
     33         url,
     34     },
     35 };
     36 use taler_test_utils::{
     37     json,
     38     routine::{
     39         Status, admin_add_incoming_routine, in_history_routine, registration_routine,
     40         revenue_routine, routine_pagination, transfer_routine,
     41     },
     42     server::TestServer as _,
     43 };
     44 
     45 mod common;
     46 
     47 static PAYTO: LazyLock<PaytoURI> = LazyLock::new(|| payto("payto://test?receiver-name=Test"));
     48 
     49 #[tokio::test]
     50 async fn errors() {
     51     let (server, _) = setup().await;
     52     server
     53         .get("/unknown")
     54         .await
     55         .assert_error(ErrorCode::GENERIC_ENDPOINT_UNKNOWN);
     56     server
     57         .post("/taler-revenue/config")
     58         .await
     59         .assert_error(ErrorCode::GENERIC_METHOD_INVALID);
     60 }
     61 
     62 #[tokio::test]
     63 async fn config() {
     64     let (server, _) = setup().await;
     65     server
     66         .get("/taler-wire-gateway/config")
     67         .await
     68         .assert_ok_json::<WireConfig>();
     69     server
     70         .get("/taler-prepared-transfer/config")
     71         .await
     72         .assert_ok_json::<PreparedTransferConfig>();
     73     server
     74         .get("/taler-revenue/config")
     75         .await
     76         .assert_ok_json::<RevenueConfig>();
     77 }
     78 
     79 #[tokio::test]
     80 async fn transfer() {
     81     let (server, _) = setup().await;
     82     transfer_routine(&server, TransferState::success, &PAYTO).await;
     83 }
     84 
     85 #[tokio::test]
     86 async fn outgoing_history() {
     87     let (server, _) = setup().await;
     88     routine_pagination::<OutgoingHistory>(
     89         &server,
     90         "/taler-wire-gateway/history/outgoing",
     91         async |i| {
     92             server
     93                 .post("/taler-wire-gateway/transfer")
     94                 .json(&json!({
     95                     "request_uid": HashCode::rand(),
     96                     "amount": amount(format!("EUR:0.0{i}")),
     97                     "exchange_base_url": url("http://exchange.taler"),
     98                     "wtid": ShortHashCode::rand(),
     99                     "credit_account": PAYTO.clone(),
    100                 }))
    101                 .await
    102                 .assert_ok_json::<TransferResponse>();
    103         },
    104     )
    105     .await;
    106 }
    107 
    108 #[tokio::test]
    109 async fn admin_add_incoming() {
    110     let (server, _) = setup().await;
    111     admin_add_incoming_routine(&server, &PAYTO, true).await;
    112 }
    113 
    114 #[tokio::test]
    115 async fn in_history() {
    116     let (server, _) = setup().await;
    117     in_history_routine(&server, &PAYTO, true).await;
    118 }
    119 
    120 #[tokio::test]
    121 async fn revenue() {
    122     let (server, _) = setup().await;
    123     revenue_routine(&server, &PAYTO, true).await;
    124 }
    125 
    126 #[tokio::test]
    127 async fn account_check() {
    128     let (server, _) = setup().await;
    129     server
    130         .get("/taler-wire-gateway/account/check")
    131         .query("account", "payto://test")
    132         .await
    133         .assert_status(StatusCode::NOT_IMPLEMENTED);
    134 }
    135 
    136 async fn check_in(pool: &PgPool) -> Vec<Status> {
    137     sqlx::query(
    138             "
    139             SELECT pending_recurrent_in.authorization_pub IS NOT NULL, bounced.tx_in_id IS NOT NULL, type, taler_in.account_pub 
    140             FROM tx_in
    141                 LEFT JOIN taler_in USING (tx_in_id)
    142                 LEFT JOIN pending_recurrent_in USING (tx_in_id)
    143                 LEFT JOIN bounced USING (tx_in_id)
    144             ORDER BY tx_in.tx_in_id
    145         ",
    146         )
    147         .try_map(|r: PgRow| {
    148             Ok(
    149                 if r.try_get_flag(0)? {
    150                     Status::Pending
    151                 } else if r.try_get_flag(1)? {
    152                     Status::Bounced
    153                 } else {
    154                     match r.try_get(2)? {
    155                         None => Status::Simple,
    156                         Some(IncomingType::reserve) => Status::Reserve(r.try_get(3)?),
    157                         Some(IncomingType::kyc) => Status::Kyc(r.try_get(3)?),
    158                         Some(e) => unreachable!("{e:?}")
    159                     }
    160                 }
    161             )
    162         })
    163         .fetch_all(pool)
    164         .await
    165         .unwrap()
    166 }
    167 
    168 #[tokio::test]
    169 async fn registration() {
    170     let (server, pool) = setup().await;
    171     registration_routine(&server, &PAYTO, || check_in(&pool)).await;
    172 }