using HoneyBox.Core.Services; using HoneyBox.Model.Data; using HoneyBox.Model.Entities; using HoneyBox.Model.Models.Payment; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.Extensions.Logging; using Moq; using Xunit; namespace HoneyBox.Tests.Integration; /// /// 支付服务集成测试 /// 测试余额扣减、积分扣减、哈尼券扣减和混合支付 /// Requirements: 3.1-6.7 /// public class PaymentServiceIntegrationTests { private readonly Mock> _mockLogger; public PaymentServiceIntegrationTests() { _mockLogger = new Mock>(); } private HoneyBoxDbContext CreateInMemoryDbContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .ConfigureWarnings(w => w.Ignore(InMemoryEventId.TransactionIgnoredWarning)) .Options; return new HoneyBoxDbContext(options); } private PaymentService CreatePaymentService(HoneyBoxDbContext dbContext) { return new PaymentService(dbContext, _mockLogger.Object); } private async Task CreateTestUserAsync( HoneyBoxDbContext dbContext, decimal money = 100, decimal integral = 1000, decimal money2 = 500) { var user = new User { Id = 1, OpenId = "test_openid_123456", Uid = "test_uid", Nickname = "测试用户", HeadImg = "avatar.jpg", Mobile = "13800138000", Money = money, Integral = integral, Money2 = money2, IsTest = 0, Status = 1, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; await dbContext.Users.AddAsync(user); await dbContext.SaveChangesAsync(); return user; } #region 余额扣减测试 (Requirements 3.1-3.5) /// /// 测试余额扣减 - 成功扣减 /// Requirements: 3.3, 3.4 /// [Fact] public async Task DeductBalanceAsync_SufficientBalance_DeductsSuccessfully() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, money: 100); var service = CreatePaymentService(dbContext); // Act var result = await service.DeductBalanceAsync(user.Id, 30, "测试扣减", "TST_ORDER_001"); // Assert Assert.True(result.Success); Assert.Equal(70, result.RemainingBalance); // Verify user balance updated var updatedUser = await dbContext.Users.FindAsync(user.Id); Assert.NotNull(updatedUser); Assert.Equal(70, updatedUser.Money); // Verify profit_money record created var profitRecord = await dbContext.ProfitMoneys.FirstOrDefaultAsync(p => p.UserId == user.Id); Assert.NotNull(profitRecord); Assert.Equal(-30, profitRecord.ChangeMoney); Assert.Equal(70, profitRecord.Money); } /// /// 测试余额扣减 - 余额不足 /// Requirements: 3.2 /// [Fact] public async Task DeductBalanceAsync_InsufficientBalance_ReturnsFalse() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, money: 20); var service = CreatePaymentService(dbContext); // Act var result = await service.DeductBalanceAsync(user.Id, 50, "测试扣减"); // Assert Assert.False(result.Success); Assert.Contains("不足", result.Message); // Verify user balance unchanged var updatedUser = await dbContext.Users.FindAsync(user.Id); Assert.NotNull(updatedUser); Assert.Equal(20, updatedUser.Money); } /// /// 测试余额扣减 - 用户不存在 /// Requirements: 3.1 /// [Fact] public async Task DeductBalanceAsync_UserNotFound_ReturnsFalse() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreatePaymentService(dbContext); // Act var result = await service.DeductBalanceAsync(999, 30, "测试扣减"); // Assert Assert.False(result.Success); Assert.Contains("用户不存在", result.Message); } /// /// 测试余额扣减 - 零金额 /// Requirements: 3.3 /// [Fact] public async Task DeductBalanceAsync_ZeroAmount_ReturnsSuccess() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, money: 100); var service = CreatePaymentService(dbContext); // Act var result = await service.DeductBalanceAsync(user.Id, 0, "测试扣减"); // Assert Assert.True(result.Success); Assert.Contains("无需扣减", result.Message); } /// /// 测试余额扣减 - 精确计算 /// Requirements: 3.3, 3.4 /// [Fact] public async Task DeductBalanceAsync_PreciseCalculation_CalculatesCorrectly() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, money: 100.50m); var service = CreatePaymentService(dbContext); // Act var result = await service.DeductBalanceAsync(user.Id, 30.25m, "测试扣减"); // Assert Assert.True(result.Success); Assert.Equal(70.25m, result.RemainingBalance); var updatedUser = await dbContext.Users.FindAsync(user.Id); Assert.NotNull(updatedUser); Assert.Equal(70.25m, updatedUser.Money); } #endregion #region 积分扣减测试 (Requirements 4.1-4.5) /// /// 测试积分扣减 - 成功扣减 /// Requirements: 4.3, 4.4 /// [Fact] public async Task DeductIntegralAsync_SufficientIntegral_DeductsSuccessfully() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, integral: 1000); var service = CreatePaymentService(dbContext); // Act var result = await service.DeductIntegralAsync(user.Id, 300, "测试扣减", "TST_ORDER_001"); // Assert Assert.True(result.Success); Assert.Equal(700, result.RemainingBalance); // Verify user integral updated var updatedUser = await dbContext.Users.FindAsync(user.Id); Assert.NotNull(updatedUser); Assert.Equal(700, updatedUser.Integral); // Verify profit_integral record created var profitRecord = await dbContext.ProfitIntegrals.FirstOrDefaultAsync(p => p.UserId == user.Id); Assert.NotNull(profitRecord); Assert.Equal(-300, profitRecord.ChangeMoney); } /// /// 测试积分扣减 - 积分不足 /// Requirements: 4.2 /// [Fact] public async Task DeductIntegralAsync_InsufficientIntegral_ReturnsFalse() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, integral: 200); var service = CreatePaymentService(dbContext); // Act var result = await service.DeductIntegralAsync(user.Id, 500, "测试扣减"); // Assert Assert.False(result.Success); Assert.Contains("不足", result.Message); } /// /// 测试积分扣减 - 用户不存在 /// Requirements: 4.1 /// [Fact] public async Task DeductIntegralAsync_UserNotFound_ReturnsFalse() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreatePaymentService(dbContext); // Act var result = await service.DeductIntegralAsync(999, 300, "测试扣减"); // Assert Assert.False(result.Success); Assert.Contains("用户不存在", result.Message); } #endregion #region 哈尼券扣减测试 (Requirements 5.1-5.4) /// /// 测试哈尼券扣减 - 成功扣减 /// Requirements: 5.3, 5.4 /// [Fact] public async Task DeductMoney2Async_SufficientMoney2_DeductsSuccessfully() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, money2: 500); var service = CreatePaymentService(dbContext); // Act var result = await service.DeductMoney2Async(user.Id, 200, "测试扣减", "TST_ORDER_001"); // Assert Assert.True(result.Success); Assert.Equal(300, result.RemainingBalance); // Verify user money2 updated var updatedUser = await dbContext.Users.FindAsync(user.Id); Assert.NotNull(updatedUser); Assert.Equal(300, updatedUser.Money2); // Verify profit_money2 record created var profitRecord = await dbContext.ProfitMoney2s.FirstOrDefaultAsync(p => p.UserId == user.Id); Assert.NotNull(profitRecord); Assert.Equal(-200, profitRecord.ChangeMoney); } /// /// 测试哈尼券扣减 - 哈尼券不足 /// Requirements: 5.2 /// [Fact] public async Task DeductMoney2Async_InsufficientMoney2_ReturnsFalse() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, money2: 100); var service = CreatePaymentService(dbContext); // Act var result = await service.DeductMoney2Async(user.Id, 300, "测试扣减"); // Assert Assert.False(result.Success); Assert.Contains("不足", result.Message); } /// /// 测试哈尼券扣减 - 用户不存在 /// Requirements: 5.1 /// [Fact] public async Task DeductMoney2Async_UserNotFound_ReturnsFalse() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreatePaymentService(dbContext); // Act var result = await service.DeductMoney2Async(999, 200, "测试扣减"); // Assert Assert.False(result.Success); Assert.Contains("用户不存在", result.Message); } #endregion #region 资产增加测试 /// /// 测试余额增加 - 成功增加 /// [Fact] public async Task AddBalanceAsync_ValidAmount_AddsSuccessfully() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, money: 100); var service = CreatePaymentService(dbContext); // Act var result = await service.AddBalanceAsync(user.Id, 50, "充值", "TST_RECHARGE_001"); // Assert Assert.True(result); var updatedUser = await dbContext.Users.FindAsync(user.Id); Assert.NotNull(updatedUser); Assert.Equal(150, updatedUser.Money); } /// /// 测试积分增加 - 成功增加 /// [Fact] public async Task AddIntegralAsync_ValidAmount_AddsSuccessfully() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, integral: 1000); var service = CreatePaymentService(dbContext); // Act var result = await service.AddIntegralAsync(user.Id, 500, "奖励", "TST_REWARD_001"); // Assert Assert.True(result); var updatedUser = await dbContext.Users.FindAsync(user.Id); Assert.NotNull(updatedUser); Assert.Equal(1500, updatedUser.Integral); } /// /// 测试哈尼券增加 - 成功增加 /// [Fact] public async Task AddMoney2Async_ValidAmount_AddsSuccessfully() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, money2: 500); var service = CreatePaymentService(dbContext); // Act var result = await service.AddMoney2Async(user.Id, 200, "推荐奖励", "TST_INVITE_001"); // Assert Assert.True(result); var updatedUser = await dbContext.Users.FindAsync(user.Id); Assert.NotNull(updatedUser); Assert.Equal(700, updatedUser.Money2); } #endregion #region 混合支付测试 (Requirements 6.1-6.7) /// /// 测试混合支付 - 余额+积分+哈尼券组合扣减 /// Requirements: 6.1-6.4 /// [Fact] public async Task MixedPayment_AllAssets_DeductsInOrder() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, money: 50, integral: 1000, money2: 500); var service = CreatePaymentService(dbContext); // Act - Simulate mixed payment order: balance -> integral -> money2 var balanceResult = await service.DeductBalanceAsync(user.Id, 30, "购买商品", "TST_ORDER_001"); var integralResult = await service.DeductIntegralAsync(user.Id, 500, "购买商品", "TST_ORDER_001"); var money2Result = await service.DeductMoney2Async(user.Id, 200, "购买商品", "TST_ORDER_001"); // Assert Assert.True(balanceResult.Success); Assert.True(integralResult.Success); Assert.True(money2Result.Success); var updatedUser = await dbContext.Users.FindAsync(user.Id); Assert.NotNull(updatedUser); Assert.Equal(20, updatedUser.Money); // 50 - 30 = 20 Assert.Equal(500, updatedUser.Integral); // 1000 - 500 = 500 Assert.Equal(300, updatedUser.Money2); // 500 - 200 = 300 } /// /// 测试混合支付 - 部分资产不足时继续其他扣减 /// Requirements: 6.5 /// [Fact] public async Task MixedPayment_PartialInsufficientBalance_ContinuesWithOthers() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, money: 10, integral: 1000, money2: 500); var service = CreatePaymentService(dbContext); // Act - Try to deduct more balance than available, then use integral var balanceResult = await service.DeductBalanceAsync(user.Id, 50, "购买商品", "TST_ORDER_001"); var integralResult = await service.DeductIntegralAsync(user.Id, 500, "购买商品", "TST_ORDER_001"); // Assert Assert.False(balanceResult.Success); // Balance insufficient Assert.True(integralResult.Success); // Integral deduction succeeds var updatedUser = await dbContext.Users.FindAsync(user.Id); Assert.NotNull(updatedUser); Assert.Equal(10, updatedUser.Money); // Unchanged Assert.Equal(500, updatedUser.Integral); // 1000 - 500 = 500 } /// /// 测试混合支付 - 全额资产支付(无需微信支付) /// Requirements: 6.6 /// [Fact] public async Task MixedPayment_FullAssetPayment_NoWechatNeeded() { // Arrange var dbContext = CreateInMemoryDbContext(); // Total order: 30元 // Balance: 10元, Integral: 1000 (=10元), Money2: 1000 (=10元) var user = await CreateTestUserAsync(dbContext, money: 10, integral: 1000, money2: 1000); var service = CreatePaymentService(dbContext); // Act - Deduct all assets to cover 30元 order var balanceResult = await service.DeductBalanceAsync(user.Id, 10, "购买商品", "TST_ORDER_001"); var integralResult = await service.DeductIntegralAsync(user.Id, 1000, "购买商品", "TST_ORDER_001"); var money2Result = await service.DeductMoney2Async(user.Id, 1000, "购买商品", "TST_ORDER_001"); // Assert - All deductions successful Assert.True(balanceResult.Success); Assert.True(integralResult.Success); Assert.True(money2Result.Success); var updatedUser = await dbContext.Users.FindAsync(user.Id); Assert.NotNull(updatedUser); Assert.Equal(0, updatedUser.Money); Assert.Equal(0, updatedUser.Integral); Assert.Equal(0, updatedUser.Money2); } #endregion #region 支付记录测试 (Requirements 8.1-8.3) /// /// 测试支付记录 - 创建记录 /// Requirements: 8.1, 8.2 /// [Fact] public async Task RecordPaymentAsync_ValidData_CreatesRecord() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext); var service = CreatePaymentService(dbContext); // Act var result = await service.RecordPaymentAsync( user.Id, "TST_ORDER_001", 30.00m, PaymentType.WechatPay, "微信支付"); // Assert Assert.True(result); var record = await dbContext.ProfitPays.FirstOrDefaultAsync(p => p.OrderNum == "TST_ORDER_001"); Assert.NotNull(record); Assert.Equal(user.Id, record.UserId); Assert.Equal(30.00m, record.ChangeMoney); Assert.Equal((byte)PaymentType.WechatPay, record.PayType); } /// /// 测试支付记录 - 查询记录 /// Requirements: 8.3 /// [Fact] public async Task GetPaymentRecordsAsync_HasRecords_ReturnsRecords() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext); var service = CreatePaymentService(dbContext); // Create some payment records await service.RecordPaymentAsync(user.Id, "TST_ORDER_001", 30.00m, PaymentType.WechatPay, "微信支付"); await service.RecordPaymentAsync(user.Id, "TST_ORDER_002", 20.00m, PaymentType.BalancePay, "余额支付"); // Act var records = await service.GetPaymentRecordsAsync(user.Id); // Assert Assert.Equal(2, records.Count); } /// /// 测试支付记录 - 检查订单是否有支付记录 /// Requirements: 8.1 /// [Fact] public async Task HasPaymentRecordAsync_RecordExists_ReturnsTrue() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext); var service = CreatePaymentService(dbContext); await service.RecordPaymentAsync(user.Id, "TST_ORDER_001", 30.00m, PaymentType.WechatPay, "微信支付"); // Act var hasRecord = await service.HasPaymentRecordAsync("TST_ORDER_001"); var noRecord = await service.HasPaymentRecordAsync("TST_ORDER_999"); // Assert Assert.True(hasRecord); Assert.False(noRecord); } /// /// 测试支付记录 - 根据订单号获取记录 /// Requirements: 8.3 /// [Fact] public async Task GetPaymentRecordByOrderNumAsync_RecordExists_ReturnsRecord() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext); var service = CreatePaymentService(dbContext); await service.RecordPaymentAsync(user.Id, "TST_ORDER_001", 30.00m, PaymentType.WechatPay, "微信支付"); // Act var record = await service.GetPaymentRecordByOrderNumAsync("TST_ORDER_001"); // Assert Assert.NotNull(record); Assert.Equal("TST_ORDER_001", record.OrderNum); Assert.Equal("30.00", record.ChangeMoney); Assert.Equal("微信支付", record.PayTypeText); } #endregion #region 余额验证测试 /// /// 测试余额验证 - 余额充足 /// [Fact] public async Task ValidateBalanceAsync_SufficientBalance_ReturnsTrue() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, money: 100); var service = CreatePaymentService(dbContext); // Act var result = await service.ValidateBalanceAsync(user.Id, 50); // Assert Assert.True(result); } /// /// 测试余额验证 - 余额不足 /// [Fact] public async Task ValidateBalanceAsync_InsufficientBalance_ReturnsFalse() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, money: 30); var service = CreatePaymentService(dbContext); // Act var result = await service.ValidateBalanceAsync(user.Id, 50); // Assert Assert.False(result); } /// /// 测试积分验证 - 积分充足 /// [Fact] public async Task ValidateIntegralAsync_SufficientIntegral_ReturnsTrue() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, integral: 1000); var service = CreatePaymentService(dbContext); // Act var result = await service.ValidateIntegralAsync(user.Id, 500); // Assert Assert.True(result); } /// /// 测试哈尼券验证 - 哈尼券充足 /// [Fact] public async Task ValidateMoney2Async_SufficientMoney2_ReturnsTrue() { // Arrange var dbContext = CreateInMemoryDbContext(); var user = await CreateTestUserAsync(dbContext, money2: 500); var service = CreatePaymentService(dbContext); // Act var result = await service.ValidateMoney2Async(user.Id, 300); // Assert Assert.True(result); } #endregion }