commit 3322693545f8648b7e8dd9b881d0827b737680a5
parent dae0531936b449deef1242dfcbcea994de9e3fe2
Author: Antoine A <>
Date: Thu, 26 Mar 2026 16:19:22 +0100
common: clean tests
Diffstat:
14 files changed, 468 insertions(+), 463 deletions(-)
diff --git a/common/failure-injection/Cargo.toml b/common/failure-injection/Cargo.toml
@@ -7,5 +7,9 @@ homepage.workspace = true
repository.workspace = true
license-file.workspace = true
+[lib]
+test = false
+doctest = false
+
[dependencies]
-thiserror.workspace = true
-\ No newline at end of file
+thiserror.workspace = true
diff --git a/common/http-client/Cargo.toml b/common/http-client/Cargo.toml
@@ -7,6 +7,9 @@ homepage.workspace = true
repository.workspace = true
license-file.workspace = true
+[lib]
+doctest = false
+
[dependencies]
serde_json = { workspace = true, features = ["raw_value"] }
serde.workspace = true
diff --git a/common/taler-api/Cargo.toml b/common/taler-api/Cargo.toml
@@ -7,6 +7,9 @@ homepage.workspace = true
repository.workspace = true
license-file.workspace = true
+[lib]
+doctest = false
+
[dependencies]
listenfd = "1.0.0"
dashmap = "6.1"
diff --git a/common/taler-build/Cargo.toml b/common/taler-build/Cargo.toml
@@ -5,4 +5,8 @@ edition.workspace = true
authors.workspace = true
homepage.workspace = true
repository.workspace = true
-license-file.workspace = true
-\ No newline at end of file
+license-file.workspace = true
+
+[lib]
+test = false
+doctest = false
+\ No newline at end of file
diff --git a/common/taler-common/Cargo.toml b/common/taler-common/Cargo.toml
@@ -7,6 +7,9 @@ homepage.workspace = true
repository.workspace = true
license-file.workspace = true
+[lib]
+doctest = false
+
[dependencies]
glob = "0.3"
indexmap = "2.7"
diff --git a/common/taler-test-utils/Cargo.toml b/common/taler-test-utils/Cargo.toml
@@ -7,10 +7,13 @@ homepage.workspace = true
repository.workspace = true
license-file.workspace = true
+[lib]
+test = false
+doctest = false
+
[dependencies]
tower = "0.5"
flate2 = { version = "1.0", features = ["zlib-rs"], default-features = false }
-uuid = { version = "1", features = ["v4"] }
axum.workspace = true
tokio.workspace = true
serde_json.workspace = true
diff --git a/taler-cyclos/Cargo.toml b/taler-cyclos/Cargo.toml
@@ -8,6 +8,9 @@ homepage.workspace = true
repository.workspace = true
license-file.workspace = true
+[lib]
+doctest = false
+
[dependencies]
sqlx.workspace = true
serde_json = { workspace = true, features = ["raw_value"] }
diff --git a/taler-cyclos/src/api.rs b/taler-cyclos/src/api.rs
@@ -294,3 +294,227 @@ impl WireTransferGateway for CyclosApi {
}
}
}
+
+#[cfg(test)]
+mod test {
+ use std::{
+ str::FromStr as _,
+ sync::{Arc, LazyLock},
+ };
+
+ use compact_str::CompactString;
+ use jiff::Timestamp;
+ use sqlx::{PgPool, Row as _, postgres::PgRow};
+ use taler_api::{
+ api::TalerRouter as _,
+ auth::AuthMethod,
+ db::TypeHelper as _,
+ subject::{IncomingSubject, OutgoingSubject},
+ };
+ use taler_common::{
+ api_common::EddsaPublicKey,
+ api_revenue::RevenueConfig,
+ api_transfer::WireTransferConfig,
+ api_wire::{OutgoingHistory, TransferState, WireConfig},
+ db::IncomingType,
+ types::{
+ amount::{Currency, decimal},
+ payto::{PaytoURI, payto},
+ },
+ };
+ use taler_test_utils::{
+ Router,
+ db::db_test_setup,
+ routine::{
+ Status, admin_add_incoming_routine, registration_routine, revenue_routine,
+ routine_pagination, transfer_routine,
+ },
+ server::TestServer as _,
+ };
+
+ use crate::{
+ api::CyclosApi,
+ constants::CONFIG_SOURCE,
+ db::{self, AddIncomingResult, TxIn, TxOutKind},
+ };
+
+ static ACCOUNT: LazyLock<PaytoURI> =
+ LazyLock::new(|| payto("payto://cyclos/localhost/7762070814178012479?receiver-name=name"));
+
+ async fn setup() -> (Router, PgPool) {
+ let (_, pool) = db_test_setup(CONFIG_SOURCE).await;
+ let api = Arc::new(
+ CyclosApi::start(
+ pool.clone(),
+ CompactString::const_new("localhost"),
+ ACCOUNT.clone(),
+ Currency::from_str("TEST").unwrap(),
+ )
+ .await,
+ );
+ let server = Router::new()
+ .wire_gateway(api.clone(), AuthMethod::None)
+ .wire_transfer_gateway(api.clone())
+ .revenue(api, AuthMethod::None)
+ .finalize();
+
+ (server, pool)
+ }
+
+ #[tokio::test]
+ async fn config() {
+ let (server, _) = setup().await;
+ server
+ .get("/taler-wire-gateway/config")
+ .await
+ .assert_ok_json::<WireConfig>();
+ server
+ .get("/taler-wire-transfer-gateway/config")
+ .await
+ .assert_ok_json::<WireTransferConfig>();
+ server
+ .get("/taler-revenue/config")
+ .await
+ .assert_ok_json::<RevenueConfig>();
+ }
+
+ #[tokio::test]
+ async fn transfer() {
+ let (server, _) = setup().await;
+ transfer_routine(&server, TransferState::pending, &ACCOUNT).await;
+ }
+
+ #[tokio::test]
+ async fn outgoing_history() {
+ let (server, pool) = setup().await;
+ routine_pagination::<OutgoingHistory, _>(
+ &server,
+ "/taler-wire-gateway/history/outgoing",
+ |it| {
+ it.outgoing_transactions
+ .into_iter()
+ .map(|it| *it.row_id as i64)
+ .collect()
+ },
+ async |_, i| {
+ db::register_tx_out(
+ &mut pool.acquire().await.unwrap(),
+ &db::TxOut {
+ transfer_id: i as i64,
+ tx_id: if i % 2 == 0 {
+ Some((i % 2) as i64)
+ } else {
+ None
+ },
+ amount: decimal("10"),
+ subject: "subject".to_owned(),
+ creditor_id: 31000163100000000,
+ creditor_name: "Name".into(),
+ valued_at: Timestamp::now(),
+ },
+ &TxOutKind::Talerable(OutgoingSubject::rand()),
+ &Timestamp::now(),
+ )
+ .await
+ .unwrap();
+ },
+ )
+ .await;
+ }
+
+ #[tokio::test]
+ async fn admin_add_incoming() {
+ let (server, _) = setup().await;
+ admin_add_incoming_routine(&server, &ACCOUNT, true).await;
+ }
+
+ #[tokio::test]
+ async fn revenue() {
+ let (server, _) = setup().await;
+ revenue_routine(&server, &ACCOUNT, true).await;
+ }
+
+ async fn check_in(pool: &PgPool) -> Vec<Status> {
+ sqlx::query(
+ "
+ SELECT pending_recurrent_in.authorization_pub IS NOT NULL, bounced.tx_in_id IS NOT NULL, type, metadata
+ FROM tx_in
+ LEFT JOIN taler_in USING (tx_in_id)
+ LEFT JOIN pending_recurrent_in USING (tx_in_id)
+ LEFT JOIN bounced USING (tx_in_id)
+ ORDER BY tx_in.tx_in_id
+ ",
+ )
+ .try_map(|r: PgRow| {
+ Ok(
+ if r.try_get_flag(0)? {
+ Status::Pending
+ } else if r.try_get_flag(1)? {
+ Status::Bounced
+ } else {
+ match r.try_get(2)? {
+ None => Status::Simple,
+ Some(IncomingType::reserve) => Status::Reserve(r.try_get(3)?),
+ Some(IncomingType::kyc) => Status::Kyc(r.try_get(3)?),
+ Some(e) => unreachable!("{e:?}")
+ }
+ }
+ )
+ })
+ .fetch_all(pool)
+ .await
+ .unwrap()
+ }
+
+ pub async fn test_in(pool: &PgPool, key: EddsaPublicKey) {
+ let tx = TxIn {
+ transfer_id: rand::random_range(10..10000),
+ tx_id: None,
+ amount: decimal("12"),
+ subject: String::new(),
+ debtor_id: rand::random_range(10..10000),
+ debtor_name: "Name".into(),
+ valued_at: Timestamp::now(),
+ };
+ let mut db = pool.acquire().await.unwrap();
+ let reason = match db::register_tx_in(
+ &mut db,
+ &tx,
+ &Some(IncomingSubject::Map(key)),
+ &Timestamp::now(),
+ )
+ .await
+ .unwrap()
+ {
+ AddIncomingResult::Success { .. } => return,
+ AddIncomingResult::ReservePubReuse => "reserve pub reuse",
+ AddIncomingResult::UnknownMapping => "unknown mapping",
+ AddIncomingResult::MappingReuse => "mapping reuse",
+ };
+ db::register_bounced_tx_in(
+ &mut db,
+ &tx,
+ rand::random_range(10..10000),
+ reason,
+ &Timestamp::now(),
+ )
+ .await
+ .unwrap();
+ }
+
+ #[tokio::test]
+ async fn registration() {
+ let (server, pool) = setup().await;
+ registration_routine(
+ &server,
+ &ACCOUNT,
+ || check_in(&pool),
+ |account_pub| {
+ let account_pub = account_pub.clone();
+ let pool = &pool;
+ async move { test_in(pool, account_pub).await }
+ },
+ )
+ .await;
+ }
+}
diff --git a/taler-cyclos/tests/api.rs b/taler-cyclos/tests/api.rs
@@ -1,235 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2025, 2026 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-
-use std::{
- str::FromStr,
- sync::{Arc, LazyLock},
-};
-
-use compact_str::CompactString;
-use jiff::Timestamp;
-use sqlx::{PgPool, Row as _, postgres::PgRow};
-use taler_api::{
- api::TalerRouter,
- auth::AuthMethod,
- db::TypeHelper as _,
- subject::{IncomingSubject, OutgoingSubject},
-};
-use taler_common::{
- api_common::EddsaPublicKey,
- api_revenue::RevenueConfig,
- api_transfer::WireTransferConfig,
- api_wire::{OutgoingHistory, TransferState, WireConfig},
- db::IncomingType,
- types::{
- amount::{Currency, decimal},
- payto::{PaytoURI, payto},
- },
-};
-use taler_cyclos::{
- api::CyclosApi,
- constants::CONFIG_SOURCE,
- db::{self, AddIncomingResult, TxIn, TxOutKind},
-};
-use taler_test_utils::{
- Router,
- db::db_test_setup,
- routine::{
- Status, admin_add_incoming_routine, registration_routine, revenue_routine,
- routine_pagination, transfer_routine,
- },
- server::TestServer,
-};
-
-static ACCOUNT: LazyLock<PaytoURI> =
- LazyLock::new(|| payto("payto://cyclos/localhost/7762070814178012479?receiver-name=name"));
-
-async fn setup() -> (Router, PgPool) {
- let (_, pool) = db_test_setup(CONFIG_SOURCE).await;
- let api = Arc::new(
- CyclosApi::start(
- pool.clone(),
- CompactString::const_new("localhost"),
- ACCOUNT.clone(),
- Currency::from_str("TEST").unwrap(),
- )
- .await,
- );
- let server = Router::new()
- .wire_gateway(api.clone(), AuthMethod::None)
- .wire_transfer_gateway(api.clone())
- .revenue(api, AuthMethod::None)
- .finalize();
-
- (server, pool)
-}
-
-#[tokio::test]
-async fn config() {
- let (server, _) = setup().await;
- server
- .get("/taler-wire-gateway/config")
- .await
- .assert_ok_json::<WireConfig>();
- server
- .get("/taler-wire-transfer-gateway/config")
- .await
- .assert_ok_json::<WireTransferConfig>();
- server
- .get("/taler-revenue/config")
- .await
- .assert_ok_json::<RevenueConfig>();
-}
-
-#[tokio::test]
-async fn transfer() {
- let (server, _) = setup().await;
- transfer_routine(&server, TransferState::pending, &ACCOUNT).await;
-}
-
-#[tokio::test]
-async fn outgoing_history() {
- let (server, pool) = setup().await;
- routine_pagination::<OutgoingHistory, _>(
- &server,
- "/taler-wire-gateway/history/outgoing",
- |it| {
- it.outgoing_transactions
- .into_iter()
- .map(|it| *it.row_id as i64)
- .collect()
- },
- async |_, i| {
- db::register_tx_out(
- &mut pool.acquire().await.unwrap(),
- &db::TxOut {
- transfer_id: i as i64,
- tx_id: if i % 2 == 0 {
- Some((i % 2) as i64)
- } else {
- None
- },
- amount: decimal("10"),
- subject: "subject".to_owned(),
- creditor_id: 31000163100000000,
- creditor_name: "Name".into(),
- valued_at: Timestamp::now(),
- },
- &TxOutKind::Talerable(OutgoingSubject::rand()),
- &Timestamp::now(),
- )
- .await
- .unwrap();
- },
- )
- .await;
-}
-
-#[tokio::test]
-async fn admin_add_incoming() {
- let (server, _) = setup().await;
- admin_add_incoming_routine(&server, &ACCOUNT, true).await;
-}
-
-#[tokio::test]
-async fn revenue() {
- let (server, _) = setup().await;
- revenue_routine(&server, &ACCOUNT, true).await;
-}
-
-async fn check_in(pool: &PgPool) -> Vec<Status> {
- sqlx::query(
- "
- SELECT pending_recurrent_in.authorization_pub IS NOT NULL, bounced.tx_in_id IS NOT NULL, type, metadata
- FROM tx_in
- LEFT JOIN taler_in USING (tx_in_id)
- LEFT JOIN pending_recurrent_in USING (tx_in_id)
- LEFT JOIN bounced USING (tx_in_id)
- ORDER BY tx_in.tx_in_id
- ",
- )
- .try_map(|r: PgRow| {
- Ok(
- if r.try_get_flag(0)? {
- Status::Pending
- } else if r.try_get_flag(1)? {
- Status::Bounced
- } else {
- match r.try_get(2)? {
- None => Status::Simple,
- Some(IncomingType::reserve) => Status::Reserve(r.try_get(3)?),
- Some(IncomingType::kyc) => Status::Kyc(r.try_get(3)?),
- Some(e) => unreachable!("{e:?}")
- }
- }
- )
- })
- .fetch_all(pool)
- .await
- .unwrap()
-}
-
-pub async fn test_in(pool: &PgPool, key: EddsaPublicKey) {
- let tx = TxIn {
- transfer_id: rand::random_range(10..10000),
- tx_id: None,
- amount: decimal("12"),
- subject: String::new(),
- debtor_id: rand::random_range(10..10000),
- debtor_name: "Name".into(),
- valued_at: Timestamp::now(),
- };
- let mut db = pool.acquire().await.unwrap();
- let reason = match db::register_tx_in(
- &mut db,
- &tx,
- &Some(IncomingSubject::Map(key)),
- &Timestamp::now(),
- )
- .await
- .unwrap()
- {
- AddIncomingResult::Success { .. } => return,
- AddIncomingResult::ReservePubReuse => "reserve pub reuse",
- AddIncomingResult::UnknownMapping => "unknown mapping",
- AddIncomingResult::MappingReuse => "mapping reuse",
- };
- db::register_bounced_tx_in(
- &mut db,
- &tx,
- rand::random_range(10..10000),
- reason,
- &Timestamp::now(),
- )
- .await
- .unwrap();
-}
-
-#[tokio::test]
-async fn registration() {
- let (server, pool) = setup().await;
- registration_routine(
- &server,
- &ACCOUNT,
- || check_in(&pool),
- |account_pub| {
- let account_pub = account_pub.clone();
- let pool = &pool;
- async move { test_in(pool, account_pub).await }
- },
- )
- .await;
-}
diff --git a/taler-magnet-bank/Cargo.toml b/taler-magnet-bank/Cargo.toml
@@ -8,6 +8,9 @@ homepage.workspace = true
repository.workspace = true
license-file.workspace = true
+[lib]
+doctest = false
+
[dependencies]
form_urlencoded = "1.2"
percent-encoding = "2.3"
diff --git a/taler-magnet-bank/tests/fixtures/setup.json b/taler-magnet-bank/fixtures/setup.json
diff --git a/taler-magnet-bank/src/api.rs b/taler-magnet-bank/src/api.rs
@@ -269,3 +269,216 @@ impl WireTransferGateway for MagnetApi {
}
}
}
+
+#[cfg(test)]
+mod test {
+
+ use std::sync::{Arc, LazyLock};
+
+ use crate::{
+ FullHuPayto,
+ api::MagnetApi,
+ constants::CONFIG_SOURCE,
+ db::{self, AddIncomingResult, TxIn, TxOutKind},
+ magnet_api::types::TxStatus,
+ magnet_payto,
+ };
+
+ use jiff::{Timestamp, Zoned};
+ use sqlx::{PgPool, Row as _, postgres::PgRow};
+ use taler_api::{
+ api::TalerRouter as _,
+ auth::AuthMethod,
+ db::TypeHelper as _,
+ subject::{IncomingSubject, OutgoingSubject},
+ };
+ use taler_common::{
+ api_common::EddsaPublicKey,
+ api_revenue::RevenueConfig,
+ api_transfer::WireTransferConfig,
+ api_wire::{OutgoingHistory, TransferState, WireConfig},
+ db::IncomingType,
+ types::{
+ amount::amount,
+ payto::{PaytoURI, payto},
+ },
+ };
+ use taler_test_utils::{
+ Router,
+ db::db_test_setup,
+ routine::{
+ Status, admin_add_incoming_routine, registration_routine, revenue_routine,
+ routine_pagination, transfer_routine,
+ },
+ server::TestServer,
+ };
+
+ static PAYTO: LazyLock<FullHuPayto> = LazyLock::new(|| {
+ magnet_payto("payto://iban/HU02162000031000164800000000?receiver-name=name")
+ });
+ static ACCOUNT: LazyLock<PaytoURI> = LazyLock::new(|| PAYTO.as_payto());
+
+ async fn setup() -> (Router, PgPool) {
+ let (_, pool) = db_test_setup(CONFIG_SOURCE).await;
+ let api = Arc::new(MagnetApi::start(pool.clone(), ACCOUNT.clone()).await);
+ let server = Router::new()
+ .wire_gateway(api.clone(), AuthMethod::None)
+ .wire_transfer_gateway(api.clone())
+ .revenue(api, AuthMethod::None)
+ .finalize();
+
+ (server, pool)
+ }
+
+ #[tokio::test]
+ async fn config() {
+ let (server, _) = setup().await;
+ server
+ .get("/taler-wire-gateway/config")
+ .await
+ .assert_ok_json::<WireConfig>();
+ server
+ .get("/taler-wire-transfer-gateway/config")
+ .await
+ .assert_ok_json::<WireTransferConfig>();
+ server
+ .get("/taler-revenue/config")
+ .await
+ .assert_ok_json::<RevenueConfig>();
+ }
+
+ #[tokio::test]
+ async fn transfer() {
+ let (server, _) = setup().await;
+ transfer_routine(
+ &server,
+ TransferState::pending,
+ &payto("payto://iban/HU02162000031000164800000000?receiver-name=name"),
+ )
+ .await;
+ }
+
+ #[tokio::test]
+ async fn outgoing_history() {
+ let (server, pool) = setup().await;
+ routine_pagination::<OutgoingHistory, _>(
+ &server,
+ "/taler-wire-gateway/history/outgoing",
+ |it| {
+ it.outgoing_transactions
+ .into_iter()
+ .map(|it| *it.row_id as i64)
+ .collect()
+ },
+ async |_, i| {
+ let mut conn = pool.acquire().await.unwrap();
+ let now = Zoned::now().date();
+ db::register_tx_out(
+ &mut conn,
+ &db::TxOut {
+ code: i as u64,
+ amount: amount("EUR:10"),
+ subject: "subject".into(),
+ creditor: PAYTO.clone(),
+ value_date: now,
+ status: TxStatus::Completed,
+ },
+ &TxOutKind::Talerable(OutgoingSubject::rand()),
+ &Timestamp::now(),
+ )
+ .await
+ .unwrap();
+ },
+ )
+ .await;
+ }
+
+ #[tokio::test]
+ async fn admin_add_incoming() {
+ let (server, _) = setup().await;
+ admin_add_incoming_routine(&server, &ACCOUNT, true).await;
+ }
+
+ #[tokio::test]
+ async fn revenue() {
+ let (server, _) = setup().await;
+ revenue_routine(&server, &ACCOUNT, true).await;
+ }
+
+ async fn check_in(pool: &PgPool) -> Vec<Status> {
+ sqlx::query(
+ "
+ SELECT pending_recurrent_in.authorization_pub IS NOT NULL, initiated_id IS NOT NULL, type, metadata
+ FROM tx_in
+ LEFT JOIN taler_in USING (tx_in_id)
+ LEFT JOIN pending_recurrent_in USING (tx_in_id)
+ LEFT JOIN bounced USING (tx_in_id)
+ ORDER BY tx_in.tx_in_id
+ ",
+ )
+ .try_map(|r: PgRow| {
+ Ok(
+ if r.try_get_flag(0)? {
+ Status::Pending
+ } else if r.try_get_flag(1)? {
+ Status::Bounced
+ } else {
+ match r.try_get(2)? {
+ None => Status::Simple,
+ Some(IncomingType::reserve) => Status::Reserve(r.try_get(3)?),
+ Some(IncomingType::kyc) => Status::Kyc(r.try_get(3)?),
+ Some(e) => unreachable!("{e:?}")
+ }
+ }
+ )
+ })
+ .fetch_all(pool)
+ .await
+ .unwrap()
+ }
+
+ pub async fn test_in(pool: &PgPool, key: EddsaPublicKey) {
+ let tx = TxIn {
+ code: rand::random_range(10..10000),
+ amount: amount("EUR:12"),
+ subject: Box::default(),
+ debtor: PAYTO.clone(),
+ value_date: Zoned::now().date(),
+ status: TxStatus::Completed,
+ };
+ let mut db = pool.acquire().await.unwrap();
+ let reason = match db::register_tx_in(
+ &mut db,
+ &tx,
+ &Some(IncomingSubject::Map(key)),
+ &Timestamp::now(),
+ )
+ .await
+ .unwrap()
+ {
+ AddIncomingResult::Success { .. } => return,
+ AddIncomingResult::ReservePubReuse => "reserve pub reuse",
+ AddIncomingResult::UnknownMapping => "unknown mapping",
+ AddIncomingResult::MappingReuse => "mapping reuse",
+ };
+ db::register_bounce_tx_in(&mut db, &tx, reason, &Timestamp::now())
+ .await
+ .unwrap();
+ }
+
+ #[tokio::test]
+ async fn registration() {
+ let (server, pool) = setup().await;
+ registration_routine(
+ &server,
+ &ACCOUNT,
+ || check_in(&pool),
+ |account_pub| {
+ let account_pub = account_pub.clone();
+ let pool = &pool;
+ async move { test_in(pool, account_pub).await }
+ },
+ )
+ .await;
+ }
+}
diff --git a/taler-magnet-bank/src/setup.rs b/taler-magnet-bank/src/setup.rs
@@ -220,7 +220,7 @@ mod test {
#[test]
fn keys_files() {
// Load JSON file
- let content: KeysFile = json_file::load("tests/fixtures/setup.json").unwrap();
+ let content: KeysFile = json_file::load("./fixtures/setup.json").unwrap();
// Check full
assert!(content.access_token.is_some());
let key = content.signing_key.clone().unwrap();
diff --git a/taler-magnet-bank/tests/api.rs b/taler-magnet-bank/tests/api.rs
@@ -1,222 +0,0 @@
-/*
- This file is part of TALER
- Copyright (C) 2025, 2026 Taler Systems SA
-
- TALER is free software; you can redistribute it and/or modify it under the
- terms of the GNU Affero General Public License as published by the Free Software
- Foundation; either version 3, or (at your option) any later version.
-
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-
-use std::sync::{Arc, LazyLock};
-
-use jiff::{Timestamp, Zoned};
-use sqlx::{PgPool, Row as _, postgres::PgRow};
-use taler_api::{
- api::TalerRouter as _,
- auth::AuthMethod,
- db::TypeHelper as _,
- subject::{IncomingSubject, OutgoingSubject},
-};
-use taler_common::{
- api_common::EddsaPublicKey,
- api_revenue::RevenueConfig,
- api_transfer::WireTransferConfig,
- api_wire::{OutgoingHistory, TransferState, WireConfig},
- db::IncomingType,
- types::{
- amount::amount,
- payto::{PaytoURI, payto},
- },
-};
-use taler_magnet_bank::{
- FullHuPayto,
- api::MagnetApi,
- constants::CONFIG_SOURCE,
- db::{self, AddIncomingResult, TxIn, TxOutKind},
- magnet_api::types::TxStatus,
- magnet_payto,
-};
-use taler_test_utils::{
- Router,
- db::db_test_setup,
- routine::{
- Status, admin_add_incoming_routine, registration_routine, revenue_routine,
- routine_pagination, transfer_routine,
- },
- server::TestServer,
-};
-
-static PAYTO: LazyLock<FullHuPayto> =
- LazyLock::new(|| magnet_payto("payto://iban/HU02162000031000164800000000?receiver-name=name"));
-static ACCOUNT: LazyLock<PaytoURI> = LazyLock::new(|| PAYTO.as_payto());
-
-async fn setup() -> (Router, PgPool) {
- let (_, pool) = db_test_setup(CONFIG_SOURCE).await;
- let api = Arc::new(MagnetApi::start(pool.clone(), ACCOUNT.clone()).await);
- let server = Router::new()
- .wire_gateway(api.clone(), AuthMethod::None)
- .wire_transfer_gateway(api.clone())
- .revenue(api, AuthMethod::None)
- .finalize();
-
- (server, pool)
-}
-
-#[tokio::test]
-async fn config() {
- let (server, _) = setup().await;
- server
- .get("/taler-wire-gateway/config")
- .await
- .assert_ok_json::<WireConfig>();
- server
- .get("/taler-wire-transfer-gateway/config")
- .await
- .assert_ok_json::<WireTransferConfig>();
- server
- .get("/taler-revenue/config")
- .await
- .assert_ok_json::<RevenueConfig>();
-}
-
-#[tokio::test]
-async fn transfer() {
- let (server, _) = setup().await;
- transfer_routine(
- &server,
- TransferState::pending,
- &payto("payto://iban/HU02162000031000164800000000?receiver-name=name"),
- )
- .await;
-}
-
-#[tokio::test]
-async fn outgoing_history() {
- let (server, pool) = setup().await;
- routine_pagination::<OutgoingHistory, _>(
- &server,
- "/taler-wire-gateway/history/outgoing",
- |it| {
- it.outgoing_transactions
- .into_iter()
- .map(|it| *it.row_id as i64)
- .collect()
- },
- async |_, i| {
- let mut conn = pool.acquire().await.unwrap();
- let now = Zoned::now().date();
- db::register_tx_out(
- &mut conn,
- &db::TxOut {
- code: i as u64,
- amount: amount("EUR:10"),
- subject: "subject".into(),
- creditor: PAYTO.clone(),
- value_date: now,
- status: TxStatus::Completed,
- },
- &TxOutKind::Talerable(OutgoingSubject::rand()),
- &Timestamp::now(),
- )
- .await
- .unwrap();
- },
- )
- .await;
-}
-
-#[tokio::test]
-async fn admin_add_incoming() {
- let (server, _) = setup().await;
- admin_add_incoming_routine(&server, &ACCOUNT, true).await;
-}
-
-#[tokio::test]
-async fn revenue() {
- let (server, _) = setup().await;
- revenue_routine(&server, &ACCOUNT, true).await;
-}
-
-async fn check_in(pool: &PgPool) -> Vec<Status> {
- sqlx::query(
- "
- SELECT pending_recurrent_in.authorization_pub IS NOT NULL, initiated_id IS NOT NULL, type, metadata
- FROM tx_in
- LEFT JOIN taler_in USING (tx_in_id)
- LEFT JOIN pending_recurrent_in USING (tx_in_id)
- LEFT JOIN bounced USING (tx_in_id)
- ORDER BY tx_in.tx_in_id
- ",
- )
- .try_map(|r: PgRow| {
- Ok(
- if r.try_get_flag(0)? {
- Status::Pending
- } else if r.try_get_flag(1)? {
- Status::Bounced
- } else {
- match r.try_get(2)? {
- None => Status::Simple,
- Some(IncomingType::reserve) => Status::Reserve(r.try_get(3)?),
- Some(IncomingType::kyc) => Status::Kyc(r.try_get(3)?),
- Some(e) => unreachable!("{e:?}")
- }
- }
- )
- })
- .fetch_all(pool)
- .await
- .unwrap()
-}
-
-pub async fn test_in(pool: &PgPool, key: EddsaPublicKey) {
- let tx = TxIn {
- code: rand::random_range(10..10000),
- amount: amount("EUR:12"),
- subject: Box::default(),
- debtor: PAYTO.clone(),
- value_date: Zoned::now().date(),
- status: TxStatus::Completed,
- };
- let mut db = pool.acquire().await.unwrap();
- let reason = match db::register_tx_in(
- &mut db,
- &tx,
- &Some(IncomingSubject::Map(key)),
- &Timestamp::now(),
- )
- .await
- .unwrap()
- {
- AddIncomingResult::Success { .. } => return,
- AddIncomingResult::ReservePubReuse => "reserve pub reuse",
- AddIncomingResult::UnknownMapping => "unknown mapping",
- AddIncomingResult::MappingReuse => "mapping reuse",
- };
- db::register_bounce_tx_in(&mut db, &tx, reason, &Timestamp::now())
- .await
- .unwrap();
-}
-
-#[tokio::test]
-async fn registration() {
- let (server, pool) = setup().await;
- registration_routine(
- &server,
- &ACCOUNT,
- || check_in(&pool),
- |account_pub| {
- let account_pub = account_pub.clone();
- let pool = &pool;
- async move { test_in(pool, account_pub).await }
- },
- )
- .await;
-}