//! 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!("✅ 健康检查通过"); } }