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; /// /// 仓库服务集成测试 /// 测试奖品回收和发货流程 /// Requirements: 10.1-17.3 /// public class WarehouseServiceIntegrationTests { private HoneyBoxDbContext CreateInMemoryDbContext() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .ConfigureWarnings(w => w.Ignore(InMemoryEventId.TransactionIgnoredWarning)) .Options; return new HoneyBoxDbContext(options); } private WarehouseService CreateWarehouseService(HoneyBoxDbContext dbContext) { var mockLogger = new Mock>(); var mockLogisticsService = new Mock(); return new WarehouseService(dbContext, mockLogger.Object, mockLogisticsService.Object); } #region 测试数据准备 private async Task CreateTestUserAsync(HoneyBoxDbContext dbContext, decimal money2 = 0) { var user = new User { Id = 1, OpenId = "test_openid", Uid = "test_uid", Nickname = "测试用户", HeadImg = "avatar.jpg", Mobile = "13800138000", Money = 100, Integral = 1000, Money2 = money2, Status = 1, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; await dbContext.Users.AddAsync(user); await dbContext.SaveChangesAsync(); return user; } private async Task CreateTestGoodsAsync(HoneyBoxDbContext dbContext) { var goods = new Good { Id = 1, Title = "测试商品", Type = 1, Status = 1, Price = 10, Stock = 10, ImgUrl = "img.jpg", ImgUrlDetail = "detail.jpg" }; await dbContext.Goods.AddAsync(goods); await dbContext.SaveChangesAsync(); return goods; } private async Task> CreateTestOrderItemsAsync(HoneyBoxDbContext dbContext, int userId, int count = 3) { var items = new List(); for (int i = 1; i <= count; i++) { items.Add(new OrderItem { Id = i, OrderId = 1, UserId = userId, GoodsId = 1, ShangId = 10, GoodslistId = i, GoodslistTitle = $"奖品{i}", GoodslistImgurl = $"prize{i}.jpg", GoodslistPrice = 100, GoodslistMoney = 50, // 回收金额50 GoodslistType = 1, // 现货 Status = 0, // 待处理 InsuranceIs = 0, OrderType = 1, PrizeCode = $"PRIZE_{i}", Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(), CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }); } await dbContext.OrderItems.AddRangeAsync(items); await dbContext.SaveChangesAsync(); return items; } #endregion #region 仓库首页查询测试 (Requirements 10.1-10.3) /// /// 测试仓库首页查询 - 赏品类型 /// Requirements: 10.1, 10.2 /// [Fact] public async Task GetWarehouseIndex_PrizesType_ReturnsCorrectItems() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateWarehouseService(dbContext); await CreateTestUserAsync(dbContext); await CreateTestGoodsAsync(dbContext); await CreateTestOrderItemsAsync(dbContext, 1, 5); var request = new WarehouseIndexRequest { Type = 1, // 赏品 Page = 1 }; // Act var result = await service.GetWarehouseIndexAsync(1, request); // Assert Assert.NotNull(result); Assert.True(result.Total > 0); Assert.NotEmpty(result.Data); } /// /// 测试仓库首页查询 - 状态筛选 /// Requirements: 10.2 /// [Fact] public async Task GetWarehouseIndex_FiltersByStatus_ReturnsFilteredItems() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateWarehouseService(dbContext); await CreateTestUserAsync(dbContext); await CreateTestGoodsAsync(dbContext); // 创建不同状态的奖品 var items = new List { new() { Id = 1, OrderId = 1, UserId = 1, GoodsId = 1, ShangId = 10, GoodslistTitle = "待处理奖品", GoodslistMoney = 50, GoodslistType = 1, Status = 0, InsuranceIs = 0, OrderType = 1, PrizeCode = "P1", Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() }, new() { Id = 2, OrderId = 1, UserId = 1, GoodsId = 1, ShangId = 10, GoodslistTitle = "已回收奖品", GoodslistMoney = 50, GoodslistType = 1, Status = 1, InsuranceIs = 0, OrderType = 1, PrizeCode = "P2", Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() }, new() { Id = 3, OrderId = 1, UserId = 1, GoodsId = 1, ShangId = 10, GoodslistTitle = "已发货奖品", GoodslistMoney = 50, GoodslistType = 1, Status = 2, InsuranceIs = 0, OrderType = 1, PrizeCode = "P3", Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() } }; await dbContext.OrderItems.AddRangeAsync(items); await dbContext.SaveChangesAsync(); var request = new WarehouseIndexRequest { Type = 1, Page = 1 }; // Act var result = await service.GetWarehouseIndexAsync(1, request); // Assert Assert.NotNull(result); // 只返回status=0的待处理奖品 Assert.Equal(1, result.Total); } /// /// 测试仓库首页查询 - 无限赏类型 /// Requirements: 10.3 /// [Fact] public async Task GetWarehouseIndex_InfiniteType_ReturnsInfiniteItems() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateWarehouseService(dbContext); await CreateTestUserAsync(dbContext); await CreateTestGoodsAsync(dbContext); // 创建无限赏奖品 var items = new List { new() { Id = 1, OrderId = 1, UserId = 1, GoodsId = 1, ShangId = 34, GoodslistTitle = "无限赏奖品", GoodslistMoney = 50, GoodslistType = 1, Status = 0, InsuranceIs = 0, OrderType = 2, PrizeCode = "INF1", Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() } }; await dbContext.OrderItems.AddRangeAsync(items); await dbContext.SaveChangesAsync(); var request = new WarehouseIndexRequest { Type = 5, // 无限赏 Page = 1 }; // Act var result = await service.GetWarehouseIndexAsync(1, request); // Assert Assert.NotNull(result); Assert.Equal(1, result.Total); } #endregion #region 奖品回收测试 (Requirements 11.1-11.5) /// /// 测试奖品回收 - 成功回收 /// Requirements: 11.1, 11.2, 11.3, 11.4 /// Note: This test requires a real database due to transaction and ExecuteUpdate usage /// [Fact(Skip = "InMemory database does not support transactions and ExecuteUpdate")] public async Task RecoveryPrizes_Success_UpdatesStatusAndBalance() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateWarehouseService(dbContext); await CreateTestUserAsync(dbContext, money2: 0); await CreateTestGoodsAsync(dbContext); await CreateTestOrderItemsAsync(dbContext, 1, 2); // 构建回收信息 var recoveryInfo = System.Text.Json.JsonSerializer.Serialize(new[] { new { prize_code = "PRIZE_1", number = 1 }, new { prize_code = "PRIZE_2", number = 1 } }); var request = new RecoveryRequest { RecoveryInfo = recoveryInfo }; // Act var result = await service.RecoveryPrizesAsync(1, request); // Assert Assert.NotNull(result); Assert.Equal(2, result.Count); Assert.NotEmpty(result.RecoveryNum); // 验证奖品状态已更新 var items = await dbContext.OrderItems.Where(o => o.UserId == 1).ToListAsync(); Assert.All(items, item => Assert.Equal(1, item.Status)); // 已回收 // 验证用户余额已增加 (50 * 2 * 100 = 10000 哈尼券) var user = await dbContext.Users.FindAsync(1); Assert.NotNull(user); Assert.Equal(10000, user.Money2); } /// /// 测试奖品回收 - 空回收信息 /// Requirements: 11.5 /// [Fact] public async Task RecoveryPrizes_EmptyInfo_ThrowsException() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateWarehouseService(dbContext); await CreateTestUserAsync(dbContext); var request = new RecoveryRequest { RecoveryInfo = "" }; // Act & Assert var ex = await Assert.ThrowsAsync( () => service.RecoveryPrizesAsync(1, request)); Assert.Contains("选择", ex.Message); } /// /// 测试奖品回收 - 无效的回收信息格式 /// Requirements: 11.5 /// [Fact] public async Task RecoveryPrizes_InvalidFormat_ThrowsException() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateWarehouseService(dbContext); await CreateTestUserAsync(dbContext); var request = new RecoveryRequest { RecoveryInfo = "invalid json" }; // Act & Assert var ex = await Assert.ThrowsAsync( () => service.RecoveryPrizesAsync(1, request)); Assert.Contains("格式错误", ex.Message); } #endregion #region 奖品发货测试 (Requirements 12.1-12.5, 13.1-13.2) /// /// 测试奖品发货申请 - 成功 /// Requirements: 12.1, 12.2, 12.3, 12.4 /// Note: This test requires a real database due to transaction usage /// [Fact(Skip = "InMemory database does not support transactions")] public async Task SendPrizes_Success_CreatesDeliveryRecord() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateWarehouseService(dbContext); await CreateTestUserAsync(dbContext); await CreateTestGoodsAsync(dbContext); await CreateTestOrderItemsAsync(dbContext, 1, 2); // 构建发货信息JSON var recoveryInfo = System.Text.Json.JsonSerializer.Serialize(new[] { new { prize_code = "PRIZE_1", number = 1 }, new { prize_code = "PRIZE_2", number = 1 } }); var request = new SendRequest { Type = 1, RecoveryInfo = recoveryInfo, Name = "张三", Mobile = "13800138000", Address = "北京市朝阳区xxx街道xxx号" }; // Act var result = await service.SendPrizesAsync(1, request); // Assert Assert.NotNull(result); Assert.NotEmpty(result.OrderNo); // 验证发货记录已创建 var delivery = await dbContext.OrderItemsSends.FirstOrDefaultAsync(); Assert.NotNull(delivery); Assert.Equal("张三", delivery.Name); } /// /// 测试奖品发货申请 - 地址信息不完整 /// Requirements: 12.5 /// [Fact] public async Task SendPrizes_IncompleteAddress_ThrowsException() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateWarehouseService(dbContext); await CreateTestUserAsync(dbContext); await CreateTestGoodsAsync(dbContext); await CreateTestOrderItemsAsync(dbContext, 1); var recoveryInfo = System.Text.Json.JsonSerializer.Serialize(new[] { new { prize_code = "PRIZE_1", number = 1 } }); var request = new SendRequest { Type = 1, RecoveryInfo = recoveryInfo, Name = "", // 空姓名 Mobile = "13800138000", Address = "北京市" }; // Act & Assert var ex = await Assert.ThrowsAsync( () => service.SendPrizesAsync(1, request)); Assert.Contains("收货", ex.Message); } /// /// 测试奖品发货申请 - 空订单列表 /// Requirements: 12.5 /// [Fact] public async Task SendPrizes_EmptyOrderList_ThrowsException() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateWarehouseService(dbContext); await CreateTestUserAsync(dbContext); var request = new SendRequest { Type = 1, RecoveryInfo = "", Name = "张三", Mobile = "13800138000", Address = "北京市" }; // Act & Assert var ex = await Assert.ThrowsAsync( () => service.SendPrizesAsync(1, request)); Assert.Contains("选择", ex.Message); } #endregion #region 发货记录查询测试 (Requirements 14.1-14.3, 15.1-15.3) /// /// 测试发货记录查询 - 分页 /// Requirements: 14.1, 14.3 /// Note: This test requires a real database due to ExecuteUpdate usage in the service /// [Fact(Skip = "InMemory database does not support ExecuteUpdate")] public async Task GetSendRecords_Pagination_ReturnsCorrectPage() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateWarehouseService(dbContext); await CreateTestUserAsync(dbContext); // 创建15条发货记录 var records = Enumerable.Range(1, 15).Select(i => new OrderItemsSend { Id = i, UserId = 1, SendNum = $"SEND_{i:0000}", Name = $"收货人{i}", Mobile = "138****8000", Address = $"地址{i}", Status = 1, // 待发货 Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() - i * 60, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }).ToList(); await dbContext.OrderItemsSends.AddRangeAsync(records); await dbContext.SaveChangesAsync(); // Act var page1 = await service.GetSendRecordsAsync(1, 1, 1); var page2 = await service.GetSendRecordsAsync(1, 2, 1); // Assert Assert.Equal(15, page1.Total); Assert.Equal(10, page1.Data.Count); Assert.Equal(5, page2.Data.Count); } /// /// 测试发货记录详情查询 /// Requirements: 15.1, 15.2 /// [Fact] public async Task GetSendRecordDetail_ReturnsCompleteInfo() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateWarehouseService(dbContext); await CreateTestUserAsync(dbContext); // 创建发货记录 var sendRecord = new OrderItemsSend { Id = 1, UserId = 1, SendNum = "SEND_0001", Name = "张三", Mobile = "13800138000", Address = "北京市朝阳区", Status = 1, // 待发货 Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds(), CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; await dbContext.OrderItemsSends.AddAsync(sendRecord); // 创建关联的奖品 var items = new List { new() { Id = 1, OrderId = 1, UserId = 1, GoodsId = 1, ShangId = 10, SendNum = "SEND_0001", GoodslistTitle = "奖品1", GoodslistMoney = 50, Status = 2, Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() } }; await dbContext.OrderItems.AddRangeAsync(items); await dbContext.SaveChangesAsync(); // Act var result = await service.GetSendRecordDetailAsync(1, 1); // Assert Assert.NotNull(result); Assert.Equal("SEND_0001", result.SendNum); Assert.Equal("张三", result.Name); Assert.NotNull(result.Goods); } /// /// 测试发货记录详情查询 - 记录不存在 /// Requirements: 15.3 /// [Fact] public async Task GetSendRecordDetail_NotFound_ThrowsException() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateWarehouseService(dbContext); await CreateTestUserAsync(dbContext); // Act & Assert var ex = await Assert.ThrowsAsync( () => service.GetSendRecordDetailAsync(1, 999)); Assert.Contains("参数错误", ex.Message); } #endregion #region 回收记录查询测试 (Requirements 16.1-16.2) /// /// 测试回收记录查询 - 分页 /// Requirements: 16.1, 16.2 /// [Fact] public async Task GetRecoveryRecords_Pagination_ReturnsCorrectPage() { // Arrange var dbContext = CreateInMemoryDbContext(); var service = CreateWarehouseService(dbContext); await CreateTestUserAsync(dbContext); // 创建10条回收记录 var records = Enumerable.Range(1, 10).Select(i => new OrderItemsRecovery { Id = i, UserId = 1, RecoveryNum = $"REC_{i:0000}", Money = 50 * i, Count = i, Addtime = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds() - i * 60, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }).ToList(); await dbContext.OrderItemsRecoveries.AddRangeAsync(records); await dbContext.SaveChangesAsync(); // Act var result = await service.GetRecoveryRecordsAsync(1, 1); // Assert Assert.NotNull(result); Assert.NotNull(result.Data); Assert.True(result.Data.Count <= 10); Assert.True(result.LastPage >= 1); } #endregion }