commit ee271578e49912f80d5a458f1a5473c53d9819a0
parent 124cee60a5900413da674278ab1668a66f1c9a7a
Author: Antoine A <>
Date: Tue, 31 Mar 2026 15:50:53 +0200
common: add new wire gateway test endpoint and improve routines
Diffstat:
13 files changed, 426 insertions(+), 354 deletions(-)
diff --git a/common/taler-api/src/api/wire.rs b/common/taler-api/src/api/wire.rs
@@ -27,9 +27,9 @@ use regex::Regex;
use taler_common::{
api_params::{AccountParams, History, HistoryParams, Page, TransferParams},
api_wire::{
- AccountInfo, AddIncomingRequest, AddIncomingResponse, AddKycauthRequest,
- AddKycauthResponse, IncomingHistory, OutgoingHistory, TransferList, TransferRequest,
- TransferResponse, TransferState, TransferStatus, WireConfig,
+ AccountInfo, AddIncomingRequest, AddIncomingResponse, AddKycauthRequest, AddMappedRequest,
+ IncomingHistory, OutgoingHistory, TransferList, TransferRequest, TransferResponse,
+ TransferState, TransferStatus, WireConfig,
},
error_code::ErrorCode,
};
@@ -73,7 +73,11 @@ pub trait WireGateway: TalerApi {
fn add_incoming_kyc(
&self,
req: AddKycauthRequest,
- ) -> impl std::future::Future<Output = ApiResult<AddKycauthResponse>> + Send;
+ ) -> impl std::future::Future<Output = ApiResult<AddIncomingResponse>> + Send;
+ fn add_incoming_mapped(
+ &self,
+ req: AddMappedRequest,
+ ) -> impl std::future::Future<Output = ApiResult<AddIncomingResponse>> + Send;
fn support_account_check(&self) -> bool;
@@ -189,6 +193,15 @@ pub fn router<I: WireGateway>(state: Arc<I>, auth: AuthMethod) -> Router {
),
)
.route(
+ "/admin/add-mapped",
+ post(
+ async |State(state): State<Arc<I>>, Req(req): Req<AddMappedRequest>| {
+ state.check_currency(&req.amount)?;
+ ApiResult::Ok(Json(state.add_incoming_mapped(req).await?))
+ },
+ ),
+ )
+ .route(
"/account/check",
get(
async |State(state): State<Arc<I>>, Query(params): Query<AccountParams>| match state
diff --git a/common/taler-api/tests/api.rs b/common/taler-api/tests/api.rs
@@ -18,11 +18,10 @@ use std::sync::LazyLock;
use axum::http::StatusCode;
use common::setup;
-use jiff::Timestamp;
use sqlx::{PgPool, Row, postgres::PgRow};
use taler_api::db::TypeHelper as _;
use taler_common::{
- api_common::{EddsaPublicKey, HashCode, ShortHashCode},
+ api_common::{HashCode, ShortHashCode},
api_revenue::RevenueConfig,
api_transfer::PreparedTransferConfig,
api_wire::{OutgoingHistory, TransferResponse, TransferState, WireConfig},
@@ -37,14 +36,11 @@ use taler_common::{
use taler_test_utils::{
json,
routine::{
- Status, admin_add_incoming_routine, registration_routine, revenue_routine,
- routine_pagination, transfer_routine,
+ Status, admin_add_incoming_routine, in_history_routine, registration_routine,
+ revenue_routine, routine_pagination, transfer_routine,
},
server::TestServer as _,
};
-use tracing::warn;
-
-use crate::common::db::{AddIncomingResult, add_incoming};
mod common;
@@ -116,6 +112,12 @@ async fn admin_add_incoming() {
}
#[tokio::test]
+async fn in_history() {
+ let (server, _) = setup().await;
+ in_history_routine(&server, &PAYTO, true).await;
+}
+
+#[tokio::test]
async fn revenue() {
let (server, _) = setup().await;
revenue_routine(&server, &PAYTO, true).await;
@@ -163,61 +165,8 @@ async fn check_in(pool: &PgPool) -> Vec<Status> {
.unwrap()
}
-async fn register_mapped(pool: &PgPool, account_pub: &EddsaPublicKey) {
- let reason = match add_incoming(
- pool,
- &amount("EUR:42"),
- &PAYTO,
- "lol",
- &Timestamp::now(),
- IncomingType::map,
- account_pub,
- )
- .await
- .unwrap()
- {
- AddIncomingResult::Success { .. } => return,
- AddIncomingResult::ReservePubReuse => "reserve pub reuse",
- AddIncomingResult::UnknownMapping => "unknown mapping",
- AddIncomingResult::MappingReuse => "mapping reuse",
- };
- warn!("Bounce {reason}");
- sqlx::query(
- "
- WITH tx_in AS (
- INSERT INTO tx_in (
- amount,
- debit_payto,
- created_at,
- subject
- ) VALUES (
- (32, 0),
- 'payto',
- 0,
- 'subject'
- ) RETURNING tx_in_id
- )
- INSERT INTO bounced (tx_in_id)
- SELECT tx_in_id FROM tx_in
- ",
- )
- .execute(pool)
- .await
- .unwrap();
-}
-
#[tokio::test]
async fn registration() {
let (server, pool) = setup().await;
- registration_routine(
- &server,
- &PAYTO,
- || check_in(&pool),
- |account_pub| {
- let account_pub = account_pub.clone();
- let pool = &pool;
- async move { register_mapped(pool, &account_pub).await }
- },
- )
- .await;
+ registration_routine(&server, &PAYTO, || check_in(&pool)).await;
}
diff --git a/common/taler-api/tests/common/mod.rs b/common/taler-api/tests/common/mod.rs
@@ -35,7 +35,7 @@ use taler_common::{
RegistrationRequest, RegistrationResponse, SubjectFormat, TransferSubject, Unregistration,
},
api_wire::{
- AddIncomingRequest, AddIncomingResponse, AddKycauthRequest, AddKycauthResponse,
+ AddIncomingRequest, AddIncomingResponse, AddKycauthRequest, AddMappedRequest,
IncomingHistory, OutgoingHistory, TransferList, TransferRequest, TransferResponse,
TransferState, TransferStatus,
},
@@ -50,6 +50,8 @@ use taler_common::{
use taler_test_utils::db::db_test_setup_manual;
use tokio::sync::watch::Sender;
+use crate::common::db::AddIncomingResult;
+
pub mod db;
/// Taler API implementation for tests
@@ -76,14 +78,12 @@ impl WireGateway for TestApi {
let result = db::transfer(&self.pool, &req).await?;
match result {
db::TransferResult::Success(transfer_response) => Ok(transfer_response),
- db::TransferResult::RequestUidReuse => Err(failure(
- ErrorCode::BANK_TRANSFER_REQUEST_UID_REUSED,
- "request_uid used already",
- )),
- db::TransferResult::WtidReuse => Err(failure(
- ErrorCode::BANK_TRANSFER_WTID_REUSED,
- "wtid used already",
- )),
+ db::TransferResult::RequestUidReuse => {
+ Err(failure_code(ErrorCode::BANK_TRANSFER_REQUEST_UID_REUSED))
+ }
+ db::TransferResult::WtidReuse => {
+ Err(failure_code(ErrorCode::BANK_TRANSFER_WTID_REUSED))
+ }
}
}
@@ -139,21 +139,20 @@ impl WireGateway for TestApi {
)
.await?;
match res {
- db::AddIncomingResult::Success { id, created_at } => Ok(AddIncomingResponse {
+ AddIncomingResult::Success { id, created_at } => Ok(AddIncomingResponse {
timestamp: created_at.into(),
row_id: id,
}),
- db::AddIncomingResult::ReservePubReuse => Err(failure(
- ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
- "reserve_pub used already".to_owned(),
- )),
- db::AddIncomingResult::UnknownMapping | db::AddIncomingResult::MappingReuse => {
+ AddIncomingResult::ReservePubReuse => {
+ Err(failure_code(ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT))
+ }
+ AddIncomingResult::UnknownMapping | AddIncomingResult::MappingReuse => {
unreachable!("mapping not used")
}
}
}
- async fn add_incoming_kyc(&self, req: AddKycauthRequest) -> ApiResult<AddKycauthResponse> {
+ async fn add_incoming_kyc(&self, req: AddKycauthRequest) -> ApiResult<AddIncomingResponse> {
let res = db::add_incoming(
&self.pool,
&req.amount,
@@ -165,20 +164,47 @@ impl WireGateway for TestApi {
)
.await?;
match res {
- db::AddIncomingResult::Success { id, created_at } => Ok(AddKycauthResponse {
+ AddIncomingResult::Success { id, created_at } => Ok(AddIncomingResponse {
timestamp: created_at.into(),
row_id: id,
}),
- db::AddIncomingResult::ReservePubReuse => Err(failure(
- ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT,
- "reserve_pub used already".to_owned(),
- )),
- db::AddIncomingResult::UnknownMapping | db::AddIncomingResult::MappingReuse => {
+ AddIncomingResult::ReservePubReuse => {
+ Err(failure_code(ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT))
+ }
+ AddIncomingResult::UnknownMapping | AddIncomingResult::MappingReuse => {
unreachable!("mapping not used")
}
}
}
+ async fn add_incoming_mapped(&self, req: AddMappedRequest) -> ApiResult<AddIncomingResponse> {
+ let res = db::add_incoming(
+ &self.pool,
+ &req.amount,
+ &req.debit_account,
+ "",
+ &Timestamp::now(),
+ IncomingType::map,
+ &req.authorization_pub,
+ )
+ .await?;
+ match res {
+ AddIncomingResult::Success { id, created_at } => Ok(AddIncomingResponse {
+ timestamp: created_at.into(),
+ row_id: id,
+ }),
+ AddIncomingResult::ReservePubReuse => {
+ Err(failure_code(ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT))
+ }
+ AddIncomingResult::UnknownMapping => {
+ Err(failure_code(ErrorCode::BANK_TRANSFER_MAPPING_UNKNOWN))
+ }
+ AddIncomingResult::MappingReuse => {
+ Err(failure_code(ErrorCode::BANK_TRANSFER_MAPPING_REUSED))
+ }
+ }
+ }
+
fn support_account_check(&self) -> bool {
false
}
diff --git a/common/taler-common/src/api_wire.rs b/common/taler-common/src/api_wire.rs
@@ -172,13 +172,18 @@ pub struct AddKycauthRequest {
pub debit_account: PaytoURI,
}
+/// <https://docs.taler.net/core/api-bank-wire.html#tsref-type-AddMappedRequest>
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct AddMappedRequest {
+ pub amount: Amount,
+ pub authorization_pub: EddsaPublicKey,
+ pub debit_account: PaytoURI,
+}
+
/// <https://docs.taler.net/core/api-bank-wire.html#tsref-type-AccountInfo>
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AccountInfo {}
-/// <https://docs.taler.net/core/api-bank-wire.html#tsref-type-AddKycauthResponse>
-pub type AddKycauthResponse = AddIncomingResponse;
-
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, sqlx::Type)]
#[allow(non_camel_case_types)]
#[sqlx(type_name = "transfer_status")]
diff --git a/common/taler-common/src/error_code.rs b/common/taler-common/src/error_code.rs
@@ -700,6 +700,12 @@ pub enum ErrorCode {
MERCHANT_GENERIC_DONAU_CHARITY_UNKNOWN = 2039,
/// The merchant does not expect any transfer with the given ID and can thus not return any details about it.
MERCHANT_GENERIC_EXPECTED_TRANSFER_UNKNOWN = 2040,
+ /// The Donau is not known to the backend.
+ MERCHANT_GENERIC_DONAU_UNKNOWN = 2041,
+ /// The access token is not known to the backend.
+ MERCHANT_GENERIC_ACCESS_TOKEN_UNKNOWN = 2042,
+ /// One of the binaries needed to generate the PDF is not installed. If this feature is required, the system administrator should make sure Typst and pdftk are both installed.
+ MERCHANT_GENERIC_NO_TYPST_OR_PDFTK = 2048,
/// The exchange failed to provide a valid answer to the tracking request, thus those details are not in the response.
MERCHANT_GET_ORDERS_EXCHANGE_TRACKING_FAILURE = 2100,
/// The merchant backend failed to construct the request for tracking to the exchange, thus tracking details are not in the response.
@@ -844,6 +850,8 @@ pub enum ErrorCode {
MERCHANT_POST_ORDERS_ID_CLAIM_ALREADY_CLAIMED = 2301,
/// The client-side experienced an internal failure.
MERCHANT_POST_ORDERS_ID_CLAIM_CLIENT_INTERNAL_FAILURE = 2302,
+ /// The unclaim signature of the wallet is not valid for the given contract hash.
+ MERCHANT_POST_ORDERS_UNCLAIM_SIGNATURE_INVALID = 2303,
/// The backend failed to sign the refund request.
MERCHANT_POST_ORDERS_ID_REFUND_SIGNATURE_FAILED = 2350,
/// The client failed to unblind the signature returned by the merchant.
@@ -892,6 +900,8 @@ pub enum ErrorCode {
MERCHANT_PRIVATE_DELETE_ORDERS_AWAITING_PAYMENT = 2520,
/// The order provided to the backend could not be deleted as the order was already paid.
MERCHANT_PRIVATE_DELETE_ORDERS_ALREADY_PAID = 2521,
+ /// The client requested a report granularity that is not available at the backend. Possible solutions include extending the backend code and/or the database statistic triggers to support the desired data granularity. Alternatively, the client could request a different granularity.
+ MERCHANT_PRIVATE_GET_STATISTICS_REPORT_GRANULARITY_UNAVAILABLE = 2525,
/// The amount to be refunded is inconsistent: either is lower than the previous amount being awarded, or it exceeds the original price paid by the customer.
MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_INCONSISTENT_AMOUNT = 2530,
/// Only paid orders can be refunded, and the frontend specified an unpaid order to issue a refund for.
@@ -1134,6 +1144,10 @@ pub enum ErrorCode {
BANK_BAD_SIGNATURE = 5160,
/// The provided timestamp is too old.
BANK_OLD_TIMESTAMP = 5161,
+ /// The authorization_pub for a request to transfer funds has already been used for another non recurrent transfer.
+ BANK_TRANSFER_MAPPING_REUSED = 5162,
+ /// The authorization_pub for a request to transfer funds is not currently registered.
+ BANK_TRANSFER_MAPPING_UNKNOWN = 5163,
/// The sync service failed find the account in its database.
SYNC_ACCOUNT_UNKNOWN = 6100,
/// The SHA-512 hash provided in the If-None-Match header is malformed.
@@ -2730,6 +2744,14 @@ impl ErrorCode {
404,
"The merchant does not expect any transfer with the given ID and can thus not return any details about it.",
),
+ MERCHANT_GENERIC_DONAU_UNKNOWN => (404, "The Donau is not known to the backend."),
+ MERCHANT_GENERIC_ACCESS_TOKEN_UNKNOWN => {
+ (404, "The access token is not known to the backend.")
+ }
+ MERCHANT_GENERIC_NO_TYPST_OR_PDFTK => (
+ 501,
+ "One of the binaries needed to generate the PDF is not installed. If this feature is required, the system administrator should make sure Typst and pdftk are both installed.",
+ ),
MERCHANT_GET_ORDERS_EXCHANGE_TRACKING_FAILURE => (
200,
"The exchange failed to provide a valid answer to the tracking request, thus those details are not in the response.",
@@ -3008,6 +3030,10 @@ impl ErrorCode {
MERCHANT_POST_ORDERS_ID_CLAIM_CLIENT_INTERNAL_FAILURE => {
(0, "The client-side experienced an internal failure.")
}
+ MERCHANT_POST_ORDERS_UNCLAIM_SIGNATURE_INVALID => (
+ 403,
+ "The unclaim signature of the wallet is not valid for the given contract hash.",
+ ),
MERCHANT_POST_ORDERS_ID_REFUND_SIGNATURE_FAILED => {
(0, "The backend failed to sign the refund request.")
}
@@ -3099,6 +3125,10 @@ impl ErrorCode {
409,
"The order provided to the backend could not be deleted as the order was already paid.",
),
+ MERCHANT_PRIVATE_GET_STATISTICS_REPORT_GRANULARITY_UNAVAILABLE => (
+ 410,
+ "The client requested a report granularity that is not available at the backend. Possible solutions include extending the backend code and/or the database statistic triggers to support the desired data granularity. Alternatively, the client could request a different granularity.",
+ ),
MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_INCONSISTENT_AMOUNT => (
409,
"The amount to be refunded is inconsistent: either is lower than the previous amount being awarded, or it exceeds the original price paid by the customer.",
@@ -3519,6 +3549,14 @@ impl ErrorCode {
BANK_DERIVATION_REUSE => (409, "The derived subject is already used."),
BANK_BAD_SIGNATURE => (409, "The provided signature is invalid."),
BANK_OLD_TIMESTAMP => (409, "The provided timestamp is too old."),
+ BANK_TRANSFER_MAPPING_REUSED => (
+ 409,
+ "The authorization_pub for a request to transfer funds has already been used for another non recurrent transfer.",
+ ),
+ BANK_TRANSFER_MAPPING_UNKNOWN => (
+ 409,
+ "The authorization_pub for a request to transfer funds is not currently registered.",
+ ),
SYNC_ACCOUNT_UNKNOWN => (
404,
"The sync service failed find the account in its database.",
diff --git a/common/taler-test-utils/src/routine.rs b/common/taler-test-utils/src/routine.rs
@@ -145,56 +145,6 @@ async fn assert_time<R: Debug>(range: std::ops::Range<u128>, task: impl Future<O
}
}
-async fn check_history_trigger<T: Page>(
- server: &Router,
- url: &str,
- lambda: impl AsyncFnOnce() -> (),
-) {
- // Check history is following specs
- macro_rules! assert_history {
- ($args:expr, $size:expr) => {
- async {
- server
- .get(&format!("{url}?{}", $args))
- .await
- .assert_ids::<T>($size)
- }
- };
- }
- // Get latest registered id
- let latest_id = latest_id::<T>(server, url).await;
- tokio::join!(
- // Check polling succeed
- assert_time(
- 100..400,
- assert_history!(
- format_args!("limit=2&offset={latest_id}&timeout_ms=1000"),
- 1
- )
- ),
- assert_time(
- 200..500,
- assert_history!(
- format_args!("limit=1&offset={}&timeout_ms=200", latest_id + 3),
- 0
- )
- ),
- async {
- sleep(Duration::from_millis(100)).await;
- lambda().await
- }
- );
-}
-
-async fn check_history_in_trigger(server: &Router, lambda: impl AsyncFnOnce() -> ()) {
- check_history_trigger::<IncomingHistory>(
- server,
- "/taler-wire-gateway/history/incoming",
- lambda,
- )
- .await;
-}
-
pub async fn routine_history<T: Page>(
server: &Router,
url: &str,
@@ -359,7 +309,7 @@ impl TestResponse {
}
// Get currency from config
-async fn get_wire_currency(server: &Router) -> String {
+async fn get_currency(server: &Router) -> String {
let config = server
.get("/taler-wire-gateway/config")
.await
@@ -374,7 +324,7 @@ pub async fn transfer_routine(
default_status: TransferState,
credit_account: &PaytoURI,
) {
- let currency = &get_wire_currency(server).await;
+ let currency = &get_currency(server).await;
let default_amount = amount(format!("{currency}:42"));
let request_uid = HashCode::rand();
let wtid = ShortHashCode::rand();
@@ -625,11 +575,27 @@ async fn add_incoming_routine(
let (path, key) = match kind {
IncomingType::reserve => ("/taler-wire-gateway/admin/add-incoming", "reserve_pub"),
IncomingType::kyc => ("/taler-wire-gateway/admin/add-kycauth", "account_pub"),
- IncomingType::map => unreachable!(),
+ IncomingType::map => ("/taler-wire-gateway/admin/add-mapped", "authorization_pub"),
};
+ let key_pair = Ed25519KeyPair::generate().unwrap();
+ let pub_key = EddsaPublicKey::try_from(key_pair.public_key().as_ref()).unwrap();
+ // Valid
+ server
+ .post("/taler-prepared-transfer/registration")
+ .json(&json!({
+ "type": "reserve",
+ "credit_amount": format!("{currency}:44"),
+ "alg": "EdDSA",
+ "account_pub": pub_key,
+ "authorization_pub": pub_key,
+ "authorization_sig": eddsa_sign(&key_pair, pub_key.as_ref()),
+ "recurrent": false
+ }))
+ .await
+ .assert_ok_json::<RegistrationResponse>();
let valid_req = json!({
"amount": format!("{currency}:44"),
- key: EddsaPublicKey::rand(),
+ key: pub_key,
"debit_account": debit_acount,
});
@@ -651,7 +617,22 @@ async fn add_incoming_routine(
// Non conflict on reuse
server.post(path).json(&valid_req).await.assert_ok();
}
- IncomingType::map => unreachable!(),
+ IncomingType::map => {
+ // Trigger conflict due to reused authorization_pub
+ server
+ .post(path)
+ .json(&valid_req)
+ .await
+ .assert_error(ErrorCode::BANK_TRANSFER_MAPPING_REUSED);
+ // Trigger conflict due to unknown authorization_pub
+ server
+ .post(path)
+ .json(&json!(valid_req + {
+ key: EddsaPublicKey::rand()
+ }))
+ .await
+ .assert_error(ErrorCode::BANK_TRANSFER_MAPPING_UNKNOWN);
+ }
}
// Currency mismatch
@@ -692,7 +673,7 @@ async fn add_incoming_routine(
/// Test standard behavior of the revenue endpoints
pub async fn revenue_routine(server: &Router, debit_acount: &PaytoURI, kyc: bool) {
- let currency = &get_wire_currency(server).await;
+ let currency = &get_currency(server).await;
routine_history::<RevenueIncomingHistory>(
server,
@@ -727,18 +708,19 @@ pub async fn revenue_routine(server: &Router, debit_acount: &PaytoURI, kyc: bool
.await;
}
-/// Test standard behavior of the admin add incoming endpoints
-pub async fn admin_add_incoming_routine(server: &Router, debit_acount: &PaytoURI, kyc: bool) {
- let currency = &get_wire_currency(server).await;
-
+/// Test standard behavior of the incoming history endpoint
+pub async fn in_history_routine(server: &Router, debit_acount: &PaytoURI, kyc: bool) {
+ let currency = &get_currency(server).await;
// History
// TODO check non taler some are ignored
+ let mut key_pair = Ed25519KeyPair::generate().unwrap();
+ let len = if kyc { 6 } else { 3 };
routine_history::<IncomingHistory>(
server,
"/taler-wire-gateway/history/incoming",
- 2,
- async |i| {
- if i % 2 == 0 || !kyc {
+ len,
+ async |i| match i % len {
+ 0 => {
server
.post("/taler-wire-gateway/admin/add-incoming")
.json(&json!({
@@ -748,7 +730,62 @@ pub async fn admin_add_incoming_routine(server: &Router, debit_acount: &PaytoURI
}))
.await
.assert_ok_json::<TransferResponse>();
- } else {
+ }
+ 1 => {
+ key_pair = Ed25519KeyPair::generate().unwrap();
+ let auth_pub = EddsaPublicKey::try_from(key_pair.public_key().as_ref()).unwrap();
+ let reserve_pub = EddsaPublicKey::rand();
+ let amount = format!("{currency}:0.0{i}");
+ server
+ .post("/taler-prepared-transfer/registration")
+ .json(&json!({
+ "credit_amount": amount,
+ "type": "reserve",
+ "alg": "EdDSA",
+ "account_pub": reserve_pub,
+ "authorization_pub": auth_pub,
+ "authorization_sig": eddsa_sign(&key_pair, reserve_pub.as_ref()),
+ "recurrent": true
+ }))
+ .await
+ .assert_ok_json::<RegistrationResponse>();
+ server
+ .post("/taler-wire-gateway/admin/add-mapped")
+ .json(&json!({
+ "amount": amount,
+ "authorization_pub": auth_pub,
+ "debit_account": debit_acount,
+ }))
+ .await
+ .assert_ok_json::<TransferResponse>();
+ server
+ .post("/taler-wire-gateway/admin/add-mapped")
+ .json(&json!({
+ "amount": amount,
+ "authorization_pub": auth_pub,
+ "debit_account": debit_acount,
+ }))
+ .await
+ .assert_ok_json::<TransferResponse>();
+ }
+ 2 => {
+ let auth_pub = EddsaPublicKey::try_from(key_pair.public_key().as_ref()).unwrap();
+ let reserve_pub = EddsaPublicKey::rand();
+ server
+ .post("/taler-prepared-transfer/registration")
+ .json(&json!({
+ "credit_amount": format!("{currency}:0.0{i}"),
+ "type": "reserve",
+ "alg": "EdDSA",
+ "account_pub": reserve_pub,
+ "authorization_pub": auth_pub,
+ "authorization_sig": eddsa_sign(&key_pair, reserve_pub.as_ref()),
+ "recurrent": true
+ }))
+ .await
+ .assert_ok_json::<RegistrationResponse>();
+ }
+ 3 => {
server
.post("/taler-wire-gateway/admin/add-kycauth")
.json(&json!({
@@ -759,29 +796,78 @@ pub async fn admin_add_incoming_routine(server: &Router, debit_acount: &PaytoURI
.await
.assert_ok_json::<TransferResponse>();
}
+ 4 => {
+ key_pair = Ed25519KeyPair::generate().unwrap();
+ let auth_pub = EddsaPublicKey::try_from(key_pair.public_key().as_ref()).unwrap();
+ let account_pub = EddsaPublicKey::rand();
+ let amount = format!("{currency}:0.0{i}");
+ server
+ .post("/taler-prepared-transfer/registration")
+ .json(&json!({
+ "credit_amount": amount,
+ "type": "kyc",
+ "alg": "EdDSA",
+ "account_pub": account_pub,
+ "authorization_pub": auth_pub,
+ "authorization_sig": eddsa_sign(&key_pair, account_pub.as_ref()),
+ "recurrent": true
+ }))
+ .await
+ .assert_ok_json::<RegistrationResponse>();
+ server
+ .post("/taler-wire-gateway/admin/add-mapped")
+ .json(&json!({
+ "amount": amount,
+ "authorization_pub": auth_pub,
+ "debit_account": debit_acount,
+ }))
+ .await
+ .assert_ok_json::<TransferResponse>();
+ server
+ .post("/taler-wire-gateway/admin/add-mapped")
+ .json(&json!({
+ "amount": amount,
+ "authorization_pub": auth_pub,
+ "debit_account": debit_acount,
+ }))
+ .await
+ .assert_ok_json::<TransferResponse>();
+ }
+ 5 => {
+ let auth_pub = EddsaPublicKey::try_from(key_pair.public_key().as_ref()).unwrap();
+ let account_pub = EddsaPublicKey::rand();
+ server
+ .post("/taler-prepared-transfer/registration")
+ .json(&json!({
+ "credit_amount": format!("{currency}:0.0{i}"),
+ "type": "kyc",
+ "alg": "EdDSA",
+ "account_pub": account_pub,
+ "authorization_pub": auth_pub,
+ "authorization_sig": eddsa_sign(&key_pair, account_pub.as_ref()),
+ "recurrent": true
+ }))
+ .await
+ .assert_ok_json::<RegistrationResponse>();
+ }
+ nb => unreachable!("unexpected state {nb}"),
},
0,
async |_| {},
)
.await;
- // Add incoming reserve
+}
+
+/// Test standard behavior of the admin add incoming endpoints
+pub async fn admin_add_incoming_routine(server: &Router, debit_acount: &PaytoURI, kyc: bool) {
+ let currency = &get_currency(server).await;
add_incoming_routine(server, currency, IncomingType::reserve, debit_acount).await;
+ add_incoming_routine(server, currency, IncomingType::map, debit_acount).await;
if kyc {
- // Add incoming kyc
add_incoming_routine(server, currency, IncomingType::kyc, debit_acount).await;
}
}
-// Get currency from config
-async fn get_transfer_currency(server: &Router) -> String {
- let config = server
- .get("/taler-prepared-transfer/config")
- .await
- .assert_ok_json::<serde_json::Value>();
- let currency = config["currency"].as_str().unwrap();
- currency.to_owned()
-}
-
#[derive(Debug, PartialEq, Eq)]
pub enum Status {
Simple,
@@ -793,11 +879,10 @@ pub enum Status {
}
/// Test standard registration behavior of the registration endpoints
-pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<Output = ()>>(
+pub async fn registration_routine<F1: Future<Output = Vec<Status>>>(
server: &Router,
account: &PaytoURI,
mut in_status: impl FnMut() -> F1,
- mut register: impl FnMut(&EddsaPublicKey) -> F2,
) {
pub use Status::*;
let mut check_in = async |state: &[Status]| {
@@ -805,7 +890,7 @@ pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<O
assert_eq!(state, current);
};
- let currency = &get_transfer_currency(server).await;
+ let currency = &get_currency(server).await;
let amount = amount(format!("{currency}:42"));
let key_pair1 = Ed25519KeyPair::generate().unwrap();
let auth_pub1 = EddsaPublicKey::try_from(key_pair1.public_key().as_ref()).unwrap();
@@ -819,6 +904,17 @@ pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<O
"recurrent": false
});
+ let register = async |auth_pub: &EddsaPublicKey| {
+ server
+ .post("/taler-wire-gateway/admin/add-mapped")
+ .json(&json!({
+ "amount": format!("{currency}:42"),
+ "authorization_pub": auth_pub,
+ "debit_account": account,
+ }))
+ .await
+ };
+
/* ----- Registration ----- */
let routine = async |ty: TransferType,
account_pub: &EddsaPublicKey,
@@ -908,10 +1004,13 @@ pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<O
}))
.await
.assert_ok_json::<RegistrationResponse>();
- check_history_in_trigger(server, async || register(&auth_pub1).await).await;
+ register(&auth_pub1)
+ .await
+ .assert_ok_json::<TransferResponse>();
check_in(&[Reserve(acc_pub1.clone())]).await;
- register(&auth_pub1).await;
- check_in(&[Reserve(acc_pub1.clone()), Bounced]).await;
+ register(&auth_pub1)
+ .await
+ .assert_error(ErrorCode::BANK_TRANSFER_MAPPING_REUSED);
// Again without using mapping
let acc_pub2 = EddsaPublicKey::rand();
@@ -932,14 +1031,10 @@ pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<O
}))
.await
.assert_ok();
- register(&auth_pub1).await;
- check_in(&[
- Reserve(acc_pub1.clone()),
- Bounced,
- Reserve(acc_pub2.clone()),
- Bounced,
- ])
- .await;
+ register(&auth_pub1)
+ .await
+ .assert_error(ErrorCode::BANK_TRANSFER_MAPPING_REUSED);
+ check_in(&[Reserve(acc_pub1.clone()), Reserve(acc_pub2.clone())]).await;
// Recurrent accept one and delay others
let acc_pub3 = EddsaPublicKey::rand();
@@ -953,13 +1048,13 @@ pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<O
.await
.assert_ok_json::<RegistrationResponse>();
for _ in 0..5 {
- register(&auth_pub1).await;
+ register(&auth_pub1)
+ .await
+ .assert_ok_json::<TransferResponse>();
}
check_in(&[
Reserve(acc_pub1.clone()),
- Bounced,
Reserve(acc_pub2.clone()),
- Bounced,
Reserve(acc_pub3.clone()),
Pending,
Pending,
@@ -980,23 +1075,18 @@ pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<O
}))
.await
.assert_ok_json::<RegistrationResponse>();
- check_history_in_trigger(server, async || {
- server
- .post("/taler-prepared-transfer/registration")
- .json(&json!(req + {
- "account_pub": acc_pub4,
- "authorization_sig": eddsa_sign(&key_pair1, acc_pub4.as_ref()),
- "recurrent": true
- }))
- .await
- .assert_ok_json::<RegistrationResponse>();
- })
- .await;
+ server
+ .post("/taler-prepared-transfer/registration")
+ .json(&json!(req + {
+ "account_pub": acc_pub4,
+ "authorization_sig": eddsa_sign(&key_pair1, acc_pub4.as_ref()),
+ "recurrent": true
+ }))
+ .await
+ .assert_ok_json::<RegistrationResponse>();
check_in(&[
Reserve(acc_pub1.clone()),
- Bounced,
Reserve(acc_pub2.clone()),
- Bounced,
Reserve(acc_pub3.clone()),
Kyc(acc_pub4.clone()),
Reserve(acc_pub4.clone()),
@@ -1017,9 +1107,7 @@ pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<O
.assert_ok_json::<TransferResponse>();
check_in(&[
Reserve(acc_pub1.clone()),
- Bounced,
Reserve(acc_pub2.clone()),
- Bounced,
Reserve(acc_pub3.clone()),
Kyc(acc_pub4.clone()),
Reserve(acc_pub4.clone()),
@@ -1043,13 +1131,13 @@ pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<O
.await
.assert_ok_json::<RegistrationResponse>();
for _ in 0..3 {
- register(&auth_pub2).await;
+ register(&auth_pub2)
+ .await
+ .assert_ok_json::<TransferResponse>();
}
check_in(&[
Reserve(acc_pub1.clone()),
- Bounced,
Reserve(acc_pub2.clone()),
- Bounced,
Reserve(acc_pub3.clone()),
Kyc(acc_pub4.clone()),
Reserve(acc_pub4.clone()),
@@ -1074,9 +1162,7 @@ pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<O
.assert_ok_json::<RegistrationResponse>();
check_in(&[
Reserve(acc_pub1.clone()),
- Bounced,
Reserve(acc_pub2.clone()),
- Bounced,
Reserve(acc_pub3.clone()),
Kyc(acc_pub4.clone()),
Reserve(acc_pub4.clone()),
@@ -1111,12 +1197,12 @@ pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<O
}))
.await
.assert_ok();
- register(&auth_pub2).await;
+ register(&auth_pub2)
+ .await
+ .assert_ok_json::<TransferResponse>();
check_in(&[
Reserve(acc_pub1.clone()),
- Bounced,
Reserve(acc_pub2.clone()),
- Bounced,
Reserve(acc_pub3.clone()),
Kyc(acc_pub4.clone()),
Reserve(acc_pub4.clone()),
@@ -1163,13 +1249,14 @@ pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<O
}))
.await
.assert_ok();
- register(&auth_pub2).await;
- register(&auth_pub2).await;
+ for _ in 0..2 {
+ register(&auth_pub2)
+ .await
+ .assert_ok_json::<TransferResponse>();
+ }
check_in(&[
Reserve(acc_pub1.clone()),
- Bounced,
Reserve(acc_pub2.clone()),
- Bounced,
Reserve(acc_pub3.clone()),
Kyc(acc_pub4.clone()),
Reserve(acc_pub4.clone()),
@@ -1206,9 +1293,7 @@ pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<O
check_in(&[
Reserve(acc_pub1.clone()),
- Bounced,
Reserve(acc_pub2.clone()),
- Bounced,
Reserve(acc_pub3.clone()),
Kyc(acc_pub4.clone()),
Reserve(acc_pub4.clone()),
@@ -1290,7 +1375,6 @@ pub async fn registration_routine<F1: Future<Output = Vec<Status>>, F2: Future<O
(acc_pub, auth_pub)
})
.collect();
- dbg!(&history);
assert_eq!(
history,
[
diff --git a/common/taler-test-utils/src/server.rs b/common/taler-test-utils/src/server.rs
@@ -154,6 +154,7 @@ impl IntoFuture for TestRequest {
}
}
+#[must_use]
pub struct TestResponse {
bytes: Bytes,
method: Method,
diff --git a/taler-cyclos/db/cyclos-procedures.sql b/taler-cyclos/db/cyclos-procedures.sql
@@ -69,8 +69,7 @@ out_pending=false;
SELECT tx_in_id, valued_at
INTO out_tx_row_id, out_valued_at
FROM tx_in
-WHERE (in_transfer_id IS NOT NULL AND transfer_id = in_transfer_id) -- Cyclos transaction
- OR (in_transfer_id IS NULL AND amount = in_amount AND debit_account = in_debit_account AND subject = in_subject); -- Admin transaction
+WHERE transfer_id = in_transfer_id;
out_new = NOT found;
IF NOT out_new THEN
RETURN;
diff --git a/taler-cyclos/src/api.rs b/taler-cyclos/src/api.rs
@@ -29,7 +29,7 @@ use taler_common::{
RegistrationRequest, RegistrationResponse, SubjectFormat, TransferSubject, Unregistration,
},
api_wire::{
- AddIncomingRequest, AddIncomingResponse, AddKycauthRequest, AddKycauthResponse,
+ AddIncomingRequest, AddIncomingResponse, AddKycauthRequest, AddMappedRequest,
IncomingHistory, OutgoingHistory, TransferList, TransferRequest, TransferResponse,
TransferState, TransferStatus,
},
@@ -205,7 +205,7 @@ impl WireGateway for CyclosApi {
}
}
- async fn add_incoming_kyc(&self, req: AddKycauthRequest) -> ApiResult<AddKycauthResponse> {
+ async fn add_incoming_kyc(&self, req: AddKycauthRequest) -> ApiResult<AddIncomingResponse> {
let debtor = FullCyclosPayto::try_from(&req.debit_account)?;
let res = db::register_tx_in_admin(
&self.pool,
@@ -222,7 +222,7 @@ impl WireGateway for CyclosApi {
match res {
AddIncomingResult::Success {
row_id, valued_at, ..
- } => Ok(AddKycauthResponse {
+ } => Ok(AddIncomingResponse {
row_id: safe_u64(row_id),
timestamp: valued_at.into(),
}),
@@ -235,6 +235,39 @@ impl WireGateway for CyclosApi {
}
}
+ async fn add_incoming_mapped(&self, req: AddMappedRequest) -> ApiResult<AddIncomingResponse> {
+ let debtor = FullCyclosPayto::try_from(&req.debit_account)?;
+ let res = db::register_tx_in_admin(
+ &self.pool,
+ &TxInAdmin {
+ amount: req.amount.decimal(),
+ subject: format!("Admin incoming MAP:{}", req.authorization_pub),
+ debtor_id: *debtor.id,
+ debtor_name: debtor.name,
+ metadata: IncomingSubject::Map(req.authorization_pub),
+ },
+ &Timestamp::now(),
+ )
+ .await?;
+ match res {
+ AddIncomingResult::Success {
+ row_id, valued_at, ..
+ } => Ok(AddIncomingResponse {
+ row_id: safe_u64(row_id),
+ timestamp: valued_at.into(),
+ }),
+ AddIncomingResult::ReservePubReuse => {
+ Err(failure_code(ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT))
+ }
+ AddIncomingResult::UnknownMapping => {
+ Err(failure_code(ErrorCode::BANK_TRANSFER_MAPPING_UNKNOWN))
+ }
+ AddIncomingResult::MappingReuse => {
+ Err(failure_code(ErrorCode::BANK_TRANSFER_MAPPING_REUSED))
+ }
+ }
+ }
+
fn support_account_check(&self) -> bool {
false
}
@@ -306,13 +339,9 @@ mod test {
use jiff::Timestamp;
use sqlx::{PgPool, Row as _, postgres::PgRow};
use taler_api::{
- api::TalerRouter as _,
- auth::AuthMethod,
- db::TypeHelper as _,
- subject::{IncomingSubject, OutgoingSubject},
+ api::TalerRouter as _, auth::AuthMethod, db::TypeHelper as _, subject::OutgoingSubject,
};
use taler_common::{
- api_common::EddsaPublicKey,
api_revenue::RevenueConfig,
api_transfer::PreparedTransferConfig,
api_wire::{OutgoingHistory, TransferState, WireConfig},
@@ -326,8 +355,8 @@ mod test {
Router,
db::db_test_setup,
routine::{
- Status, admin_add_incoming_routine, registration_routine, revenue_routine,
- routine_pagination, transfer_routine,
+ Status, admin_add_incoming_routine, in_history_routine, registration_routine,
+ revenue_routine, routine_pagination, transfer_routine,
},
server::TestServer as _,
};
@@ -335,7 +364,7 @@ mod test {
use crate::{
api::CyclosApi,
constants::CONFIG_SOURCE,
- db::{self, AddIncomingResult, TxIn, TxOutKind},
+ db::{self, TxOutKind},
};
static ACCOUNT: LazyLock<PaytoURI> =
@@ -423,6 +452,12 @@ mod test {
}
#[tokio::test]
+ async fn in_history() {
+ let (server, _) = setup().await;
+ in_history_routine(&server, &ACCOUNT, true).await;
+ }
+
+ #[tokio::test]
async fn revenue() {
let (server, _) = setup().await;
revenue_routine(&server, &ACCOUNT, true).await;
@@ -460,55 +495,9 @@ mod test {
.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;
+ registration_routine(&server, &ACCOUNT, || check_in(&pool)).await;
}
}
diff --git a/taler-cyclos/src/db.rs b/taler-cyclos/src/db.rs
@@ -1131,18 +1131,6 @@ mod test {
valued_at: now
}
);
- // Idempotent
- assert_eq!(
- db::register_tx_in_admin(&pool, &tx, &later)
- .await
- .expect("register tx in"),
- AddIncomingResult::Success {
- new: false,
- pending: false,
- row_id: 1,
- valued_at: now
- }
- );
// Many
assert_eq!(
db::register_tx_in_admin(
diff --git a/taler-magnet-bank/db/magnet-bank-procedures.sql b/taler-magnet-bank/db/magnet-bank-procedures.sql
@@ -68,8 +68,7 @@ out_pending=false;
SELECT tx_in_id, valued_at
INTO out_tx_row_id, out_valued_at
FROM tx_in
-WHERE (in_code IS NOT NULL AND magnet_code = in_code) -- Magnet transaction
- OR (in_code IS NULL AND amount = in_amount AND debit_account = in_debit_account AND subject = in_subject); -- Admin transaction
+WHERE magnet_code = in_code;
out_new = NOT found;
IF NOT out_new THEN
RETURN;
@@ -449,7 +448,7 @@ LOOP
)
SELECT
amount,
- 'bounce: ' || magnet_code,
+ CONCAT('bounce: ', magnet_code),
debit_account,
debit_name,
in_timestamp
diff --git a/taler-magnet-bank/src/api.rs b/taler-magnet-bank/src/api.rs
@@ -28,7 +28,7 @@ use taler_common::{
RegistrationRequest, RegistrationResponse, SubjectFormat, TransferSubject, Unregistration,
},
api_wire::{
- AddIncomingRequest, AddIncomingResponse, AddKycauthRequest, AddKycauthResponse,
+ AddIncomingRequest, AddIncomingResponse, AddKycauthRequest, AddMappedRequest,
IncomingHistory, OutgoingHistory, TransferList, TransferRequest, TransferResponse,
TransferState, TransferStatus,
},
@@ -185,7 +185,7 @@ impl WireGateway for MagnetApi {
}
}
- async fn add_incoming_kyc(&self, req: AddKycauthRequest) -> ApiResult<AddKycauthResponse> {
+ async fn add_incoming_kyc(&self, req: AddKycauthRequest) -> ApiResult<AddIncomingResponse> {
let debtor = FullHuPayto::try_from(&req.debit_account)?;
let res = db::register_tx_in_admin(
&self.pool,
@@ -201,15 +201,45 @@ impl WireGateway for MagnetApi {
match res {
AddIncomingResult::Success {
row_id, valued_at, ..
- } => Ok(AddKycauthResponse {
+ } => Ok(AddIncomingResponse {
+ row_id: safe_u64(row_id),
+ timestamp: date_to_utc_ts(&valued_at).into(),
+ }),
+ AddIncomingResult::ReservePubReuse => unreachable!("kyc"),
+ AddIncomingResult::UnknownMapping | AddIncomingResult::MappingReuse => {
+ unreachable!("mapping not used")
+ }
+ }
+ }
+
+ async fn add_incoming_mapped(&self, req: AddMappedRequest) -> ApiResult<AddIncomingResponse> {
+ let debtor = FullHuPayto::try_from(&req.debit_account)?;
+ let res = db::register_tx_in_admin(
+ &self.pool,
+ &TxInAdmin {
+ amount: req.amount,
+ subject: format!("Admin incoming MAP:{}", req.authorization_pub),
+ debtor,
+ metadata: IncomingSubject::Map(req.authorization_pub),
+ },
+ &Timestamp::now(),
+ )
+ .await?;
+ match res {
+ AddIncomingResult::Success {
+ row_id, valued_at, ..
+ } => Ok(AddIncomingResponse {
row_id: safe_u64(row_id),
timestamp: date_to_utc_ts(&valued_at).into(),
}),
AddIncomingResult::ReservePubReuse => {
Err(failure_code(ErrorCode::BANK_DUPLICATE_RESERVE_PUB_SUBJECT))
}
- AddIncomingResult::UnknownMapping | AddIncomingResult::MappingReuse => {
- unreachable!("mapping not used")
+ AddIncomingResult::UnknownMapping => {
+ Err(failure_code(ErrorCode::BANK_TRANSFER_MAPPING_UNKNOWN))
+ }
+ AddIncomingResult::MappingReuse => {
+ Err(failure_code(ErrorCode::BANK_TRANSFER_MAPPING_REUSED))
}
}
}
@@ -279,7 +309,7 @@ mod test {
FullHuPayto,
api::MagnetApi,
constants::CONFIG_SOURCE,
- db::{self, AddIncomingResult, TxIn, TxOutKind},
+ db::{self, TxOutKind},
magnet_api::types::TxStatus,
magnet_payto,
};
@@ -287,13 +317,9 @@ mod test {
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},
+ api::TalerRouter as _, auth::AuthMethod, db::TypeHelper as _, subject::OutgoingSubject,
};
use taler_common::{
- api_common::EddsaPublicKey,
api_revenue::RevenueConfig,
api_transfer::PreparedTransferConfig,
api_wire::{OutgoingHistory, TransferState, WireConfig},
@@ -307,8 +333,8 @@ mod test {
Router,
db::db_test_setup,
routine::{
- Status, admin_add_incoming_routine, registration_routine, revenue_routine,
- routine_pagination, transfer_routine,
+ Status, admin_add_incoming_routine, in_history_routine, registration_routine,
+ revenue_routine, routine_pagination, transfer_routine,
},
server::TestServer,
};
@@ -394,6 +420,12 @@ mod test {
}
#[tokio::test]
+ async fn in_history() {
+ let (server, _) = setup().await;
+ in_history_routine(&server, &ACCOUNT, true).await;
+ }
+
+ #[tokio::test]
async fn revenue() {
let (server, _) = setup().await;
revenue_routine(&server, &ACCOUNT, true).await;
@@ -431,48 +463,9 @@ mod test {
.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;
+ registration_routine(&server, &ACCOUNT, || check_in(&pool)).await;
}
}
diff --git a/taler-magnet-bank/src/db.rs b/taler-magnet-bank/src/db.rs
@@ -1095,18 +1095,6 @@ mod test {
valued_at: date
}
);
- // Idempotent
- assert_eq!(
- register_tx_in_admin(&pool, &tx, &later)
- .await
- .expect("register tx in"),
- AddIncomingResult::Success {
- new: false,
- pending: false,
- row_id: 1,
- valued_at: date
- }
- );
// Many
assert_eq!(
register_tx_in_admin(