HaniBlindBox/server/HoneyBox/tests/HoneyBox.Tests/Integration/WarehouseServiceIntegrationTests.cs

587 lines
19 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: 10.1-17.3
/// </summary>
public class WarehouseServiceIntegrationTests
{
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 WarehouseService CreateWarehouseService(HoneyBoxDbContext dbContext)
{
var mockLogger = new Mock<ILogger<WarehouseService>>();
var mockLogisticsService = new Mock<ILogisticsService>();
var mockWechatPayService = new Mock<IWechatPayService>();
return new WarehouseService(dbContext, mockLogger.Object, mockLogisticsService.Object, mockWechatPayService.Object);
}
#region
private async Task<User> 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<Good> 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<List<OrderItem>> CreateTestOrderItemsAsync(HoneyBoxDbContext dbContext, int userId, int count = 3)
{
var items = new List<OrderItem>();
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)
/// <summary>
/// 测试仓库首页查询 - 赏品类型
/// Requirements: 10.1, 10.2
/// </summary>
[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);
}
/// <summary>
/// 测试仓库首页查询 - 状态筛选
/// Requirements: 10.2
/// </summary>
[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<OrderItem>
{
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);
}
/// <summary>
/// 测试仓库首页查询 - 无限赏类型
/// Requirements: 10.3
/// </summary>
[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<OrderItem>
{
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)
/// <summary>
/// 测试奖品回收 - 成功回收
/// Requirements: 11.1, 11.2, 11.3, 11.4
/// Note: This test requires a real database due to transaction and ExecuteUpdate usage
/// </summary>
[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);
}
/// <summary>
/// 测试奖品回收 - 空回收信息
/// Requirements: 11.5
/// </summary>
[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<InvalidOperationException>(
() => service.RecoveryPrizesAsync(1, request));
Assert.Contains("选择", ex.Message);
}
/// <summary>
/// 测试奖品回收 - 无效的回收信息格式
/// Requirements: 11.5
/// </summary>
[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<InvalidOperationException>(
() => service.RecoveryPrizesAsync(1, request));
Assert.Contains("格式错误", ex.Message);
}
#endregion
#region (Requirements 12.1-12.5, 13.1-13.2)
/// <summary>
/// 测试奖品发货申请 - 成功
/// Requirements: 12.1, 12.2, 12.3, 12.4
/// Note: This test requires a real database due to transaction usage
/// </summary>
[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);
}
/// <summary>
/// 测试奖品发货申请 - 地址信息不完整
/// Requirements: 12.5
/// </summary>
[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<InvalidOperationException>(
() => service.SendPrizesAsync(1, request));
Assert.Contains("收货", ex.Message);
}
/// <summary>
/// 测试奖品发货申请 - 空订单列表
/// Requirements: 12.5
/// </summary>
[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<InvalidOperationException>(
() => service.SendPrizesAsync(1, request));
Assert.Contains("选择", ex.Message);
}
#endregion
#region (Requirements 14.1-14.3, 15.1-15.3)
/// <summary>
/// 测试发货记录查询 - 分页
/// Requirements: 14.1, 14.3
/// Note: This test requires a real database due to ExecuteUpdate usage in the service
/// </summary>
[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);
}
/// <summary>
/// 测试发货记录详情查询
/// Requirements: 15.1, 15.2
/// </summary>
[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<OrderItem>
{
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);
}
/// <summary>
/// 测试发货记录详情查询 - 记录不存在
/// Requirements: 15.3
/// </summary>
[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<InvalidOperationException>(
() => service.GetSendRecordDetailAsync(1, 999));
Assert.Contains("参数错误", ex.Message);
}
#endregion
#region (Requirements 16.1-16.2)
/// <summary>
/// 测试回收记录查询 - 分页
/// Requirements: 16.1, 16.2
/// </summary>
[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
}