rustjr-account-management/tests/api_integration_tests.rs
tangweijie 0473b6f717 test: 添加完整后端 API 集成测试 (32个端点)
- 添加 tests/api_integration_tests.rs: Rust 集成测试
- 添加 test_all_apis.sh: Shell 脚本测试所有 API

测试覆盖:
- Account API: 11个端点
- Transaction API: 5个端点
- Ledger API: 3个端点
- Reconciliation API: 8个端点
- Points API: 5个端点
2026-01-05 18:39:49 +08:00

728 lines
22 KiB
Rust

//! API 集成测试
//! 测试所有 32 个后端 API 端点
use axum::http::StatusCode;
use serde_json::{json, Value};
/// 测试辅助模块
mod test_helpers {
use super::*;
/// 测试基础 URL
pub const BASE_URL: &str = "http://localhost:8080";
/// 创建测试客户端
pub fn create_client() -> reqwest::Client {
reqwest::Client::new()
}
/// 断言响应成功
pub fn assert_success(status: StatusCode) {
assert!(status.is_success(), "Expected success status, got: {}", status);
}
}
// ==================== Account API 测试 (11个端点) ====================
#[cfg(test)]
mod account_api_tests {
use super::test_helpers::*;
use reqwest::Client;
use serde_json::json;
/// 测试创建实体账户
/// POST /api/v1/physical-accounts
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_create_physical_account() {
let client = create_client();
let url = format!("{}/api/v1/physical-accounts", BASE_URL);
let body = json!({
"account_no": format!("ACC_TEST_{}", chrono::Utc::now().timestamp()),
"account_name": "测试账户",
"bank_code": "ICBC",
"bank_name": "中国工商银行",
"consistency_mode": "eventual",
"outbound_control": "online_bank"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
assert!(res.status().is_success(), "创建账户失败: {}", res.status());
let json: serde_json::Value = res.json().await.unwrap();
assert!(json["data"]["id"].is_number(), "返回数据应包含 id");
println!("✅ 创建实体账户成功: {:?}", json);
}
/// 测试获取实体账户列表
/// GET /api/v1/physical-accounts
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_list_physical_accounts() {
let client = create_client();
let url = format!("{}/api/v1/physical-accounts?page=1&page_size=10", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
assert!(res.status().is_success(), "获取账户列表失败");
let json: serde_json::Value = res.json().await.unwrap();
assert!(json["data"]["data"].is_array(), "返回数据应为数组");
println!("✅ 获取账户列表成功, 总数: {}", json["data"]["total"]);
}
/// 测试获取实体账户详情
/// GET /api/v1/physical-accounts/:id
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_get_physical_account() {
let client = create_client();
let url = format!("{}/api/v1/physical-accounts/1", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
// 可能账户不存在,接受 404
if res.status().is_success() {
let json: serde_json::Value = res.json().await.unwrap();
println!("✅ 获取账户详情成功: {:?}", json["data"]["account_no"]);
} else {
println!("⚠️ 账户不存在 (预期行为)");
}
}
/// 测试冻结实体账户资金
/// POST /api/v1/physical-accounts/:id/freeze
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_freeze_physical_account() {
let client = create_client();
let url = format!("{}/api/v1/physical-accounts/1/freeze", BASE_URL);
let body = json!({
"amount": "100.00"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("冻结账户结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试解冻实体账户资金
/// POST /api/v1/physical-accounts/:id/unfreeze
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_unfreeze_physical_account() {
let client = create_client();
let url = format!("{}/api/v1/physical-accounts/1/unfreeze", BASE_URL);
let body = json!({
"amount": "50.00"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("解冻账户结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试创建虚拟子账户
/// POST /api/v1/sub-accounts
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_create_sub_account() {
let client = create_client();
let url = format!("{}/api/v1/sub-accounts", BASE_URL);
let body = json!({
"physical_account_id": 1,
"account_code": format!("SUB_TEST_{}", chrono::Utc::now().timestamp()),
"account_type": "settlement"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("创建子账户结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试获取子账户详情
/// GET /api/v1/sub-accounts/:id
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_get_sub_account() {
let client = create_client();
let url = format!("{}/api/v1/sub-accounts/1", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
println!("获取子账户结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试获取子账户余额
/// GET /api/v1/sub-accounts/:id/balance
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_get_sub_account_balance() {
let client = create_client();
let url = format!("{}/api/v1/sub-accounts/1/balance", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
println!("获取子账户余额结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试冻结子账户资金
/// POST /api/v1/sub-accounts/:id/freeze
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_freeze_sub_account() {
let client = create_client();
let url = format!("{}/api/v1/sub-accounts/1/freeze", BASE_URL);
let body = json!({
"amount": "10.00"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("冻结子账户结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试解冻子账户资金
/// POST /api/v1/sub-accounts/:id/unfreeze
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_unfreeze_sub_account() {
let client = create_client();
let url = format!("{}/api/v1/sub-accounts/1/unfreeze", BASE_URL);
let body = json!({
"amount": "5.00"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("解冻子账户结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试关闭子账户
/// POST /api/v1/sub-accounts/:id/close
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_close_sub_account() {
let client = create_client();
let url = format!("{}/api/v1/sub-accounts/1/close", BASE_URL);
let res = client.post(&url)
.send()
.await
.expect("请求失败");
println!("关闭子账户结果: {} - {:?}", res.status(), res.text().await);
}
}
// ==================== Transaction API 测试 (5个端点) ====================
#[cfg(test)]
mod transaction_api_tests {
use super::test_helpers::*;
use serde_json::json;
/// 测试发起转账
/// POST /api/v1/transactions/transfer
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_transfer() {
let client = create_client();
let url = format!("{}/api/v1/transactions/transfer", BASE_URL);
let body = json!({
"from_account_id": 1,
"to_account_id": 2,
"amount": "100.00",
"remark": "测试转账"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("转账结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试发起充值
/// POST /api/v1/transactions/deposit
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_deposit() {
let client = create_client();
let url = format!("{}/api/v1/transactions/deposit", BASE_URL);
let body = json!({
"account_id": 1,
"amount": "1000.00",
"source_key": format!("DEP_{}", chrono::Utc::now().timestamp()),
"remark": "测试充值"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("充值结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试发起提现
/// POST /api/v1/transactions/withdraw
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_withdraw() {
let client = create_client();
let url = format!("{}/api/v1/transactions/withdraw", BASE_URL);
let body = json!({
"account_id": 1,
"amount": "50.00",
"remark": "测试提现"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("提现结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试获取交易详情
/// GET /api/v1/transactions/:id
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_get_transaction() {
let client = create_client();
let url = format!("{}/api/v1/transactions/1", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
println!("获取交易详情结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试获取交易列表
/// GET /api/v1/transactions
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_list_transactions() {
let client = create_client();
let url = format!("{}/api/v1/transactions?page=1&page_size=10", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
assert!(res.status().is_success(), "获取交易列表失败");
println!("获取交易列表成功");
}
}
// ==================== Ledger API 测试 (3个端点) ====================
#[cfg(test)]
mod ledger_api_tests {
use super::test_helpers::*;
/// 测试获取会计科目列表
/// GET /api/v1/ledger/subjects
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_list_subjects() {
let client = create_client();
let url = format!("{}/api/v1/ledger/subjects", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
println!("获取会计科目结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试获取分录详情
/// GET /api/v1/ledger/entries/:id
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_get_entry() {
let client = create_client();
let url = format!("{}/api/v1/ledger/entries/1", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
println!("获取分录详情结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试获取账户分录列表
/// GET /api/v1/ledger/accounts/:id/entries
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_get_account_entries() {
let client = create_client();
let url = format!("{}/api/v1/ledger/accounts/1/entries?account_type=physical", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
println!("获取账户分录结果: {} - {:?}", res.status(), res.text().await);
}
}
// ==================== Reconciliation API 测试 (8个端点) ====================
#[cfg(test)]
mod reconciliation_api_tests {
use super::test_helpers::*;
use serde_json::json;
/// 测试执行对账
/// POST /api/v1/reconciliation/run
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_run_reconciliation() {
let client = create_client();
let url = format!("{}/api/v1/reconciliation/run", BASE_URL);
let body = json!({
"physical_account_id": 1,
"recon_date": "2024-01-01"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("执行对账结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试获取对账批次详情
/// GET /api/v1/reconciliation/batches/:id
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_get_batch() {
let client = create_client();
let url = format!("{}/api/v1/reconciliation/batches/1", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
println!("获取对账批次结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试获取对账明细
/// GET /api/v1/reconciliation/batches/:id/items
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_get_batch_items() {
let client = create_client();
let url = format!("{}/api/v1/reconciliation/batches/1/items", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
println!("获取对账明细结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试三账校验
/// GET /api/v1/reconciliation/three-account/:account_id
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_verify_three_accounts() {
let client = create_client();
let url = format!("{}/api/v1/reconciliation/three-account/1", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
if res.status().is_success() {
let json: serde_json::Value = res.json().await.unwrap();
println!("✅ 三账校验成功: is_balanced={}", json["data"]["is_balanced"]);
} else {
println!("三账校验失败: {}", res.status());
}
}
/// 测试创建手工补录
/// POST /api/v1/reconciliation/adjustments
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_create_adjustment() {
let client = create_client();
let url = format!("{}/api/v1/reconciliation/adjustments", BASE_URL);
let body = json!({
"adjustment_type": "add",
"account_id": 1,
"amount": "100.00",
"reason": "测试补录"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("创建补录结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试审批补录
/// POST /api/v1/reconciliation/adjustments/:id/approve
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_approve_adjustment() {
let client = create_client();
let url = format!("{}/api/v1/reconciliation/adjustments/1/approve", BASE_URL);
let body = json!({
"approver": "admin"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("审批补录结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试拒绝补录
/// POST /api/v1/reconciliation/adjustments/:id/reject
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_reject_adjustment() {
let client = create_client();
let url = format!("{}/api/v1/reconciliation/adjustments/1/reject", BASE_URL);
let body = json!({
"approver": "admin",
"reason": "测试拒绝"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("拒绝补录结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试获取待审批补录列表
/// GET /api/v1/reconciliation/adjustments/pending
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_list_pending_adjustments() {
let client = create_client();
let url = format!("{}/api/v1/reconciliation/adjustments/pending", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
println!("获取待审批补录结果: {} - {:?}", res.status(), res.text().await);
}
}
// ==================== Points API 测试 (5个端点) ====================
#[cfg(test)]
mod points_api_tests {
use super::test_helpers::*;
use serde_json::json;
/// 测试获取积分账户
/// GET /api/v1/points/accounts/:sub_account_id
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_get_points_accounts() {
let client = create_client();
let url = format!("{}/api/v1/points/accounts/1", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
println!("获取积分账户结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试获取积分
/// POST /api/v1/points/earn
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_earn_points() {
let client = create_client();
let url = format!("{}/api/v1/points/earn", BASE_URL);
let body = json!({
"points_account_id": 1,
"amount": "100.00",
"remark": "测试获取积分"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("获取积分结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试消费积分
/// POST /api/v1/points/spend
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_spend_points() {
let client = create_client();
let url = format!("{}/api/v1/points/spend", BASE_URL);
let body = json!({
"points_account_id": 1,
"amount": "50.00",
"remark": "测试消费积分"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("消费积分结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试转移积分
/// POST /api/v1/points/transfer
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_transfer_points() {
let client = create_client();
let url = format!("{}/api/v1/points/transfer", BASE_URL);
let body = json!({
"from_account_id": 1,
"to_account_id": 2,
"amount": "10.00",
"remark": "测试转移积分"
});
let res = client.post(&url)
.json(&body)
.send()
.await
.expect("请求失败");
println!("转移积分结果: {} - {:?}", res.status(), res.text().await);
}
/// 测试获取积分交易列表
/// GET /api/v1/points/transactions
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_list_points_transactions() {
let client = create_client();
let url = format!("{}/api/v1/points/transactions?page=1&page_size=10", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
println!("获取积分交易列表结果: {} - {:?}", res.status(), res.text().await);
}
}
// ==================== 健康检查测试 ====================
#[cfg(test)]
mod health_tests {
use super::test_helpers::*;
/// 测试健康检查端点
/// GET /health
#[tokio::test]
#[ignore = "需要运行后端服务"]
async fn test_health_check() {
let client = create_client();
let url = format!("{}/health", BASE_URL);
let res = client.get(&url)
.send()
.await
.expect("请求失败");
assert!(res.status().is_success(), "健康检查失败");
println!("✅ 健康检查通过");
}
}