822 lines
26 KiB
C#
822 lines
26 KiB
C#
using HoneyBox.Core.Interfaces;
|
||
using HoneyBox.Core.Services;
|
||
using HoneyBox.Model.Data;
|
||
using HoneyBox.Model.Entities;
|
||
using HoneyBox.Model.Models.Order;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||
using Microsoft.Extensions.Logging;
|
||
using Moq;
|
||
using Xunit;
|
||
|
||
namespace HoneyBox.Tests.Integration;
|
||
|
||
/// <summary>
|
||
/// 订单服务集成测试
|
||
/// 测试完整的订单金额计算和订单创建流程
|
||
/// Requirements: 1.1-1.6, 2.1-2.6
|
||
/// </summary>
|
||
public class OrderServiceIntegrationTests
|
||
{
|
||
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 OrderService CreateOrderService(HoneyBoxDbContext dbContext)
|
||
{
|
||
var mockLogger = new Mock<ILogger<OrderService>>();
|
||
var mockLotteryEngine = new Mock<ILotteryEngine>();
|
||
return new OrderService(dbContext, mockLogger.Object, mockLotteryEngine.Object);
|
||
}
|
||
|
||
#region 测试数据准备
|
||
|
||
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",
|
||
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;
|
||
}
|
||
|
||
private async Task<Good> CreateTestGoodsAsync(HoneyBoxDbContext dbContext, byte type = 1, decimal price = 10, int stock = 10)
|
||
{
|
||
// 添加商品类型
|
||
await dbContext.GoodsTypes.AddAsync(new GoodsType
|
||
{
|
||
Value = type,
|
||
Name = "一番赏",
|
||
FlName = "一番赏",
|
||
CornerText = "一番赏",
|
||
PayWechat = 1,
|
||
PayBalance = 1,
|
||
PayCurrency = 1,
|
||
PayCurrency2 = 1,
|
||
PayCoupon = 1,
|
||
IsDeduction = 1
|
||
});
|
||
|
||
var goods = new Good
|
||
{
|
||
Id = 1,
|
||
Title = "测试商品",
|
||
Type = type,
|
||
Status = 1,
|
||
ShowIs = 0,
|
||
Price = price,
|
||
Stock = stock,
|
||
SaleStock = 0,
|
||
LockIs = 0,
|
||
IsShouZhe = 0,
|
||
QuanjuXiangou = 0,
|
||
DailyXiangou = 0,
|
||
ChoujiangXianzhi = 0,
|
||
ImgUrl = "img.jpg",
|
||
ImgUrlDetail = "detail.jpg"
|
||
};
|
||
await dbContext.Goods.AddAsync(goods);
|
||
await dbContext.SaveChangesAsync();
|
||
return goods;
|
||
}
|
||
|
||
private async Task CreateTestGoodsItemsAsync(HoneyBoxDbContext dbContext, int goodsId, int num = 1)
|
||
{
|
||
// 添加奖品等级
|
||
await dbContext.PrizeLevels.AddAsync(new PrizeLevel { Id = 10, Title = "A赏", Color = "#FF0000" });
|
||
await dbContext.PrizeLevels.AddAsync(new PrizeLevel { Id = 11, Title = "B赏", Color = "#00FF00" });
|
||
await dbContext.SaveChangesAsync();
|
||
|
||
// 添加奖品
|
||
var goodsItems = new List<GoodsItem>
|
||
{
|
||
new() { Id = 1, GoodsId = goodsId, Num = num, Title = "A赏奖品", Stock = 5, SurplusStock = 5, Price = 100, ScMoney = 50, ShangId = 10, GoodsListId = 0, ImgUrl = "a.jpg", Sort = 1 },
|
||
new() { Id = 2, GoodsId = goodsId, Num = num, Title = "B赏奖品", Stock = 10, SurplusStock = 10, Price = 50, ScMoney = 25, ShangId = 11, GoodsListId = 0, ImgUrl = "b.jpg", Sort = 2 }
|
||
};
|
||
await dbContext.GoodsItems.AddRangeAsync(goodsItems);
|
||
await dbContext.SaveChangesAsync();
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 订单金额计算测试 (Requirements 1.1-1.6)
|
||
|
||
/// <summary>
|
||
/// 测试基本订单金额计算 - 无抵扣
|
||
/// Requirements: 1.1
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task CalculateOrderMoney_BasicCalculation_ReturnsCorrectAmount()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext);
|
||
await CreateTestGoodsAsync(dbContext, price: 10);
|
||
await CreateTestGoodsItemsAsync(dbContext, 1);
|
||
|
||
var request = new OrderMoneyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 3,
|
||
UseMoneyIs = 2,
|
||
UseIntegralIs = 2,
|
||
UseMoney2Is = 2
|
||
};
|
||
|
||
// Act
|
||
var result = await service.CalculateOrderMoneyAsync(1, request);
|
||
|
||
// Assert
|
||
Assert.NotNull(result);
|
||
Assert.Equal("30.00", result.OrderTotal); // 10 * 3 = 30
|
||
Assert.NotNull(result.Goods);
|
||
Assert.Equal(1, result.Goods.Id);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试余额抵扣
|
||
/// Requirements: 1.2
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task CalculateOrderMoney_WithBalanceDeduction_DeductsCorrectly()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext, money: 20);
|
||
await CreateTestGoodsAsync(dbContext, price: 10);
|
||
await CreateTestGoodsItemsAsync(dbContext, 1);
|
||
|
||
var request = new OrderMoneyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 3,
|
||
UseMoneyIs = 1, // 使用余额
|
||
UseIntegralIs = 2,
|
||
UseMoney2Is = 2
|
||
};
|
||
|
||
// Act
|
||
var result = await service.CalculateOrderMoneyAsync(1, request);
|
||
|
||
// Assert
|
||
Assert.NotNull(result);
|
||
Assert.Equal("20.00", result.UseMoney); // 使用20元余额
|
||
Assert.Equal("10.00", result.Price); // 30 - 20 = 10
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试积分抵扣 (1:100比例)
|
||
/// Requirements: 1.3
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task CalculateOrderMoney_WithIntegralDeduction_DeductsCorrectly()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext, money: 0, integral: 2000); // 2000积分 = 20元
|
||
await CreateTestGoodsAsync(dbContext, price: 10);
|
||
await CreateTestGoodsItemsAsync(dbContext, 1);
|
||
|
||
var request = new OrderMoneyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 3,
|
||
UseMoneyIs = 2,
|
||
UseIntegralIs = 1, // 使用积分
|
||
UseMoney2Is = 2
|
||
};
|
||
|
||
// Act
|
||
var result = await service.CalculateOrderMoneyAsync(1, request);
|
||
|
||
// Assert
|
||
Assert.NotNull(result);
|
||
Assert.Equal("2000.00", result.UseIntegral); // 使用2000积分
|
||
Assert.Equal("10.00", result.Price); // 30 - 20 = 10
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试哈尼券抵扣 (1:100比例)
|
||
/// Requirements: 1.4
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task CalculateOrderMoney_WithMoney2Deduction_DeductsCorrectly()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext, money: 0, integral: 0, money2: 1500); // 1500哈尼券 = 15元
|
||
await CreateTestGoodsAsync(dbContext, price: 10);
|
||
await CreateTestGoodsItemsAsync(dbContext, 1);
|
||
|
||
var request = new OrderMoneyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 3,
|
||
UseMoneyIs = 2,
|
||
UseIntegralIs = 2,
|
||
UseMoney2Is = 1 // 使用哈尼券
|
||
};
|
||
|
||
// Act
|
||
var result = await service.CalculateOrderMoneyAsync(1, request);
|
||
|
||
// Assert
|
||
Assert.NotNull(result);
|
||
Assert.Equal(1500.00m, result.UseMoney2); // 使用1500哈尼券
|
||
Assert.Equal("15.00", result.Price); // 30 - 15 = 15
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试组合抵扣 - 余额+积分+哈尼券
|
||
/// Requirements: 1.2, 1.3, 1.4
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task CalculateOrderMoney_WithCombinedDeductions_DeductsCorrectly()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext, money: 10, integral: 1000, money2: 500);
|
||
await CreateTestGoodsAsync(dbContext, price: 10);
|
||
await CreateTestGoodsItemsAsync(dbContext, 1);
|
||
|
||
var request = new OrderMoneyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 3, // 总价30元
|
||
UseMoneyIs = 1,
|
||
UseIntegralIs = 1,
|
||
UseMoney2Is = 1
|
||
};
|
||
|
||
// Act
|
||
var result = await service.CalculateOrderMoneyAsync(1, request);
|
||
|
||
// Assert
|
||
Assert.NotNull(result);
|
||
// 余额10 + 积分10(1000/100) + 哈尼券5(500/100) = 25元抵扣
|
||
// 30 - 25 = 5元
|
||
Assert.Equal("10.00", result.UseMoney);
|
||
Assert.Equal("1000.00", result.UseIntegral);
|
||
Assert.Equal(500.00m, result.UseMoney2);
|
||
Assert.Equal("5.00", result.Price);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试商品不存在时抛出异常
|
||
/// Requirements: 1.1
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task CalculateOrderMoney_GoodsNotFound_ThrowsException()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext);
|
||
|
||
var request = new OrderMoneyRequest
|
||
{
|
||
GoodsId = 999,
|
||
Num = 1,
|
||
PrizeNum = 1
|
||
};
|
||
|
||
// Act & Assert
|
||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||
() => service.CalculateOrderMoneyAsync(1, request));
|
||
Assert.Equal("盒子不存在", ex.Message);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试商品已下架时抛出异常
|
||
/// Requirements: 1.1
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task CalculateOrderMoney_GoodsOffline_ThrowsException()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext);
|
||
|
||
// 创建下架商品
|
||
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 1, Name = "一番赏", FlName = "一番赏", CornerText = "一番赏" });
|
||
var goods = new Good
|
||
{
|
||
Id = 1,
|
||
Title = "下架商品",
|
||
Type = 1,
|
||
Status = 0, // 下架
|
||
Price = 10,
|
||
Stock = 10,
|
||
ImgUrl = "img.jpg",
|
||
ImgUrlDetail = "detail.jpg"
|
||
};
|
||
await dbContext.Goods.AddAsync(goods);
|
||
await dbContext.SaveChangesAsync();
|
||
|
||
var request = new OrderMoneyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 1
|
||
};
|
||
|
||
// Act & Assert
|
||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||
() => service.CalculateOrderMoneyAsync(1, request));
|
||
Assert.Equal("盒子已下架", ex.Message);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试用户不存在时抛出异常
|
||
/// Requirements: 1.1
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task CalculateOrderMoney_UserNotFound_ThrowsException()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestGoodsAsync(dbContext);
|
||
|
||
var request = new OrderMoneyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 1
|
||
};
|
||
|
||
// Act & Assert
|
||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||
() => service.CalculateOrderMoneyAsync(999, request));
|
||
Assert.Equal("用户不存在", ex.Message);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试全局限购验证
|
||
/// Requirements: 1.6
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task CalculateOrderMoney_ExceedsGlobalLimit_ThrowsException()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext);
|
||
|
||
// 创建有全局限购的商品
|
||
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 1, Name = "一番赏", FlName = "一番赏", CornerText = "一番赏", PayWechat = 1, PayBalance = 1, IsDeduction = 1 });
|
||
var goods = new Good
|
||
{
|
||
Id = 1,
|
||
Title = "限购商品",
|
||
Type = 1,
|
||
Status = 1,
|
||
Price = 10,
|
||
Stock = 10,
|
||
QuanjuXiangou = 2, // 全局限购2次
|
||
ImgUrl = "img.jpg",
|
||
ImgUrlDetail = "detail.jpg"
|
||
};
|
||
await dbContext.Goods.AddAsync(goods);
|
||
await CreateTestGoodsItemsAsync(dbContext, 1);
|
||
|
||
// 添加已购买记录
|
||
var orderItems = new List<OrderItem>
|
||
{
|
||
new() { Id = 1, OrderId = 1, UserId = 1, GoodsId = 1, ShangId = 10, ParentGoodsListId = 0, Status = 0, Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
|
||
new() { Id = 2, OrderId = 1, UserId = 1, GoodsId = 1, ShangId = 10, ParentGoodsListId = 0, Status = 0, Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() }
|
||
};
|
||
await dbContext.OrderItems.AddRangeAsync(orderItems);
|
||
await dbContext.SaveChangesAsync();
|
||
|
||
var request = new OrderMoneyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 1
|
||
};
|
||
|
||
// Act & Assert
|
||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||
() => service.CalculateOrderMoneyAsync(1, request));
|
||
Assert.Contains("限购", ex.Message);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 订单创建测试 (Requirements 2.1-2.6)
|
||
|
||
/// <summary>
|
||
/// 测试订单创建 - 余额全额支付
|
||
/// Requirements: 2.1, 2.2, 2.4
|
||
/// Note: This test requires a real database due to transaction usage
|
||
/// </summary>
|
||
[Fact(Skip = "InMemory database does not support transactions")]
|
||
public async Task CreateOrder_FullBalancePayment_CreatesOrderSuccessfully()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext, money: 100);
|
||
await CreateTestGoodsAsync(dbContext, price: 10);
|
||
await CreateTestGoodsItemsAsync(dbContext, 1);
|
||
|
||
var request = new OrderBuyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 3, // 总价30元
|
||
UseMoneyIs = 1, // 使用余额
|
||
UseIntegralIs = 2,
|
||
UseMoney2Is = 2
|
||
};
|
||
|
||
// Act
|
||
var result = await service.CreateOrderAsync(1, request);
|
||
|
||
// Assert
|
||
Assert.NotNull(result);
|
||
Assert.Equal(0, result.Status); // 已支付完成
|
||
Assert.NotEmpty(result.OrderNum);
|
||
Assert.Null(result.Res); // 无需微信支付
|
||
|
||
// 验证订单已创建
|
||
var order = await dbContext.Orders.FirstOrDefaultAsync(o => o.OrderNum == result.OrderNum);
|
||
Assert.NotNull(order);
|
||
Assert.Equal(1, order.Status); // 已支付
|
||
Assert.Equal(30, order.OrderTotal);
|
||
Assert.Equal(30, order.UseMoney);
|
||
Assert.Equal(0, order.Price);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试订单创建 - 需要微信支付
|
||
/// Requirements: 2.1, 2.2, 2.3
|
||
/// Note: This test requires a real database due to transaction usage
|
||
/// </summary>
|
||
[Fact(Skip = "InMemory database does not support transactions")]
|
||
public async Task CreateOrder_RequiresWechatPayment_ReturnsPayParams()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext, money: 10); // 余额不足
|
||
await CreateTestGoodsAsync(dbContext, price: 10);
|
||
await CreateTestGoodsItemsAsync(dbContext, 1);
|
||
|
||
var request = new OrderBuyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 3, // 总价30元
|
||
UseMoneyIs = 1, // 使用余额
|
||
UseIntegralIs = 2,
|
||
UseMoney2Is = 2
|
||
};
|
||
|
||
// Act
|
||
var result = await service.CreateOrderAsync(1, request);
|
||
|
||
// Assert
|
||
Assert.NotNull(result);
|
||
Assert.Equal(1, result.Status); // 需要支付
|
||
Assert.NotEmpty(result.OrderNum);
|
||
Assert.NotNull(result.Res); // 有微信支付参数
|
||
|
||
// 验证订单已创建但未支付
|
||
var order = await dbContext.Orders.FirstOrDefaultAsync(o => o.OrderNum == result.OrderNum);
|
||
Assert.NotNull(order);
|
||
Assert.Equal(0, order.Status); // 待支付
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试订单创建 - 未绑定手机号
|
||
/// Requirements: 2.6
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task CreateOrder_MobileNotBound_ThrowsException()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
// 创建未绑定手机号的用户
|
||
var user = new User
|
||
{
|
||
Id = 1,
|
||
OpenId = "test_openid",
|
||
Uid = "test_uid",
|
||
Nickname = "测试用户",
|
||
HeadImg = "avatar.jpg",
|
||
Mobile = null, // 未绑定手机号
|
||
Money = 100,
|
||
Status = 1,
|
||
CreatedAt = DateTime.Now,
|
||
UpdatedAt = DateTime.Now
|
||
};
|
||
await dbContext.Users.AddAsync(user);
|
||
await dbContext.SaveChangesAsync();
|
||
|
||
await CreateTestGoodsAsync(dbContext, price: 10);
|
||
await CreateTestGoodsItemsAsync(dbContext, 1);
|
||
|
||
var request = new OrderBuyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 1
|
||
};
|
||
|
||
// Act & Assert
|
||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||
() => service.CreateOrderAsync(1, request));
|
||
Assert.Equal("请先绑定手机号", ex.Message);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试订单创建 - 库存不足
|
||
/// Requirements: 2.5
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task CreateOrder_InsufficientStock_ThrowsException()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext, money: 100);
|
||
await CreateTestGoodsAsync(dbContext, price: 10);
|
||
|
||
// 创建库存不足的奖品
|
||
await dbContext.PrizeLevels.AddAsync(new PrizeLevel { Id = 10, Title = "A赏" });
|
||
await dbContext.GoodsItems.AddAsync(new GoodsItem
|
||
{
|
||
Id = 1,
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
Title = "A赏奖品",
|
||
Stock = 5,
|
||
SurplusStock = 2, // 只剩2个
|
||
ShangId = 10,
|
||
ImgUrl = "a.jpg"
|
||
});
|
||
await dbContext.SaveChangesAsync();
|
||
|
||
var request = new OrderBuyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 5 // 要买5个,但只有2个
|
||
};
|
||
|
||
// Act & Assert
|
||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||
() => service.CreateOrderAsync(1, request));
|
||
Assert.Contains("不足", ex.Message);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试订单创建 - 订单号唯一性
|
||
/// Requirements: 2.2
|
||
/// Note: This test requires a real database due to transaction usage
|
||
/// </summary>
|
||
[Fact(Skip = "InMemory database does not support transactions")]
|
||
public async Task CreateOrder_GeneratesUniqueOrderNum()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext, money: 100);
|
||
await CreateTestGoodsAsync(dbContext, price: 10);
|
||
await CreateTestGoodsItemsAsync(dbContext, 1);
|
||
|
||
var request = new OrderBuyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 1,
|
||
UseMoneyIs = 1
|
||
};
|
||
|
||
// Act - 创建两个订单
|
||
var result1 = await service.CreateOrderAsync(1, request);
|
||
var result2 = await service.CreateOrderAsync(1, request);
|
||
|
||
// Assert - 订单号应该不同
|
||
Assert.NotEqual(result1.OrderNum, result2.OrderNum);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试订单创建 - 用户资产扣减
|
||
/// Requirements: 2.4
|
||
/// Note: This test requires a real database due to transaction usage
|
||
/// </summary>
|
||
[Fact(Skip = "InMemory database does not support transactions")]
|
||
public async Task CreateOrder_DeductsUserAssets_Correctly()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext, money: 50, integral: 1000, money2: 500);
|
||
await CreateTestGoodsAsync(dbContext, price: 10);
|
||
await CreateTestGoodsItemsAsync(dbContext, 1);
|
||
|
||
var request = new OrderBuyRequest
|
||
{
|
||
GoodsId = 1,
|
||
Num = 1,
|
||
PrizeNum = 3, // 总价30元
|
||
UseMoneyIs = 1,
|
||
UseIntegralIs = 1,
|
||
UseMoney2Is = 1
|
||
};
|
||
|
||
// Act
|
||
var result = await service.CreateOrderAsync(1, request);
|
||
|
||
// Assert
|
||
Assert.Equal(0, result.Status); // 已支付完成
|
||
|
||
// 验证用户资产已扣减
|
||
var user = await dbContext.Users.FindAsync(1);
|
||
Assert.NotNull(user);
|
||
// 30元 = 余额30 或 余额+积分+哈尼券组合
|
||
// 具体扣减取决于实现逻辑
|
||
Assert.True(user.Money < 50 || user.Integral < 1000 || user.Money2 < 500);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 订单查询测试 (Requirements 6.1-6.4, 7.1-7.3)
|
||
|
||
/// <summary>
|
||
/// 测试订单列表查询 - 分页
|
||
/// Requirements: 6.1, 6.3, 6.4
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task GetOrderList_Pagination_ReturnsCorrectPage()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext);
|
||
|
||
// 创建15个订单
|
||
var orders = Enumerable.Range(1, 15).Select(i => new Order
|
||
{
|
||
Id = i,
|
||
UserId = 1,
|
||
OrderNum = $"MH_YFS{DateTime.Now:yyyyMMddHHmmss}{i:0000}",
|
||
OrderTotal = 10 * i,
|
||
OrderZheTotal = 10 * i,
|
||
Price = 0,
|
||
UseMoney = 10 * i,
|
||
GoodsId = 1,
|
||
GoodsTitle = $"商品{i}",
|
||
GoodsImgurl = $"img{i}.jpg",
|
||
PrizeNum = i,
|
||
Status = 1, // 已支付
|
||
Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() - i * 60,
|
||
CreatedAt = DateTime.Now,
|
||
UpdatedAt = DateTime.Now
|
||
}).ToList();
|
||
await dbContext.Orders.AddRangeAsync(orders);
|
||
await dbContext.SaveChangesAsync();
|
||
|
||
// Act - 第一页
|
||
var page1 = await service.GetOrderListAsync(1, new OrderListRequest { Page = 1, PageSize = 10 });
|
||
|
||
// Act - 第二页
|
||
var page2 = await service.GetOrderListAsync(1, new OrderListRequest { Page = 2, PageSize = 10 });
|
||
|
||
// Assert
|
||
Assert.Equal(15, page1.Total);
|
||
Assert.Equal(2, page1.LastPage);
|
||
Assert.Equal(10, page1.Data.Count);
|
||
Assert.Equal(5, page2.Data.Count);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试订单详情查询
|
||
/// Requirements: 7.1, 7.2
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task GetOrderDetail_ReturnsCompleteInfo()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext);
|
||
|
||
var orderNum = "MH_YFS202501021234561234";
|
||
var order = new Order
|
||
{
|
||
Id = 1,
|
||
UserId = 1,
|
||
OrderNum = orderNum,
|
||
OrderTotal = 30,
|
||
OrderZheTotal = 30,
|
||
Price = 0,
|
||
UseMoney = 30,
|
||
GoodsId = 1,
|
||
GoodsPrice = 10,
|
||
GoodsTitle = "测试商品",
|
||
GoodsImgurl = "img.jpg",
|
||
PrizeNum = 3,
|
||
Status = 1,
|
||
Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||
PayTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
|
||
CreatedAt = DateTime.Now,
|
||
UpdatedAt = DateTime.Now
|
||
};
|
||
await dbContext.Orders.AddAsync(order);
|
||
|
||
// 添加奖品记录
|
||
var orderItems = new List<OrderItem>
|
||
{
|
||
new() { Id = 1, OrderId = 1, UserId = 1, GoodsId = 1, ShangId = 10, GoodslistTitle = "A赏奖品", GoodslistImgurl = "a.jpg", GoodslistPrice = 100, GoodslistMoney = 50, Status = 0, LuckNo = 1, Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() },
|
||
new() { Id = 2, OrderId = 1, UserId = 1, GoodsId = 1, ShangId = 11, GoodslistTitle = "B赏奖品", GoodslistImgurl = "b.jpg", GoodslistPrice = 50, GoodslistMoney = 25, Status = 0, LuckNo = 2, Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() }
|
||
};
|
||
await dbContext.OrderItems.AddRangeAsync(orderItems);
|
||
await dbContext.SaveChangesAsync();
|
||
|
||
// Act
|
||
var result = await service.GetOrderDetailAsync(1, orderNum);
|
||
|
||
// Assert
|
||
Assert.NotNull(result);
|
||
Assert.NotNull(result.OrderInfo);
|
||
Assert.Equal(orderNum, result.OrderInfo.OrderNum);
|
||
Assert.Equal("30.00", result.OrderInfo.OrderTotal);
|
||
Assert.NotNull(result.PrizeList);
|
||
Assert.Equal(2, result.PrizeList.Count);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 测试订单详情查询 - 订单不存在
|
||
/// Requirements: 7.3
|
||
/// </summary>
|
||
[Fact]
|
||
public async Task GetOrderDetail_OrderNotFound_ThrowsException()
|
||
{
|
||
// Arrange
|
||
var dbContext = CreateInMemoryDbContext();
|
||
var service = CreateOrderService(dbContext);
|
||
|
||
await CreateTestUserAsync(dbContext);
|
||
|
||
// Act & Assert
|
||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||
() => service.GetOrderDetailAsync(1, "NONEXISTENT_ORDER"));
|
||
Assert.Equal("订单不存在", ex.Message);
|
||
}
|
||
|
||
#endregion
|
||
}
|