mirror of
https://github.com/RaisFast/raisfast.git
synced 2026-09-24 08:02:36 +00:00
275 lines
8.0 KiB
Rust
275 lines
8.0 KiB
Rust
use super::*;
|
|
|
|
static COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
|
|
|
|
fn unique_tag() -> u64 {
|
|
COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
|
|
}
|
|
|
|
async fn setup_admin() -> (axum::Router, AppState, String) {
|
|
let (app, state) = test_app().await;
|
|
raisfast::models::currencies::create(&state.pool, "CNY", "Chinese Yuan", 2)
|
|
.await
|
|
.unwrap();
|
|
raisfast::models::currencies::create(&state.pool, "USD", "US Dollar", 2)
|
|
.await
|
|
.unwrap();
|
|
let (int_id, doc_id) = create_admin(&state.pool).await;
|
|
let tok = make_token(&doc_id, int_id, raisfast::models::user::UserRole::Admin);
|
|
(app, state, tok)
|
|
}
|
|
|
|
async fn seed_user_get_doc_id(app: &mut axum::Router, tag: u64) -> (String, String) {
|
|
let email = format!("w{tag}@test.com");
|
|
let username = format!("wuser{tag}");
|
|
let (tok, _) = register_and_login(app, &email, &username, "Password123").await;
|
|
let (_, body) = send(app, get_auth("/api/v1/users/me", &tok)).await;
|
|
let doc_id = body["data"]["id"].as_str().unwrap().to_string();
|
|
(tok, doc_id)
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn admin_credit_creates_wallet() {
|
|
let tag = unique_tag();
|
|
let (mut app, _state, tok) = setup_admin().await;
|
|
let (_user_tok, user_doc_id) = seed_user_get_doc_id(&mut app, tag).await;
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/wallets/credit",
|
|
json!({
|
|
"user_id": user_doc_id,
|
|
"currency": "CNY",
|
|
"transaction_no": format!("TXCREDIT{tag}"),
|
|
"amount": 10000,
|
|
}),
|
|
&tok,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success(), "credit: {status} {body:?}");
|
|
assert_eq!(body["data"]["amount"], 10000);
|
|
assert_eq!(body["data"]["entry_type"], "credit");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn admin_debit_deducts_balance() {
|
|
let tag = unique_tag();
|
|
let (mut app, _state, tok) = setup_admin().await;
|
|
let (_user_tok, user_doc_id) = seed_user_get_doc_id(&mut app, tag).await;
|
|
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/wallets/credit",
|
|
json!({
|
|
"user_id": user_doc_id,
|
|
"currency": "CNY",
|
|
"transaction_no": format!("TXPRE{tag}"),
|
|
"amount": 5000,
|
|
}),
|
|
&tok,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/wallets/debit",
|
|
json!({
|
|
"user_id": user_doc_id,
|
|
"currency": "CNY",
|
|
"transaction_no": format!("TXDEBIT{tag}"),
|
|
"amount": 2000,
|
|
}),
|
|
&tok,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success(), "debit: {status} {body:?}");
|
|
assert_eq!(body["data"]["amount"], 2000);
|
|
assert_eq!(body["data"]["entry_type"], "debit");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn user_list_wallets_after_credit() {
|
|
let tag = unique_tag();
|
|
let (mut app, _state, tok) = setup_admin().await;
|
|
let (user_tok, user_doc_id) = seed_user_get_doc_id(&mut app, tag).await;
|
|
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/wallets/credit",
|
|
json!({
|
|
"user_id": user_doc_id,
|
|
"currency": "CNY",
|
|
"transaction_no": format!("TXLIST{tag}"),
|
|
"amount": 3000,
|
|
}),
|
|
&tok,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (status, body) = send(&mut app, get_auth("/api/v1/wallets", &user_tok)).await;
|
|
assert!(status.is_success(), "list wallets: {status} {body:?}");
|
|
let items = body["data"].as_array().unwrap();
|
|
assert!(!items.is_empty());
|
|
let cny = items.iter().find(|w| w["currency"] == "CNY").unwrap();
|
|
assert_eq!(cny["balance"], 3000);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn user_get_wallet_by_currency() {
|
|
let tag = unique_tag();
|
|
let (mut app, _state, tok) = setup_admin().await;
|
|
let (user_tok, user_doc_id) = seed_user_get_doc_id(&mut app, tag).await;
|
|
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/wallets/credit",
|
|
json!({
|
|
"user_id": user_doc_id,
|
|
"currency": "USD",
|
|
"transaction_no": format!("TXUSD{tag}"),
|
|
"amount": 5000,
|
|
}),
|
|
&tok,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (status, body) = send(&mut app, get_auth("/api/v1/wallets/USD", &user_tok)).await;
|
|
assert!(status.is_success(), "get wallet: {status} {body:?}");
|
|
assert_eq!(body["data"]["currency"], "USD");
|
|
assert_eq!(body["data"]["balance"], 5000);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn admin_list_all_wallets() {
|
|
let tag = unique_tag();
|
|
let (mut app, _state, tok) = setup_admin().await;
|
|
let (_user_tok, user_doc_id) = seed_user_get_doc_id(&mut app, tag).await;
|
|
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/wallets/credit",
|
|
json!({
|
|
"user_id": user_doc_id,
|
|
"currency": "CNY",
|
|
"transaction_no": format!("TXALIST{tag}"),
|
|
"amount": 1000,
|
|
}),
|
|
&tok,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
get_auth("/api/v1/admin/wallets?page=1&page_size=10", &tok),
|
|
)
|
|
.await;
|
|
assert!(status.is_success(), "admin list wallets: {status} {body:?}");
|
|
let items = body["data"]["items"].as_array().unwrap();
|
|
assert!(!items.is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn admin_reversal_restores_balance() {
|
|
let tag = unique_tag();
|
|
let (mut app, _state, tok) = setup_admin().await;
|
|
let (_user_tok, user_doc_id) = seed_user_get_doc_id(&mut app, tag).await;
|
|
|
|
let (_, credit_body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/wallets/credit",
|
|
json!({
|
|
"user_id": user_doc_id,
|
|
"currency": "CNY",
|
|
"transaction_no": format!("TXREVPRE{tag}"),
|
|
"amount": 5000,
|
|
}),
|
|
&tok,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let tx_doc_id = credit_body["data"]["id"].as_str().unwrap();
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
&format!("/api/v1/admin/wallets/{tx_doc_id}/reversal"),
|
|
json!({"transaction_no": format!("TXREV{tag}")}),
|
|
&tok,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success(), "reversal: {status} {body:?}");
|
|
assert_eq!(body["data"]["entry_type"], "debit");
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn user_list_transactions() {
|
|
let tag = unique_tag();
|
|
let (mut app, _state, tok) = setup_admin().await;
|
|
let (user_tok, user_doc_id) = seed_user_get_doc_id(&mut app, tag).await;
|
|
|
|
let _ = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/wallets/credit",
|
|
json!({
|
|
"user_id": user_doc_id,
|
|
"currency": "CNY",
|
|
"transaction_no": format!("TXTXN{tag}"),
|
|
"amount": 1000,
|
|
}),
|
|
&tok,
|
|
),
|
|
)
|
|
.await;
|
|
|
|
let (status, body) = send(
|
|
&mut app,
|
|
get_auth(
|
|
"/api/v1/wallets/transactions?page=1&page_size=10",
|
|
&user_tok,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(status.is_success(), "list txns: {status} {body:?}");
|
|
let items = body["data"]["items"].as_array().unwrap();
|
|
assert!(!items.is_empty());
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn debit_insufficient_balance_rejected() {
|
|
let tag = unique_tag();
|
|
let (mut app, _state, tok) = setup_admin().await;
|
|
let (_user_tok, user_doc_id) = seed_user_get_doc_id(&mut app, tag).await;
|
|
|
|
let (status, _) = send(
|
|
&mut app,
|
|
post_json_auth(
|
|
"/api/v1/admin/wallets/debit",
|
|
json!({
|
|
"user_id": user_doc_id,
|
|
"currency": "CNY",
|
|
"transaction_no": format!("TXINSUFF{tag}"),
|
|
"amount": 99999,
|
|
}),
|
|
&tok,
|
|
),
|
|
)
|
|
.await;
|
|
assert!(!status.is_success(), "should reject insufficient balance");
|
|
}
|