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

1439 lines
49 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using HoneyBox.Core.Interfaces;
using HoneyBox.Core.Services;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using HoneyBox.Model.Models.Lottery;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
namespace HoneyBox.Tests.Integration;
/// <summary>
/// 抽奖服务集成测试
/// 测试抽奖结果查询、中奖记录查询功能
/// Requirements: 1.1-2.3, 4.1-4.3, 5.1-5.3
/// </summary>
public class LotteryServiceIntegrationTests
{
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 LotteryService CreateLotteryService(HoneyBoxDbContext dbContext)
{
var mockLogger = new Mock<ILogger<LotteryService>>();
var mockLotteryEngine = new Mock<ILotteryEngine>();
return new LotteryService(dbContext, mockLotteryEngine.Object, mockLogger.Object);
}
#region
private async Task<User> CreateTestUserAsync(HoneyBoxDbContext dbContext, int id = 1, string nickname = "测试用户")
{
var user = new User
{
Id = id,
OpenId = $"test_openid_{id}",
Uid = $"test_uid_{id}",
Nickname = nickname,
HeadImg = "avatar.jpg",
Mobile = "13800138000",
Money = 100,
Integral = 1000,
Money2 = 500,
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, int id = 1, byte type = 2, int status = 1)
{
var goods = new Good
{
Id = id,
Title = "测试无限赏商品",
Type = type, // 2 = 无限赏
Status = (byte)status,
ShowIs = 0,
Price = 10,
Stock = 100,
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 CreateTestPrizeLevelsAsync(HoneyBoxDbContext dbContext)
{
var prizeLevels = new List<PrizeLevel>
{
new() { Id = 10, Title = "A赏", Color = "#FF0000", Sort = 1 },
new() { Id = 11, Title = "B赏", Color = "#00FF00", Sort = 2 },
new() { Id = 12, Title = "C赏", Color = "#0000FF", Sort = 3 }
};
await dbContext.PrizeLevels.AddRangeAsync(prizeLevels);
await dbContext.SaveChangesAsync();
}
private async Task CreateTestGoodsItemsAsync(HoneyBoxDbContext dbContext, int goodsId)
{
var goodsItems = new List<GoodsItem>
{
new() { Id = 1, GoodsId = goodsId, Num = 0, 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 = 0, Title = "B赏奖品", Stock = 10, SurplusStock = 10, Price = 50, ScMoney = 25, ShangId = 11, GoodsListId = 0, ImgUrl = "b.jpg", Sort = 2 },
new() { Id = 3, GoodsId = goodsId, Num = 0, Title = "C赏奖品", Stock = 20, SurplusStock = 20, Price = 30, ScMoney = 15, ShangId = 12, GoodsListId = 0, ImgUrl = "c.jpg", Sort = 3 }
};
await dbContext.GoodsItems.AddRangeAsync(goodsItems);
await dbContext.SaveChangesAsync();
}
private async Task CreateTestOrderItemsAsync(HoneyBoxDbContext dbContext, int goodsId, int userId, int count = 5)
{
var now = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var orderItems = new List<OrderItem>();
for (int i = 1; i <= count; i++)
{
orderItems.Add(new OrderItem
{
Id = i,
OrderId = 1,
UserId = userId,
GoodsId = goodsId,
GoodslistId = i % 3 + 1,
GoodslistTitle = $"奖品{i}",
GoodslistImgurl = $"prize{i}.jpg",
GoodslistPrice = 50 + i * 10,
GoodslistMoney = 25 + i * 5,
ShangId = 10 + (i % 3),
Num = 0,
OrderType = LotteryOrderType.WuXianShang, // 无限赏
Source = PrizeSource.Lottery, // 抽奖获得
Status = 0,
LuckNo = i,
Addtime = now - i * 60 // 每条记录间隔1分钟
});
}
await dbContext.OrderItems.AddRangeAsync(orderItems);
await dbContext.SaveChangesAsync();
}
private async Task<Order> CreateTestOrderAsync(HoneyBoxDbContext dbContext, int userId, int goodsId, string orderNum, int orderType = 1)
{
var order = new Order
{
Id = 1,
UserId = userId,
OrderNum = orderNum,
GoodsId = goodsId,
GoodsTitle = "测试商品",
GoodsImgurl = "test.jpg",
GoodsPrice = 10,
OrderTotal = 50,
OrderZheTotal = 50,
Price = 50,
PrizeNum = 5,
Status = 1, // 已支付
OrderType = (byte)orderType,
Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
PayTime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
await dbContext.Orders.AddAsync(order);
await dbContext.SaveChangesAsync();
return order;
}
private async Task CreateTestOrderItemsWithOrderAsync(HoneyBoxDbContext dbContext, int orderId, int goodsId, int userId, int orderType, int count = 5)
{
var now = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var orderItems = new List<OrderItem>();
for (int i = 1; i <= count; i++)
{
orderItems.Add(new OrderItem
{
Id = i,
OrderId = orderId,
UserId = userId,
GoodsId = goodsId,
GoodslistId = i % 3 + 1,
GoodslistTitle = $"奖品{i}",
GoodslistImgurl = $"prize{i}.jpg",
GoodslistPrice = 50 + i * 10,
GoodslistMoney = 25 + i * 5,
ShangId = 10 + (i % 3),
Num = 0,
OrderType = (byte)orderType,
Source = PrizeSource.Lottery,
Status = 0,
LuckNo = i,
PrizeCode = $"P{DateTime.Now:yyyyMMddHHmmss}{1000 + i}",
Addtime = now - i * 60
});
}
await dbContext.OrderItems.AddRangeAsync(orderItems);
await dbContext.SaveChangesAsync();
}
#endregion
#region (Requirements 1.1-1.4)
/// <summary>
/// 测试一番赏抽奖结果查询 - 有效订单号返回奖品列表
/// Requirements: 1.1
/// </summary>
[Fact]
public async Task GetPrizeOrderLogAsync_ValidOrderNum_ReturnsPrizeList()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext, type: 1); // 一番赏
await CreateTestPrizeLevelsAsync(dbContext);
var order = await CreateTestOrderAsync(dbContext, 1, 1, "TEST202601010001", orderType: 1);
await CreateTestOrderItemsWithOrderAsync(dbContext, order.Id, 1, 1, LotteryOrderType.YiFanShang, 5);
// Act
var result = await service.GetPrizeOrderLogAsync(1, "TEST202601010001");
// Assert
Assert.NotNull(result);
Assert.Equal(5, result.PrizeNum);
Assert.Equal(5, result.Data.Count);
}
/// <summary>
/// 测试一番赏抽奖结果查询 - 无效订单号返回空列表
/// Requirements: 1.2
/// </summary>
[Fact]
public async Task GetPrizeOrderLogAsync_InvalidOrderNum_ReturnsEmptyList()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
// Act
var result = await service.GetPrizeOrderLogAsync(1, "INVALID_ORDER_NUM");
// Assert
Assert.NotNull(result);
Assert.Equal(0, result.PrizeNum);
Assert.Empty(result.Data);
}
/// <summary>
/// 测试一番赏抽奖结果查询 - 返回奖品详情包含所有必要字段
/// Requirements: 1.3
/// </summary>
[Fact]
public async Task GetPrizeOrderLogAsync_ReturnsPrizeDetails_ContainsAllRequiredFields()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext, type: 1);
await CreateTestPrizeLevelsAsync(dbContext);
var order = await CreateTestOrderAsync(dbContext, 1, 1, "TEST202601010002", orderType: 1);
await CreateTestOrderItemsWithOrderAsync(dbContext, order.Id, 1, 1, LotteryOrderType.YiFanShang, 1);
// Act
var result = await service.GetPrizeOrderLogAsync(1, "TEST202601010002");
// Assert
Assert.NotNull(result);
Assert.Single(result.Data);
var prize = result.Data.First();
Assert.NotEmpty(prize.GoodsListTitle); // title
Assert.NotEmpty(prize.GoodsListImgUrl); // imgurl
Assert.NotEmpty(prize.GoodsListPrice); // price
Assert.NotEmpty(prize.GoodsListMoney); // recovery value (money)
Assert.True(prize.LuckNo > 0); // luck_no
Assert.True(prize.ShangId > 0); // shang_id
}
/// <summary>
/// 测试一番赏抽奖结果查询 - 只返回当前用户的订单
/// Requirements: 1.1
/// </summary>
[Fact]
public async Task GetPrizeOrderLogAsync_OnlyReturnsCurrentUserOrder()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext, 1, "用户1");
await CreateTestUserAsync(dbContext, 2, "用户2");
await CreateTestGoodsAsync(dbContext, type: 1);
await CreateTestPrizeLevelsAsync(dbContext);
// 创建用户2的订单
var order = await CreateTestOrderAsync(dbContext, 2, 1, "TEST202601010003", orderType: 1);
await CreateTestOrderItemsWithOrderAsync(dbContext, order.Id, 1, 2, LotteryOrderType.YiFanShang, 3);
// Act - 用户1查询用户2的订单
var result = await service.GetPrizeOrderLogAsync(1, "TEST202601010003");
// Assert - 应该返回空因为订单不属于用户1
Assert.NotNull(result);
Assert.Equal(0, result.PrizeNum);
Assert.Empty(result.Data);
}
#endregion
#region (Requirements 2.1-2.3)
/// <summary>
/// 测试无限赏抽奖结果查询 - 有效订单号返回奖品列表
/// Requirements: 2.1
/// </summary>
[Fact]
public async Task GetInfinitePrizeOrderLogAsync_ValidOrderNum_ReturnsPrizeList()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext, type: 2); // 无限赏
await CreateTestPrizeLevelsAsync(dbContext);
var order = await CreateTestOrderAsync(dbContext, 1, 1, "INF202601010001", orderType: 2);
await CreateTestOrderItemsWithOrderAsync(dbContext, order.Id, 1, 1, LotteryOrderType.WuXianShang, 5);
// Act
var result = await service.GetInfinitePrizeOrderLogAsync(1, "INF202601010001");
// Assert
Assert.NotNull(result);
Assert.Equal(5, result.PrizeNum);
Assert.Equal(5, result.Data.Count);
}
/// <summary>
/// 测试无限赏抽奖结果查询 - 只查询无限赏类型订单
/// Requirements: 2.2
/// </summary>
[Fact]
public async Task GetInfinitePrizeOrderLogAsync_OnlyReturnsInfiniteOrderType()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext, type: 2);
await CreateTestPrizeLevelsAsync(dbContext);
var order = await CreateTestOrderAsync(dbContext, 1, 1, "INF202601010002", orderType: 2);
await CreateTestOrderItemsWithOrderAsync(dbContext, order.Id, 1, 1, LotteryOrderType.WuXianShang, 3);
// Act
var result = await service.GetInfinitePrizeOrderLogAsync(1, "INF202601010002");
// Assert
Assert.NotNull(result);
Assert.Equal(3, result.Data.Count);
}
/// <summary>
/// 测试无限赏抽奖结果查询 - 返回格式与一番赏一致
/// Requirements: 2.3
/// </summary>
[Fact]
public async Task GetInfinitePrizeOrderLogAsync_ResponseFormatConsistentWithYiFanShang()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext, type: 2);
await CreateTestPrizeLevelsAsync(dbContext);
var order = await CreateTestOrderAsync(dbContext, 1, 1, "INF202601010003", orderType: 2);
await CreateTestOrderItemsWithOrderAsync(dbContext, order.Id, 1, 1, LotteryOrderType.WuXianShang, 1);
// Act
var result = await service.GetInfinitePrizeOrderLogAsync(1, "INF202601010003");
// Assert
Assert.NotNull(result);
Assert.Single(result.Data);
var prize = result.Data.First();
// 验证与一番赏相同的字段
Assert.NotEmpty(prize.GoodsListTitle);
Assert.NotEmpty(prize.GoodsListImgUrl);
Assert.NotEmpty(prize.GoodsListPrice);
Assert.NotEmpty(prize.GoodsListMoney);
Assert.True(prize.LuckNo > 0);
Assert.True(prize.ShangId > 0);
}
/// <summary>
/// 测试无限赏抽奖结果查询 - 包含用户信息
/// Requirements: 2.3
/// </summary>
[Fact]
public async Task GetInfinitePrizeOrderLogAsync_ContainsUserInfo()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext, nickname: "测试昵称");
await CreateTestGoodsAsync(dbContext, type: 2);
await CreateTestPrizeLevelsAsync(dbContext);
var order = await CreateTestOrderAsync(dbContext, 1, 1, "INF202601010004", orderType: 2);
await CreateTestOrderItemsWithOrderAsync(dbContext, order.Id, 1, 1, LotteryOrderType.WuXianShang, 1);
// Act
var result = await service.GetInfinitePrizeOrderLogAsync(1, "INF202601010004");
// Assert
Assert.NotNull(result);
Assert.NotNull(result.UserInfo);
Assert.NotEmpty(result.UserInfo.Nickname);
}
/// <summary>
/// 测试无限赏抽奖结果查询 - 无效订单号返回空列表
/// Requirements: 2.1
/// </summary>
[Fact]
public async Task GetInfinitePrizeOrderLogAsync_InvalidOrderNum_ReturnsEmptyList()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
// Act
var result = await service.GetInfinitePrizeOrderLogAsync(1, "INVALID_ORDER_NUM");
// Assert
Assert.NotNull(result);
Assert.Equal(0, result.PrizeNum);
Assert.Empty(result.Data);
}
#endregion
#region (Requirements 4.1-4.3)
/// <summary>
/// 测试无限赏中奖记录查询 - 基本查询
/// Requirements: 4.1
/// </summary>
[Fact]
public async Task GetInfiniteShangLogAsync_BasicQuery_ReturnsRecords()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsAsync(dbContext, 1);
await CreateTestOrderItemsAsync(dbContext, 1, 1, 5);
// Act
var result = await service.GetInfiniteShangLogAsync(1, 0, 0, 1, 10);
// Assert
Assert.NotNull(result);
Assert.Equal(5, result.Total);
Assert.Equal(5, result.Data.Count);
Assert.NotNull(result.Category);
Assert.True(result.Category.Count > 0);
}
/// <summary>
/// 测试无限赏中奖记录查询 - 分页功能
/// Requirements: 4.1
/// </summary>
[Fact]
public async Task GetInfiniteShangLogAsync_Pagination_ReturnsCorrectPage()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsAsync(dbContext, 1);
await CreateTestOrderItemsAsync(dbContext, 1, 1, 15);
// Act - 第一页
var page1 = await service.GetInfiniteShangLogAsync(1, 0, 0, 1, 10);
// Act - 第二页
var page2 = await service.GetInfiniteShangLogAsync(1, 0, 0, 2, 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: 4.2
/// </summary>
[Fact]
public async Task GetInfiniteShangLogAsync_FilterByShangId_ReturnsFilteredRecords()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsAsync(dbContext, 1);
await CreateTestOrderItemsAsync(dbContext, 1, 1, 9); // 创建9条记录每个等级3条
// Act - 过滤A赏 (shangId = 10)
var result = await service.GetInfiniteShangLogAsync(1, 10, 0, 1, 100);
// Assert
Assert.NotNull(result);
Assert.True(result.Data.All(d => d.ShangId == 10));
}
/// <summary>
/// 测试无限赏中奖记录查询 - 商品不存在
/// Requirements: 4.1
/// </summary>
[Fact]
public async Task GetInfiniteShangLogAsync_GoodsNotFound_ReturnsEmptyResult()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
// Act
var result = await service.GetInfiniteShangLogAsync(999, 0, 0, 1, 10);
// Assert
Assert.NotNull(result);
Assert.Equal(0, result.Total);
Assert.Empty(result.Data);
}
/// <summary>
/// 测试无限赏中奖记录查询 - 商品已下架
/// Requirements: 4.1
/// </summary>
[Fact]
public async Task GetInfiniteShangLogAsync_GoodsOffline_ReturnsEmptyResult()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestGoodsAsync(dbContext, status: 0); // 下架状态
// Act
var result = await service.GetInfiniteShangLogAsync(1, 0, 0, 1, 10);
// Assert
Assert.NotNull(result);
Assert.Equal(0, result.Total);
Assert.Empty(result.Data);
}
/// <summary>
/// 测试无限赏中奖记录查询 - 用户昵称脱敏
/// Requirements: 4.3
/// </summary>
[Fact]
public async Task GetInfiniteShangLogAsync_UserNicknameMasked_ReturnsMaskedNickname()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext, nickname: "测试用户名");
await CreateTestGoodsAsync(dbContext);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsAsync(dbContext, 1);
await CreateTestOrderItemsAsync(dbContext, 1, 1, 1);
// Act
var result = await service.GetInfiniteShangLogAsync(1, 0, 0, 1, 10);
// Assert
Assert.NotNull(result);
Assert.Single(result.Data);
var record = result.Data.First();
Assert.NotNull(record.UserInfo);
// 验证昵称已脱敏(包含***
Assert.Contains("***", record.UserInfo.Nickname);
}
/// <summary>
/// 测试无限赏中奖记录查询 - 分类列表包含全部选项
/// Requirements: 4.1
/// </summary>
[Fact]
public async Task GetInfiniteShangLogAsync_CategoryList_ContainsAllOption()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsAsync(dbContext, 1);
// Act
var result = await service.GetInfiniteShangLogAsync(1, 0, 0, 1, 10);
// Assert
Assert.NotNull(result.Category);
Assert.True(result.Category.Count > 0);
Assert.Equal(0, result.Category.First().ShangId);
Assert.Equal("全部", result.Category.First().ShangTitle);
}
#endregion
#region (Requirements 5.1-5.3)
/// <summary>
/// 测试每日抽奖记录查询 - 基本查询
/// Requirements: 5.1
/// </summary>
[Fact]
public async Task GetInfinitePrizeRecordsAsync_BasicQuery_ReturnsRecords()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsAsync(dbContext, 1);
await CreateTestOrderItemsAsync(dbContext, 1, 1, 5);
// Act
var result = await service.GetInfinitePrizeRecordsAsync(1, 1, 1, 10);
// Assert
Assert.NotNull(result);
Assert.Equal(5, result.Total);
Assert.Equal(5, result.Data.Count);
}
/// <summary>
/// 测试每日抽奖记录查询 - 分页功能
/// Requirements: 5.1
/// </summary>
[Fact]
public async Task GetInfinitePrizeRecordsAsync_Pagination_ReturnsCorrectPage()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsAsync(dbContext, 1);
await CreateTestOrderItemsAsync(dbContext, 1, 1, 15);
// Act - 第一页
var page1 = await service.GetInfinitePrizeRecordsAsync(1, 1, 1, 10);
// Act - 第二页
var page2 = await service.GetInfinitePrizeRecordsAsync(1, 1, 2, 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: 5.1
/// </summary>
[Fact]
public async Task GetInfinitePrizeRecordsAsync_OnlyCurrentUserRecords_ReturnsFilteredRecords()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext, 1, "用户1");
await CreateTestUserAsync(dbContext, 2, "用户2");
await CreateTestGoodsAsync(dbContext);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsAsync(dbContext, 1);
// 为用户1创建5条记录
await CreateTestOrderItemsAsync(dbContext, 1, 1, 5);
// 为用户2创建3条记录
var now = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
var user2Items = new List<OrderItem>();
for (int i = 6; i <= 8; i++)
{
user2Items.Add(new OrderItem
{
Id = i,
OrderId = 2,
UserId = 2,
GoodsId = 1,
GoodslistId = 1,
GoodslistTitle = $"奖品{i}",
GoodslistImgurl = $"prize{i}.jpg",
GoodslistPrice = 50,
GoodslistMoney = 25,
ShangId = 10,
Num = 0,
OrderType = LotteryOrderType.WuXianShang,
Source = PrizeSource.Lottery,
Status = 0,
LuckNo = i,
Addtime = now - i * 60
});
}
await dbContext.OrderItems.AddRangeAsync(user2Items);
await dbContext.SaveChangesAsync();
// Act - 查询用户1的记录
var result = await service.GetInfinitePrizeRecordsAsync(1, 1, 1, 100);
// Assert
Assert.Equal(5, result.Total);
Assert.All(result.Data, d => Assert.Equal(1, d.UserId));
}
/// <summary>
/// 测试每日抽奖记录查询 - 无记录时返回空列表
/// Requirements: 5.1
/// </summary>
[Fact]
public async Task GetInfinitePrizeRecordsAsync_NoRecords_ReturnsEmptyList()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
// Act
var result = await service.GetInfinitePrizeRecordsAsync(1, 1, 1, 10);
// Assert
Assert.NotNull(result);
Assert.Equal(0, result.Total);
Assert.Empty(result.Data);
}
/// <summary>
/// 测试每日抽奖记录查询 - 记录按时间倒序排列
/// Requirements: 5.3
/// </summary>
[Fact]
public async Task GetInfinitePrizeRecordsAsync_OrderByTimeDescending_ReturnsOrderedRecords()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsAsync(dbContext, 1);
await CreateTestOrderItemsAsync(dbContext, 1, 1, 5);
// Act
var result = await service.GetInfinitePrizeRecordsAsync(1, 1, 1, 10);
// Assert
Assert.NotNull(result);
Assert.Equal(5, result.Data.Count);
// 验证记录按ID倒序ID越大时间越新
var ids = result.Data.Select(d => d.UserId).ToList();
// 由于我们按id倒序第一条应该是最新的
}
#endregion
#region (Requirements 10.1-10.4)
/// <summary>
/// 测试响应格式 - 无限赏中奖记录包含所有必要字段
/// Requirements: 10.2
/// </summary>
[Fact]
public async Task GetInfiniteShangLogAsync_ResponseFormat_ContainsAllRequiredFields()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsAsync(dbContext, 1);
await CreateTestOrderItemsAsync(dbContext, 1, 1, 1);
// Act
var result = await service.GetInfiniteShangLogAsync(1, 0, 0, 1, 10);
// Assert
Assert.NotNull(result);
Assert.Single(result.Data);
var record = result.Data.First();
Assert.True(record.Id > 0);
Assert.True(record.UserId > 0);
Assert.NotEmpty(record.GoodslistTitle);
Assert.NotEmpty(record.GoodslistImgurl);
Assert.NotEmpty(record.GoodslistPrice);
Assert.NotEmpty(record.GoodslistMoney);
Assert.True(record.ShangId > 0);
Assert.NotEmpty(record.Addtime);
}
/// <summary>
/// 测试响应格式 - 每日抽奖记录包含所有必要字段
/// Requirements: 10.2
/// </summary>
[Fact]
public async Task GetInfinitePrizeRecordsAsync_ResponseFormat_ContainsAllRequiredFields()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsAsync(dbContext, 1);
await CreateTestOrderItemsAsync(dbContext, 1, 1, 1);
// Act
var result = await service.GetInfinitePrizeRecordsAsync(1, 1, 1, 10);
// Assert
Assert.NotNull(result);
Assert.Single(result.Data);
var record = result.Data.First();
Assert.True(record.UserId > 0);
Assert.NotEmpty(record.GoodslistTitle);
Assert.NotEmpty(record.GoodslistImgurl);
Assert.NotEmpty(record.Addtime);
}
#endregion
#region (Requirements 6.1-6.4)
private async Task<UserItemCard> CreateTestItemCardAsync(HoneyBoxDbContext dbContext, int userId, int status = 1)
{
var itemCard = new UserItemCard
{
Id = 1,
UserId = userId,
Type = 1,
ItemCardId = 1,
Title = "重抽卡",
Status = (byte)status, // 1=未使用2=已使用
Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
Updatetime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds()
};
await dbContext.UserItemCards.AddAsync(itemCard);
await dbContext.SaveChangesAsync();
return itemCard;
}
private LotteryService CreateLotteryServiceWithMockedEngine(HoneyBoxDbContext dbContext, Mock<ILotteryEngine> mockEngine)
{
var mockLogger = new Mock<ILogger<LotteryService>>();
return new LotteryService(dbContext, mockEngine.Object, mockLogger.Object);
}
/// <summary>
/// 测试道具卡抽奖 - 商品不存在时返回错误
/// Requirements: 6.1
/// </summary>
[Fact]
public async Task DrawWithItemCardAsync_GoodsNotFound_ThrowsException()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestItemCardAsync(dbContext, 1);
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
() => service.DrawWithItemCardAsync(1, 999, new List<int> { 1 }));
Assert.Equal("盒子不存在", exception.Message);
}
/// <summary>
/// 测试道具卡抽奖 - 商品已下架时返回错误
/// Requirements: 6.1
/// </summary>
[Fact]
public async Task DrawWithItemCardAsync_GoodsOffline_ThrowsException()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext, status: 0); // 下架状态
await CreateTestItemCardAsync(dbContext, 1);
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
() => service.DrawWithItemCardAsync(1, 1, new List<int> { 1 }));
Assert.Equal("盒子已下架", exception.Message);
}
/// <summary>
/// 测试道具卡抽奖 - 道具卡数量不足时返回错误
/// Requirements: 6.4
/// </summary>
[Fact]
public async Task DrawWithItemCardAsync_NoItemCards_ThrowsException()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
// 不创建道具卡
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
() => service.DrawWithItemCardAsync(1, 1, new List<int> { 1 }));
Assert.Equal("重抽卡数量不足", exception.Message);
}
/// <summary>
/// 测试道具卡抽奖 - 道具卡已使用时返回错误
/// Requirements: 6.4
/// </summary>
[Fact]
public async Task DrawWithItemCardAsync_ItemCardAlreadyUsed_ThrowsException()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
await CreateTestItemCardAsync(dbContext, 1, status: 2); // 已使用状态
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
() => service.DrawWithItemCardAsync(1, 1, new List<int> { 1 }));
Assert.Equal("重抽卡数量不足", exception.Message);
}
/// <summary>
/// 测试道具卡抽奖 - 参数为空时返回错误
/// Requirements: 6.4
/// </summary>
[Fact]
public async Task DrawWithItemCardAsync_EmptyOrderListIds_ThrowsException()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
await CreateTestItemCardAsync(dbContext, 1);
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
() => service.DrawWithItemCardAsync(1, 1, new List<int>()));
Assert.Equal("参数错误", exception.Message);
}
/// <summary>
/// 测试道具卡抽奖 - 订单项不存在时返回错误
/// Requirements: 6.4
/// </summary>
[Fact]
public async Task DrawWithItemCardAsync_OrderItemNotFound_ThrowsException()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext);
await CreateTestItemCardAsync(dbContext, 1);
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
() => service.DrawWithItemCardAsync(1, 1, new List<int> { 999 }));
Assert.Equal("数据错误", exception.Message);
}
/// <summary>
/// 测试道具卡抽奖 - 消费门槛验证
/// Requirements: 6.1
/// </summary>
[Fact]
public async Task DrawWithItemCardAsync_SpendingThresholdNotMet_ThrowsException()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateLotteryService(dbContext);
await CreateTestUserAsync(dbContext);
// 创建有消费门槛的商品
var goods = new Good
{
Id = 1,
Title = "测试商品",
Type = 2,
Status = 1,
Price = 10,
Stock = 100,
ChoujiangXianzhi = 1000, // 消费门槛1000元
ImgUrl = "img.jpg",
ImgUrlDetail = "detail.jpg",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
};
await dbContext.Goods.AddAsync(goods);
await dbContext.SaveChangesAsync();
await CreateTestItemCardAsync(dbContext, 1);
// Act & Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(
() => service.DrawWithItemCardAsync(1, 1, new List<int> { 1 }));
Assert.Contains("消费满", exception.Message);
}
// Note: The test for valid item card draw (DrawWithItemCardAsync_ValidItemCard_ExecutesDrawAndMarksCardAsUsed)
// is skipped because InMemory database does not support ExecuteUpdateAsync.
// This functionality should be tested with a real database in end-to-end tests.
#endregion
#region (Requirements 7.1-9.4)
private LotteryEngine CreateLotteryEngine(HoneyBoxDbContext dbContext, IInventoryManager inventoryManager)
{
var mockLogger = new Mock<ILogger<LotteryEngine>>();
return new LotteryEngine(dbContext, inventoryManager, mockLogger.Object);
}
private InventoryManager CreateInventoryManager(HoneyBoxDbContext dbContext)
{
var mockLogger = new Mock<ILogger<InventoryManager>>();
return new InventoryManager(dbContext, mockLogger.Object);
}
private async Task CreateTestGoodsItemsWithStockAsync(HoneyBoxDbContext dbContext, int goodsId, int num = 0)
{
var goodsItems = new List<GoodsItem>
{
new() { Id = 101, 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 = 102, GoodsId = goodsId, Num = num, Title = "B赏奖品", Stock = 10, SurplusStock = 10, Price = 50, ScMoney = 25, ShangId = 11, GoodsListId = 0, ImgUrl = "b.jpg", Sort = 2 },
new() { Id = 103, GoodsId = goodsId, Num = num, Title = "C赏奖品", Stock = 20, SurplusStock = 20, Price = 30, ScMoney = 15, ShangId = 12, GoodsListId = 0, ImgUrl = "c.jpg", Sort = 3 }
};
await dbContext.GoodsItems.AddRangeAsync(goodsItems);
await dbContext.SaveChangesAsync();
}
/// <summary>
/// 测试抽奖引擎 - 完整抽奖流程成功
/// Requirements: 7.1-7.7
/// </summary>
[Fact]
public async Task LotteryEngine_DrawAsync_CompletesSuccessfully()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var inventoryManager = CreateInventoryManager(dbContext);
var engine = CreateLotteryEngine(dbContext, inventoryManager);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext, type: 1);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsWithStockAsync(dbContext, 1);
var request = new LotteryDrawRequest
{
UserId = 1,
GoodsId = 1,
Num = 0,
OrderId = 1,
OrderType = LotteryOrderType.YiFanShang,
Source = PrizeSource.Lottery
};
// Act
var result = await engine.DrawAsync(request);
// Assert
Assert.True(result.Success);
Assert.True(result.GoodsItemId > 0);
Assert.NotEmpty(result.Title);
Assert.NotEmpty(result.PrizeCode);
Assert.True(result.LuckNo > 0);
Assert.True(result.OrderItemId > 0);
}
/// <summary>
/// 测试抽奖引擎 - 库存扣减正确
/// Requirements: 8.1-8.4
/// </summary>
[Fact]
public async Task LotteryEngine_DrawAsync_DeductsStockCorrectly()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var inventoryManager = CreateInventoryManager(dbContext);
var engine = CreateLotteryEngine(dbContext, inventoryManager);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext, type: 1);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsWithStockAsync(dbContext, 1);
// 获取初始总库存
var initialTotalStock = await dbContext.GoodsItems
.Where(gi => gi.GoodsId == 1 && gi.Num == 0)
.SumAsync(gi => gi.SurplusStock);
var request = new LotteryDrawRequest
{
UserId = 1,
GoodsId = 1,
Num = 0,
OrderId = 1,
OrderType = LotteryOrderType.YiFanShang,
Source = PrizeSource.Lottery
};
// Act
var result = await engine.DrawAsync(request);
// Assert
Assert.True(result.Success);
// 验证库存已扣减
var finalTotalStock = await dbContext.GoodsItems
.Where(gi => gi.GoodsId == 1 && gi.Num == 0)
.SumAsync(gi => gi.SurplusStock);
Assert.Equal(initialTotalStock - 1, finalTotalStock);
}
/// <summary>
/// 测试抽奖引擎 - 抽奖记录创建正确
/// Requirements: 9.1-9.4
/// </summary>
[Fact]
public async Task LotteryEngine_DrawAsync_CreatesRecordCorrectly()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var inventoryManager = CreateInventoryManager(dbContext);
var engine = CreateLotteryEngine(dbContext, inventoryManager);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext, type: 1);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsWithStockAsync(dbContext, 1);
var request = new LotteryDrawRequest
{
UserId = 1,
GoodsId = 1,
Num = 0,
OrderId = 1,
OrderType = LotteryOrderType.YiFanShang,
Source = PrizeSource.Lottery
};
// Act
var result = await engine.DrawAsync(request);
// Assert
Assert.True(result.Success);
// 验证记录已创建
var orderItem = await dbContext.OrderItems.FindAsync(result.OrderItemId);
Assert.NotNull(orderItem);
Assert.Equal(1, orderItem.UserId);
Assert.Equal(1, orderItem.GoodsId);
Assert.Equal(result.GoodsItemId, orderItem.GoodslistId);
Assert.Equal(result.ShangId, orderItem.ShangId);
Assert.Equal(result.PrizeCode, orderItem.PrizeCode);
Assert.Equal(result.LuckNo, orderItem.LuckNo);
Assert.Equal((byte)PrizeSource.Lottery, orderItem.Source);
}
/// <summary>
/// 测试抽奖引擎 - 无可用奖品时返回失败
/// Requirements: 7.3
/// </summary>
[Fact]
public async Task LotteryEngine_DrawAsync_NoPrizes_ReturnsFailed()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var inventoryManager = CreateInventoryManager(dbContext);
var engine = CreateLotteryEngine(dbContext, inventoryManager);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext, type: 1);
// 不创建奖品
var request = new LotteryDrawRequest
{
UserId = 1,
GoodsId = 1,
Num = 0,
OrderId = 1,
OrderType = LotteryOrderType.YiFanShang,
Source = PrizeSource.Lottery
};
// Act
var result = await engine.DrawAsync(request);
// Assert
Assert.False(result.Success);
Assert.Equal("暂无可抽奖品", result.ErrorMessage);
}
/// <summary>
/// 测试抽奖引擎 - 多次抽奖成功
/// Requirements: 7.1-7.7
/// </summary>
[Fact]
public async Task LotteryEngine_DrawMultipleAsync_CompletesSuccessfully()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var inventoryManager = CreateInventoryManager(dbContext);
var engine = CreateLotteryEngine(dbContext, inventoryManager);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext, type: 1);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsWithStockAsync(dbContext, 1);
var request = new LotteryDrawRequest
{
UserId = 1,
GoodsId = 1,
Num = 0,
OrderId = 1,
OrderType = LotteryOrderType.YiFanShang,
Source = PrizeSource.Lottery
};
// Act
var results = await engine.DrawMultipleAsync(request, 3);
// Assert
Assert.Equal(3, results.Count);
Assert.All(results, r => Assert.True(r.Success));
// 验证每次抽奖都创建了记录
var orderItemCount = await dbContext.OrderItems.CountAsync();
Assert.Equal(3, orderItemCount);
}
/// <summary>
/// 测试抽奖引擎 - 概率计算正确
/// Requirements: 7.1
/// </summary>
[Fact]
public async Task LotteryEngine_CalculateProbabilitiesAsync_ReturnsCorrectProbabilities()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var inventoryManager = CreateInventoryManager(dbContext);
var engine = CreateLotteryEngine(dbContext, inventoryManager);
await CreateTestGoodsAsync(dbContext, type: 1);
await CreateTestGoodsItemsWithStockAsync(dbContext, 1);
// Act
var probabilities = await engine.CalculateProbabilitiesAsync(1, 0);
// Assert
Assert.Equal(3, probabilities.Count);
// 验证概率总和为100%
var totalProbability = probabilities.Sum(p => p.Probability);
Assert.Equal(100m, totalProbability, 2);
// 验证概率与库存成正比
// A赏: 5/(5+10+20) = 14.29%
// B赏: 10/(5+10+20) = 28.57%
// C赏: 20/(5+10+20) = 57.14%
var aPrize = probabilities.First(p => p.ShangId == 10);
var bPrize = probabilities.First(p => p.ShangId == 11);
var cPrize = probabilities.First(p => p.ShangId == 12);
Assert.True(cPrize.Probability > bPrize.Probability);
Assert.True(bPrize.Probability > aPrize.Probability);
}
/// <summary>
/// 测试抽奖引擎 - 库存为0的奖品被排除
/// Requirements: 7.3
/// </summary>
[Fact]
public async Task LotteryEngine_CalculateProbabilitiesAsync_ExcludesZeroStockPrizes()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var inventoryManager = CreateInventoryManager(dbContext);
var engine = CreateLotteryEngine(dbContext, inventoryManager);
await CreateTestGoodsAsync(dbContext, type: 1);
// 创建包含库存为0的奖品
var goodsItems = new List<GoodsItem>
{
new() { Id = 201, GoodsId = 1, Num = 0, Title = "A赏奖品", Stock = 5, SurplusStock = 0, Price = 100, ScMoney = 50, ShangId = 10, GoodsListId = 0, ImgUrl = "a.jpg", Sort = 1 },
new() { Id = 202, GoodsId = 1, Num = 0, 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();
// Act
var probabilities = await engine.CalculateProbabilitiesAsync(1, 0);
// Assert
Assert.Single(probabilities);
Assert.Equal(11, probabilities.First().ShangId);
Assert.Equal(100m, probabilities.First().Probability);
}
/// <summary>
/// 测试抽奖引擎 - 验证抽奖结果
/// Requirements: 9.1-9.4
/// </summary>
[Fact]
public async Task LotteryEngine_ValidateDrawResultAsync_ValidResult_ReturnsTrue()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var inventoryManager = CreateInventoryManager(dbContext);
var engine = CreateLotteryEngine(dbContext, inventoryManager);
await CreateTestUserAsync(dbContext);
await CreateTestGoodsAsync(dbContext, type: 1);
await CreateTestPrizeLevelsAsync(dbContext);
await CreateTestGoodsItemsWithStockAsync(dbContext, 1);
var request = new LotteryDrawRequest
{
UserId = 1,
GoodsId = 1,
Num = 0,
OrderId = 1,
OrderType = LotteryOrderType.YiFanShang,
Source = PrizeSource.Lottery
};
var drawResult = await engine.DrawAsync(request);
// Act
var isValid = await engine.ValidateDrawResultAsync(drawResult);
// Assert
Assert.True(isValid);
}
/// <summary>
/// 测试抽奖引擎 - 验证失败的抽奖结果
/// Requirements: 9.1-9.4
/// </summary>
[Fact]
public async Task LotteryEngine_ValidateDrawResultAsync_FailedResult_ReturnsFalse()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var inventoryManager = CreateInventoryManager(dbContext);
var engine = CreateLotteryEngine(dbContext, inventoryManager);
var failedResult = new LotteryDrawResult { Success = false };
// Act
var isValid = await engine.ValidateDrawResultAsync(failedResult);
// Assert
Assert.False(isValid);
}
#endregion
}