HaniBlindBox/server/HoneyBox/tests/HoneyBox.Tests/Integration/PaymentServiceIntegrationTests.cs
2026-01-04 01:47:02 +08:00

678 lines
22 KiB
C#

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;
/// <summary>
/// 支付服务集成测试
/// 测试余额扣减、积分扣减、哈尼券扣减和混合支付
/// Requirements: 3.1-6.7
/// </summary>
public class PaymentServiceIntegrationTests
{
private readonly Mock<ILogger<PaymentService>> _mockLogger;
public PaymentServiceIntegrationTests()
{
_mockLogger = new Mock<ILogger<PaymentService>>();
}
private HoneyBoxDbContext CreateInMemoryDbContext()
{
var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
.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<User> 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)
/// <summary>
/// 测试余额扣减 - 成功扣减
/// Requirements: 3.3, 3.4
/// </summary>
[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);
}
/// <summary>
/// 测试余额扣减 - 余额不足
/// Requirements: 3.2
/// </summary>
[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);
}
/// <summary>
/// 测试余额扣减 - 用户不存在
/// Requirements: 3.1
/// </summary>
[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);
}
/// <summary>
/// 测试余额扣减 - 零金额
/// Requirements: 3.3
/// </summary>
[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);
}
/// <summary>
/// 测试余额扣减 - 精确计算
/// Requirements: 3.3, 3.4
/// </summary>
[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)
/// <summary>
/// 测试积分扣减 - 成功扣减
/// Requirements: 4.3, 4.4
/// </summary>
[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);
}
/// <summary>
/// 测试积分扣减 - 积分不足
/// Requirements: 4.2
/// </summary>
[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);
}
/// <summary>
/// 测试积分扣减 - 用户不存在
/// Requirements: 4.1
/// </summary>
[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)
/// <summary>
/// 测试哈尼券扣减 - 成功扣减
/// Requirements: 5.3, 5.4
/// </summary>
[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);
}
/// <summary>
/// 测试哈尼券扣减 - 哈尼券不足
/// Requirements: 5.2
/// </summary>
[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);
}
/// <summary>
/// 测试哈尼券扣减 - 用户不存在
/// Requirements: 5.1
/// </summary>
[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
/// <summary>
/// 测试余额增加 - 成功增加
/// </summary>
[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);
}
/// <summary>
/// 测试积分增加 - 成功增加
/// </summary>
[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);
}
/// <summary>
/// 测试哈尼券增加 - 成功增加
/// </summary>
[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)
/// <summary>
/// 测试混合支付 - 余额+积分+哈尼券组合扣减
/// Requirements: 6.1-6.4
/// </summary>
[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
}
/// <summary>
/// 测试混合支付 - 部分资产不足时继续其他扣减
/// Requirements: 6.5
/// </summary>
[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
}
/// <summary>
/// 测试混合支付 - 全额资产支付(无需微信支付)
/// Requirements: 6.6
/// </summary>
[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)
/// <summary>
/// 测试支付记录 - 创建记录
/// Requirements: 8.1, 8.2
/// </summary>
[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);
}
/// <summary>
/// 测试支付记录 - 查询记录
/// Requirements: 8.3
/// </summary>
[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);
}
/// <summary>
/// 测试支付记录 - 检查订单是否有支付记录
/// Requirements: 8.1
/// </summary>
[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);
}
/// <summary>
/// 测试支付记录 - 根据订单号获取记录
/// Requirements: 8.3
/// </summary>
[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
/// <summary>
/// 测试余额验证 - 余额充足
/// </summary>
[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);
}
/// <summary>
/// 测试余额验证 - 余额不足
/// </summary>
[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);
}
/// <summary>
/// 测试积分验证 - 积分充足
/// </summary>
[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);
}
/// <summary>
/// 测试哈尼券验证 - 哈尼券充足
/// </summary>
[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
}