//! 不变量校验测试 //! //! 测试 personal + labor + frozen = bank_balance 不变量 //! //! 测试场景: //! - 正常状态不变量验证 //! - 不变量违反检测 //! - 多操作后不变量验证 use chrono::Utc; use rust_decimal::Decimal; use rust_decimal_macros::dec; use rustjr::domain::ledger::entity::AccountBalance; use rustjr::domain::account::AccountType; use rustjr::error::AppError; /// 创建测试余额 fn create_balance(personal: Decimal, labor: Decimal, frozen: Decimal) -> AccountBalance { AccountBalance { id: 1, account_id: 1001, account_type: AccountType::Virtual, personal_balance: personal, labor_balance: labor, frozen_balance: frozen, bank_balance: personal + labor + frozen, transit_amount: Decimal::ZERO, system_balance: personal + labor + frozen, available_balance: personal + labor, frozen_amount: frozen, version: 1, updated_at: Utc::now(), } } /// 创建不一致的余额(用于测试不变量违反) fn create_invalid_balance( personal: Decimal, labor: Decimal, frozen: Decimal, bank: Decimal, ) -> AccountBalance { AccountBalance { id: 1, account_id: 1001, account_type: AccountType::Virtual, personal_balance: personal, labor_balance: labor, frozen_balance: frozen, bank_balance: bank, // 故意不一致 transit_amount: Decimal::ZERO, system_balance: bank, available_balance: personal + labor, frozen_amount: frozen, version: 1, updated_at: Utc::now(), } } // ==================== 正常不变量验证 ==================== #[test] fn test_invariant_holds_zero_balance() { let balance = create_balance(dec!(0.00), dec!(0.00), dec!(0.00)); assert!(balance.validate_invariant().is_ok()); } #[test] fn test_invariant_holds_personal_only() { let balance = create_balance(dec!(1000.00), dec!(0.00), dec!(0.00)); assert!(balance.validate_invariant().is_ok()); } #[test] fn test_invariant_holds_labor_only() { let balance = create_balance(dec!(0.00), dec!(500.00), dec!(0.00)); assert!(balance.validate_invariant().is_ok()); } #[test] fn test_invariant_holds_frozen_only() { let balance = create_balance(dec!(0.00), dec!(0.00), dec!(200.00)); assert!(balance.validate_invariant().is_ok()); } #[test] fn test_invariant_holds_mixed() { let balance = create_balance(dec!(1000.00), dec!(500.00), dec!(200.00)); assert!(balance.validate_invariant().is_ok()); } #[test] fn test_invariant_holds_large_amounts() { let balance = create_balance( dec!(999999999.99), dec!(888888888.88), dec!(777777777.77), ); assert!(balance.validate_invariant().is_ok()); } // ==================== 不变量违反检测 ==================== #[test] fn test_invariant_violation_bank_too_high() { let balance = create_invalid_balance( dec!(1000.00), dec!(500.00), dec!(0.00), dec!(2000.00), // 银行余额过高 ); let result = balance.validate_invariant(); assert!(result.is_err()); if let Err(AppError::InvariantViolation { account_id: _, expected, actual }) = result { assert_eq!(expected, dec!(2000.00)); assert_eq!(actual, dec!(1500.00)); } else { panic!("Expected InvariantViolation error"); } } #[test] fn test_invariant_violation_bank_too_low() { let balance = create_invalid_balance( dec!(1000.00), dec!(500.00), dec!(200.00), dec!(1000.00), // 银行余额过低 ); let result = balance.validate_invariant(); assert!(result.is_err()); if let Err(AppError::InvariantViolation { account_id: _, expected, actual }) = result { assert_eq!(expected, dec!(1000.00)); assert_eq!(actual, dec!(1700.00)); } else { panic!("Expected InvariantViolation error"); } } #[test] fn test_invariant_violation_small_difference() { let balance = create_invalid_balance( dec!(1000.00), dec!(500.00), dec!(0.00), dec!(1500.01), // 差 0.01 ); let result = balance.validate_invariant(); assert!(result.is_err()); } // ==================== 操作后不变量验证 ==================== #[test] fn test_invariant_after_add_personal() { let mut balance = create_balance(dec!(1000.00), dec!(500.00), dec!(0.00)); balance.add_personal(dec!(200.00)); assert!(balance.validate_invariant().is_ok()); balance.add_personal(dec!(300.00)); assert!(balance.validate_invariant().is_ok()); } #[test] fn test_invariant_after_add_labor() { let mut balance = create_balance(dec!(1000.00), dec!(500.00), dec!(0.00)); balance.add_labor(dec!(200.00)); assert!(balance.validate_invariant().is_ok()); balance.add_labor(dec!(300.00)); assert!(balance.validate_invariant().is_ok()); } #[test] fn test_invariant_after_freeze_unfreeze() { let mut balance = create_balance(dec!(1000.00), dec!(500.00), dec!(0.00)); // 冻结 balance.freeze(dec!(300.00)); assert!(balance.validate_invariant().is_ok()); // 部分解冻 balance.unfreeze(dec!(100.00)); assert!(balance.validate_invariant().is_ok()); // 全部解冻 balance.unfreeze(dec!(200.00)); assert!(balance.validate_invariant().is_ok()); } #[test] fn test_invariant_after_deduction() { let mut balance = create_balance(dec!(1000.00), dec!(500.00), dec!(0.00)); // 只扣个人 balance.deduct_with_priority(dec!(500.00)).unwrap(); assert!(balance.validate_invariant().is_ok()); // 扣个人+劳动 balance.deduct_with_priority(dec!(800.00)).unwrap(); assert!(balance.validate_invariant().is_ok()); } #[test] fn test_invariant_after_transit_operations() { let mut balance = create_balance(dec!(1000.00), dec!(500.00), dec!(0.00)); // 在途划转 balance.deduct_with_priority(dec!(300.00)).unwrap(); balance.add_transit(dec!(300.00)); assert!(balance.validate_invariant().is_ok()); // 在途结转 balance.settle_transit(dec!(300.00)).unwrap(); assert!(balance.validate_invariant().is_ok()); } #[test] fn test_invariant_after_transit_rollback() { let mut balance = create_balance(dec!(1000.00), dec!(500.00), dec!(0.00)); // 在途划转 balance.deduct_with_priority(dec!(300.00)).unwrap(); balance.add_transit(dec!(300.00)); assert!(balance.validate_invariant().is_ok()); // 在途回退 balance.rollback_transit(dec!(300.00)); assert!(balance.validate_invariant().is_ok()); // 余额应该恢复 assert_eq!(balance.personal_balance, dec!(1000.00)); assert_eq!(balance.transit_amount, dec!(0.00)); } // ==================== 复杂场景测试 ==================== #[test] fn test_invariant_complex_scenario() { let mut balance = create_balance(dec!(5000.00), dec!(3000.00), dec!(0.00)); // 1. 冻结部分 balance.freeze(dec!(1000.00)); assert!(balance.validate_invariant().is_ok()); // 2. 入账 balance.add_labor(dec!(500.00)); assert!(balance.validate_invariant().is_ok()); // 3. 扣款 balance.deduct_with_priority(dec!(2000.00)).unwrap(); assert!(balance.validate_invariant().is_ok()); // 4. 在途 balance.deduct_with_priority(dec!(1000.00)).unwrap(); balance.add_transit(dec!(1000.00)); assert!(balance.validate_invariant().is_ok()); // 5. 解冻部分 balance.unfreeze(dec!(500.00)); assert!(balance.validate_invariant().is_ok()); // 6. 在途结转 balance.settle_transit(dec!(1000.00)).unwrap(); assert!(balance.validate_invariant().is_ok()); } #[test] fn test_invariant_idempotent_operations() { let mut balance = create_balance(dec!(1000.00), dec!(500.00), dec!(0.00)); // 多次同样的操作 for _ in 0..10 { balance.add_personal(dec!(100.00)); balance.subtract_personal(dec!(100.00)).unwrap(); assert!(balance.validate_invariant().is_ok()); } // 最终余额应该不变 assert_eq!(balance.personal_balance, dec!(1000.00)); assert_eq!(balance.labor_balance, dec!(500.00)); } // ==================== 边界条件 ==================== #[test] fn test_invariant_with_zero_after_operations() { let mut balance = create_balance(dec!(100.00), dec!(0.00), dec!(0.00)); // 全部扣完 balance.deduct_with_priority(dec!(100.00)).unwrap(); assert!(balance.validate_invariant().is_ok()); assert_eq!(balance.personal_balance, dec!(0.00)); assert_eq!(balance.bank_balance, dec!(0.00)); } #[test] fn test_invariant_precision() { // 测试小数精度 let balance = create_balance(dec!(0.01), dec!(0.02), dec!(0.03)); assert!(balance.validate_invariant().is_ok()); assert_eq!(balance.bank_balance, dec!(0.06)); }