//! 综合测试套件 //! //! 整合所有测试模块的综合测试 #[cfg(test)] mod balance_model_tests { use chrono::Utc; use rust_decimal::Decimal; use rust_decimal_macros::dec; use rustjr::domain::ledger::AccountBalance; use rustjr::domain::account::AccountType; fn create_balance(personal: Decimal, labor: Decimal) -> AccountBalance { AccountBalance { id: 1, account_id: 1001, account_type: AccountType::Virtual, personal_balance: personal, labor_balance: labor, frozen_balance: Decimal::ZERO, bank_balance: personal + labor, transit_amount: Decimal::ZERO, system_balance: personal + labor, available_balance: personal + labor, frozen_amount: Decimal::ZERO, version: 1, updated_at: Utc::now(), } } #[test] fn test_balance_invariant() { let balance = create_balance(dec!(1000.00), dec!(500.00)); assert!(balance.validate_invariant().is_ok()); assert_eq!(balance.total_balance(), dec!(1500.00)); } #[test] fn test_priority_deduction() { let mut balance = create_balance(dec!(1000.00), dec!(500.00)); let result = balance.deduct_with_priority(dec!(1200.00)).unwrap(); assert_eq!(result.from_personal, dec!(1000.00)); assert_eq!(result.from_labor, dec!(200.00)); assert_eq!(balance.available_balance(), dec!(300.00)); assert!(balance.validate_invariant().is_ok()); } #[test] fn test_transit_flow() { let mut balance = create_balance(dec!(1000.00), dec!(500.00)); // 扣款并建立在途 // deduct 从 personal 扣 300,变成 700 balance.deduct_with_priority(dec!(300.00)).unwrap(); balance.add_transit(dec!(300.00)); assert_eq!(balance.transit_amount, dec!(300.00)); // available_balance = personal(700) + labor(500) = 1200 assert_eq!(balance.available_balance(), dec!(1200.00)); assert!(balance.validate_invariant().is_ok()); // 结转在途 balance.settle_transit(dec!(300.00)).unwrap(); assert_eq!(balance.transit_amount, dec!(0.00)); assert!(balance.validate_invariant().is_ok()); } } #[cfg(test)] mod bank_integration_tests { use rust_decimal_macros::dec; use rustjr::infrastructure::bank_integration::mock_bank::MockBankClient; use rustjr::infrastructure::bank_integration::{BankClient, BankTransferRequest}; #[tokio::test] async fn test_mock_bank_transfer() { let client = MockBankClient::new(); client.create_account("ACC001", "测试账户", dec!(10000.00)).unwrap(); client.create_account("ACC002", "对手账户", dec!(0.00)).unwrap(); let request = BankTransferRequest { from_account: "ACC001".to_string(), to_account: "ACC002".to_string(), to_account_name: "对手账户".to_string(), to_bank_code: "MOCK".to_string(), amount: dec!(3000.00), remark: Some("测试转账".to_string()), business_no: "TEST001".to_string(), }; let response = client.transfer(request).await.unwrap(); assert!(response.success); let balance1 = client.query_balance("ACC001").await.unwrap(); let balance2 = client.query_balance("ACC002").await.unwrap(); assert_eq!(balance1.balance, dec!(7000.00)); assert_eq!(balance2.balance, dec!(3000.00)); } #[tokio::test] async fn test_external_deposit() { let client = MockBankClient::new(); client.create_account("ACC001", "测试账户", dec!(10000.00)).unwrap(); client.simulate_external_deposit( "ACC001", "EXT001", "外部", dec!(2000.00), Some("外部入账".to_string()), ).unwrap(); let balance = client.query_balance("ACC001").await.unwrap(); assert_eq!(balance.balance, dec!(12000.00)); } } #[cfg(test)] mod e2e_workflow_tests { use chrono::Utc; use rust_decimal::Decimal; use rust_decimal_macros::dec; use rustjr::domain::ledger::AccountBalance; use rustjr::domain::account::AccountType; use rustjr::infrastructure::bank_integration::mock_bank::MockBankClient; use rustjr::infrastructure::bank_integration::{BankClient, BankTransferRequest}; fn create_balance(personal: Decimal, labor: Decimal) -> AccountBalance { AccountBalance { id: 1, account_id: 1001, account_type: AccountType::Virtual, personal_balance: personal, labor_balance: labor, frozen_balance: Decimal::ZERO, bank_balance: personal + labor, transit_amount: Decimal::ZERO, system_balance: personal + labor, available_balance: personal + labor, frozen_amount: Decimal::ZERO, version: 1, updated_at: Utc::now(), } } #[tokio::test] async fn test_complete_withdrawal_flow() { // 初始化 let mut balance = create_balance(dec!(5000.00), dec!(3000.00)); let client = MockBankClient::new(); client.create_account("BANK001", "银行账户", dec!(100000.00)).unwrap(); client.create_account("USER001", "用户账户", dec!(0.00)).unwrap(); let withdrawal_amount = dec!(2000.00); // 1. 扣款并建立在途 let deduction = balance.deduct_with_priority(withdrawal_amount).unwrap(); balance.add_transit(withdrawal_amount); assert!(balance.validate_invariant().is_ok()); // 2. 银行转账 let request = BankTransferRequest { from_account: "BANK001".to_string(), to_account: "USER001".to_string(), to_account_name: "用户账户".to_string(), to_bank_code: "MOCK".to_string(), amount: withdrawal_amount, remark: Some("提现".to_string()), business_no: "WD001".to_string(), }; let response = client.transfer(request).await.unwrap(); assert!(response.success); // 3. 结转在途 balance.settle_transit(withdrawal_amount).unwrap(); // 4. 验证结果 assert_eq!(balance.personal_balance, dec!(3000.00)); assert_eq!(balance.labor_balance, dec!(3000.00)); assert_eq!(balance.transit_amount, dec!(0.00)); assert!(balance.validate_invariant().is_ok()); let bank_balance = client.query_balance("USER001").await.unwrap(); assert_eq!(bank_balance.balance, withdrawal_amount); } #[tokio::test] async fn test_failure_recovery_flow() { let mut balance = create_balance(dec!(5000.00), dec!(3000.00)); let client = MockBankClient::new(); client.create_account("BANK001", "银行账户", dec!(100000.00)).unwrap(); let amount = dec!(2000.00); // 1. 扣款并建立在途 balance.deduct_with_priority(amount).unwrap(); balance.add_transit(amount); // 2. 模拟银行失败(使用余额不足来触发失败) // 先消耗银行账户余额,使第二笔转账失败 let consume_request = BankTransferRequest { from_account: "BANK001".to_string(), to_account: "USER001".to_string(), to_account_name: "用户账户".to_string(), to_bank_code: "MOCK".to_string(), amount: dec!(99000.00), // 消耗几乎全部余额,只剩 1000 remark: None, business_no: "CONSUME001".to_string(), }; client.transfer(consume_request).await.unwrap(); // 现在银行账户余额只有 1000,转账 2000 应该失败 let request = BankTransferRequest { from_account: "BANK001".to_string(), to_account: "USER001".to_string(), to_account_name: "用户账户".to_string(), to_bank_code: "MOCK".to_string(), amount, // 2000 remark: None, business_no: "FAIL001".to_string(), }; let response = client.transfer(request).await.unwrap(); assert!(!response.success); // 余额不足应该失败 // 3. 回退在途 balance.rollback_transit(amount); // 4. 验证余额恢复 // deduct_with_priority(2000) 从 personal 扣减:5000 -> 3000 // rollback_transit(2000) 恢复 personal:3000 -> 5000 assert_eq!(balance.personal_balance, dec!(5000.00)); assert_eq!(balance.labor_balance, dec!(3000.00)); assert_eq!(balance.transit_amount, dec!(0.00)); assert!(balance.validate_invariant().is_ok()); } }