using HoneyBox.Core.Interfaces;
using HoneyBox.Core.Services;
using HoneyBox.Model.Data;
using HoneyBox.Model.Entities;
using HoneyBox.Model.Models.Goods;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Moq;
using Xunit;
namespace HoneyBox.Tests.Integration;
///
/// 商品服务集成测试
/// 测试完整的商品查询流程
/// Requirements: 1.1-1.6, 2.1-2.7
///
public class GoodsServiceIntegrationTests
{
private HoneyBoxDbContext CreateInMemoryDbContext()
{
var options = new DbContextOptionsBuilder()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
return new HoneyBoxDbContext(options);
}
private GoodsService CreateGoodsService(HoneyBoxDbContext dbContext, IGoodsCacheService? cacheService = null)
{
var mockLogger = new Mock>();
var mockCacheService = cacheService ?? CreateMockCacheService();
return new GoodsService(dbContext, mockCacheService, mockLogger.Object);
}
private IGoodsCacheService CreateMockCacheService()
{
var mock = new Mock();
mock.Setup(x => x.GetJoinCountAsync(It.IsAny())).ReturnsAsync(-1);
mock.Setup(x => x.SetJoinCountAsync(It.IsAny(), It.IsAny())).Returns(Task.CompletedTask);
return mock.Object;
}
#region 商品列表测试 (Requirements 1.1-1.6)
///
/// 测试商品列表查询 - 返回分页商品
/// Requirements: 1.1
///
[Fact]
public async Task GetGoodsList_WithTypeFilter_ReturnsMatchingGoods()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
// 添加商品类型
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 2, Name = "无限赏", FlName = "无限赏", CornerText = "无限赏" });
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 6, Name = "全局赏", FlName = "全局赏", CornerText = "全局赏" });
await dbContext.SaveChangesAsync();
// 添加测试商品
var goods = new List
{
new() { Id = 1, Title = "商品1", Type = 2, Status = 1, ShowIs = 0, Price = 10, Stock = 10, Sort = 1, ImgUrl = "img1.jpg", ImgUrlDetail = "detail1.jpg" },
new() { Id = 2, Title = "商品2", Type = 2, Status = 1, ShowIs = 0, Price = 20, Stock = 20, Sort = 2, ImgUrl = "img2.jpg", ImgUrlDetail = "detail2.jpg" },
new() { Id = 3, Title = "商品3", Type = 6, Status = 1, ShowIs = 0, Price = 30, Stock = 30, Sort = 3, ImgUrl = "img3.jpg", ImgUrlDetail = "detail3.jpg" }
};
await dbContext.Goods.AddRangeAsync(goods);
await dbContext.SaveChangesAsync();
// Act - 查询类型2的商品
var request = new GoodsListRequest { Type = 2, Page = 1, PageSize = 10 };
var result = await service.GetGoodsListAsync(request, 0);
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Total);
Assert.All(result.Data, g => Assert.Equal(2, g.Type));
}
///
/// 测试商品列表查询 - type=-1返回默认类型商品
/// Requirements: 1.2
///
[Fact]
public async Task GetGoodsList_TypeMinusOne_ReturnsDefaultTypeGoods()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
// 添加商品类型
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 2, Name = "无限赏", FlName = "无限赏", CornerText = "无限赏" });
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 6, Name = "全局赏", FlName = "全局赏", CornerText = "全局赏" });
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 8, Name = "领主赏", FlName = "领主赏", CornerText = "领主赏" });
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 16, Name = "其他", FlName = "其他", CornerText = "其他" });
await dbContext.SaveChangesAsync();
// 添加测试商品 - 默认类型: 2, 6, 8, 16
var goods = new List
{
new() { Id = 1, Title = "商品1", Type = 2, Status = 1, ShowIs = 0, Price = 10, Stock = 10, Sort = 1, ImgUrl = "img1.jpg", ImgUrlDetail = "detail1.jpg" },
new() { Id = 2, Title = "商品2", Type = 6, Status = 1, ShowIs = 0, Price = 20, Stock = 20, Sort = 2, ImgUrl = "img2.jpg", ImgUrlDetail = "detail2.jpg" },
new() { Id = 3, Title = "商品3", Type = 1, Status = 1, ShowIs = 0, Price = 30, Stock = 30, Sort = 3, ImgUrl = "img3.jpg", ImgUrlDetail = "detail3.jpg" } // 非默认类型
};
await dbContext.Goods.AddRangeAsync(goods);
await dbContext.SaveChangesAsync();
// Act - type=-1 返回默认类型
var request = new GoodsListRequest { Type = -1, Page = 1, PageSize = 10 };
var result = await service.GetGoodsListAsync(request, 0);
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Total); // 只有类型2和6的商品
Assert.All(result.Data, g => Assert.Contains(g.Type, new[] { 2, 6, 8, 16 }));
}
///
/// 测试商品列表查询 - 过滤非上架商品
/// Requirements: 1.4
///
[Fact]
public async Task GetGoodsList_FiltersInactiveGoods()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 2, Name = "无限赏", FlName = "无限赏", CornerText = "无限赏" });
await dbContext.SaveChangesAsync();
var goods = new List
{
new() { Id = 1, Title = "上架商品", Type = 2, Status = 1, ShowIs = 0, Price = 10, Stock = 10, Sort = 1, ImgUrl = "img1.jpg", ImgUrlDetail = "detail1.jpg" },
new() { Id = 2, Title = "下架商品", Type = 2, Status = 0, ShowIs = 0, Price = 20, Stock = 20, Sort = 2, ImgUrl = "img2.jpg", ImgUrlDetail = "detail2.jpg" }
};
await dbContext.Goods.AddRangeAsync(goods);
await dbContext.SaveChangesAsync();
// Act
var request = new GoodsListRequest { Type = 2, Page = 1, PageSize = 10 };
var result = await service.GetGoodsListAsync(request, 0);
// Assert
Assert.Single(result.Data);
Assert.Equal("上架商品", result.Data[0].Title);
}
///
/// 测试商品列表查询 - 解锁金额过滤
/// Requirements: 1.5
///
[Fact]
public async Task GetGoodsList_FiltersUnlockAmountForAnonymousUser()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 2, Name = "无限赏", FlName = "无限赏", CornerText = "无限赏" });
await dbContext.SaveChangesAsync();
var goods = new List
{
new() { Id = 1, Title = "无门槛商品", Type = 2, Status = 1, ShowIs = 0, Price = 10, Stock = 10, Sort = 1, UnlockAmount = 0, ImgUrl = "img1.jpg", ImgUrlDetail = "detail1.jpg" },
new() { Id = 2, Title = "有门槛商品", Type = 2, Status = 1, ShowIs = 0, Price = 20, Stock = 20, Sort = 2, UnlockAmount = 100, ImgUrl = "img2.jpg", ImgUrlDetail = "detail2.jpg" }
};
await dbContext.Goods.AddRangeAsync(goods);
await dbContext.SaveChangesAsync();
// Act - 未登录用户 (userId=0)
var request = new GoodsListRequest { Type = 2, Page = 1, PageSize = 10 };
var result = await service.GetGoodsListAsync(request, 0);
// Assert - 只能看到无门槛商品
Assert.Single(result.Data);
Assert.Equal("无门槛商品", result.Data[0].Title);
}
///
/// 测试商品列表查询 - 排序正确性
/// Requirements: 1.6
///
[Fact]
public async Task GetGoodsList_SortsBySortDescThenIdDesc()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 2, Name = "无限赏", FlName = "无限赏", CornerText = "无限赏" });
await dbContext.SaveChangesAsync();
var goods = new List
{
new() { Id = 1, Title = "商品1", Type = 2, Status = 1, ShowIs = 0, Price = 10, Stock = 10, Sort = 1, ImgUrl = "img1.jpg", ImgUrlDetail = "detail1.jpg" },
new() { Id = 2, Title = "商品2", Type = 2, Status = 1, ShowIs = 0, Price = 20, Stock = 20, Sort = 2, ImgUrl = "img2.jpg", ImgUrlDetail = "detail2.jpg" },
new() { Id = 3, Title = "商品3", Type = 2, Status = 1, ShowIs = 0, Price = 30, Stock = 30, Sort = 2, ImgUrl = "img3.jpg", ImgUrlDetail = "detail3.jpg" }
};
await dbContext.Goods.AddRangeAsync(goods);
await dbContext.SaveChangesAsync();
// Act
var request = new GoodsListRequest { Type = 2, Page = 1, PageSize = 10 };
var result = await service.GetGoodsListAsync(request, 0);
// Assert - 按sort DESC, id DESC排序
Assert.Equal(3, result.Data.Count);
Assert.Equal(3, result.Data[0].Id); // sort=2, id=3
Assert.Equal(2, result.Data[1].Id); // sort=2, id=2
Assert.Equal(1, result.Data[2].Id); // sort=1, id=1
}
///
/// 测试商品列表查询 - 分页功能
/// Requirements: 1.1
///
[Fact]
public async Task GetGoodsList_PaginationWorksCorrectly()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 2, Name = "无限赏", FlName = "无限赏", CornerText = "无限赏" });
await dbContext.SaveChangesAsync();
// 添加15条商品
var goods = Enumerable.Range(1, 15).Select(i => new Good
{
Id = i,
Title = $"商品{i}",
Type = 2,
Status = 1,
ShowIs = 0,
Price = i * 10,
Stock = 10,
Sort = i,
ImgUrl = $"img{i}.jpg",
ImgUrlDetail = $"detail{i}.jpg"
}).ToList();
await dbContext.Goods.AddRangeAsync(goods);
await dbContext.SaveChangesAsync();
// Act - 第一页
var page1Request = new GoodsListRequest { Type = 2, Page = 1, PageSize = 10 };
var page1 = await service.GetGoodsListAsync(page1Request, 0);
// Act - 第二页
var page2Request = new GoodsListRequest { Type = 2, Page = 2, PageSize = 10 };
var page2 = await service.GetGoodsListAsync(page2Request, 0);
// Assert
Assert.Equal(15, page1.Total);
Assert.Equal(2, page1.LastPage);
Assert.Equal(10, page1.Data.Count);
Assert.Equal(5, page2.Data.Count);
}
#endregion
#region 商品详情测试 (Requirements 2.1-2.7)
///
/// 测试商品详情查询 - 返回完整商品信息
/// Requirements: 2.1
///
[Fact]
public async Task GetGoodsDetail_ReturnsCompleteGoodsInfo()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
// 添加商品类型
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 2, Name = "无限赏", FlName = "无限赏", CornerText = "无限赏" });
await dbContext.SaveChangesAsync();
// 添加商品
var goods = new Good
{
Id = 1,
Title = "测试商品",
Type = 2,
Status = 1,
ShowIs = 0,
Price = 10,
Stock = 5,
SaleStock = 2,
LockIs = 0,
ImgUrl = "img.jpg",
ImgUrlDetail = "detail.jpg",
CouponIs = 1,
CouponPro = 10,
IntegralIs = 1,
RageIs = 0,
LingzhuIs = 0,
DailyXiangou = 0,
QuanjuXiangou = 0
};
await dbContext.Goods.AddAsync(goods);
// 添加奖品等级
await dbContext.PrizeLevels.AddAsync(new PrizeLevel { Id = 10, Title = "A赏", Color = "#FF0000" });
await dbContext.SaveChangesAsync();
// 添加奖品
var goodsItem = new GoodsItem
{
Id = 1,
GoodsId = 1,
Num = 1,
Title = "奖品1",
Stock = 10,
SurplusStock = 8,
Price = 100,
ScMoney = 50,
ShangId = 10,
GoodsListId = 0,
ImgUrl = "prize.jpg",
Sort = 1
};
await dbContext.GoodsItems.AddAsync(goodsItem);
await dbContext.SaveChangesAsync();
// Act
var result = await service.GetGoodsDetailAsync(1, 1, 0);
// Assert
Assert.NotNull(result);
Assert.NotNull(result.Goods);
Assert.Equal(1, result.Goods.Id);
Assert.Equal("测试商品", result.Goods.Title);
Assert.Equal(2, result.Goods.Type);
Assert.NotNull(result.LockInfo);
Assert.NotNull(result.GoodsList);
Assert.NotNull(result.LimitInfo);
}
///
/// 测试商品详情查询 - 自动选择箱号
/// Requirements: 2.2
///
[Fact]
public async Task GetGoodsDetail_AutoSelectsBoxNumber_WhenGoodsNumIsZero()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 2, Name = "无限赏", FlName = "无限赏", CornerText = "无限赏" });
await dbContext.SaveChangesAsync();
var goods = new Good
{
Id = 1,
Title = "测试商品",
Type = 2,
Status = 1,
ShowIs = 0,
Price = 10,
Stock = 3,
ImgUrl = "img.jpg",
ImgUrlDetail = "detail.jpg"
};
await dbContext.Goods.AddAsync(goods);
// 添加奖品 - 箱号1无库存,箱号2有库存
var goodsItems = new List
{
new() { Id = 1, GoodsId = 1, Num = 1, Title = "奖品1", Stock = 10, SurplusStock = 0, ShangId = 10, GoodsListId = 0, ImgUrl = "p1.jpg" },
new() { Id = 2, GoodsId = 1, Num = 2, Title = "奖品2", Stock = 10, SurplusStock = 5, ShangId = 10, GoodsListId = 0, ImgUrl = "p2.jpg" }
};
await dbContext.GoodsItems.AddRangeAsync(goodsItems);
await dbContext.PrizeLevels.AddAsync(new PrizeLevel { Id = 10, Title = "A赏" });
await dbContext.SaveChangesAsync();
// Act - goodsNum=0 应自动选择有库存的箱号
var result = await service.GetGoodsDetailAsync(1, 0, 0);
// Assert - 应选择箱号2(有库存)
Assert.Equal(2, result.Goods.Num);
}
///
/// 测试商品详情查询 - 概率计算
/// Requirements: 2.3
///
[Fact]
public async Task GetGoodsDetail_CalculatesProbabilityCorrectly()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 2, Name = "无限赏", FlName = "无限赏", CornerText = "无限赏" });
await dbContext.SaveChangesAsync();
var goods = new Good
{
Id = 1,
Title = "测试商品",
Type = 2,
Status = 1,
ShowIs = 0,
Price = 10,
Stock = 1,
ImgUrl = "img.jpg",
ImgUrlDetail = "detail.jpg"
};
await dbContext.Goods.AddAsync(goods);
// 添加奖品等级
await dbContext.PrizeLevels.AddAsync(new PrizeLevel { Id = 10, Title = "A赏" });
await dbContext.PrizeLevels.AddAsync(new PrizeLevel { Id = 11, Title = "B赏" });
await dbContext.SaveChangesAsync();
// 添加奖品 - 总剩余库存=10, A赏剩余2, B赏剩余8
var goodsItems = new List
{
new() { Id = 1, GoodsId = 1, Num = 1, Title = "A赏奖品", Stock = 5, SurplusStock = 2, ShangId = 10, GoodsListId = 0, ImgUrl = "a.jpg", Sort = 2 },
new() { Id = 2, GoodsId = 1, Num = 1, Title = "B赏奖品", Stock = 10, SurplusStock = 8, ShangId = 11, GoodsListId = 0, ImgUrl = "b.jpg", Sort = 1 }
};
await dbContext.GoodsItems.AddRangeAsync(goodsItems);
await dbContext.SaveChangesAsync();
// Act
var result = await service.GetGoodsDetailAsync(1, 1, 0);
// Assert - 概率计算: A赏=2/10*100=20%, B赏=8/10*100=80%
Assert.Equal(2, result.GoodsList.Count);
var aItem = result.GoodsList.First(x => x.ShangId == 10);
var bItem = result.GoodsList.First(x => x.ShangId == 11);
Assert.Contains("20", aItem.Pro);
Assert.Contains("80", bItem.Pro);
}
///
/// 测试商品详情查询 - 锁箱信息
/// Requirements: 2.4
///
[Fact]
public async Task GetGoodsDetail_ReturnsLockInfo()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 2, Name = "无限赏", FlName = "无限赏", CornerText = "无限赏" });
await dbContext.SaveChangesAsync();
var goods = new Good
{
Id = 1,
Title = "测试商品",
Type = 2,
Status = 1,
ShowIs = 0,
Price = 10,
Stock = 1,
LockIs = 1, // 支持锁箱
ImgUrl = "img.jpg",
ImgUrlDetail = "detail.jpg"
};
await dbContext.Goods.AddAsync(goods);
// 添加锁箱记录
var futureTime = DateTimeOffset.UtcNow.AddMinutes(30).ToUnixTimeSeconds();
var goodsLock = new GoodsLock
{
Id = 1,
GoodsIdNum = "1_1",
UserId = 100,
EndTime = futureTime
};
await dbContext.GoodsLocks.AddAsync(goodsLock);
// 添加锁箱用户
var user = new User
{
Id = 100,
OpenId = "test_openid_100",
Uid = "test_uid_100",
Nickname = "锁箱用户",
HeadImg = "avatar.jpg"
};
await dbContext.Users.AddAsync(user);
await dbContext.SaveChangesAsync();
// Act
var result = await service.GetGoodsDetailAsync(1, 1, 0);
// Assert
Assert.NotNull(result.LockInfo);
Assert.Equal(1, result.LockInfo.LockIs);
Assert.Equal("锁箱用户", result.LockInfo.GoodsLockUserNickname);
Assert.True(result.LockInfo.GoodsLockSurplusTime > 0);
}
///
/// 测试商品详情查询 - 收藏状态
/// Requirements: 2.6
///
[Fact]
public async Task GetGoodsDetail_ReturnsCollectionStatus()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 2, Name = "无限赏", FlName = "无限赏", CornerText = "无限赏" });
await dbContext.SaveChangesAsync();
var goods = new Good
{
Id = 1,
Title = "测试商品",
Type = 2,
Status = 1,
ShowIs = 0,
Price = 10,
Stock = 1,
ImgUrl = "img.jpg",
ImgUrlDetail = "detail.jpg"
};
await dbContext.Goods.AddAsync(goods);
// 添加收藏记录
var collection = new GoodsCollection
{
Id = 1,
UserId = 100,
GoodsId = 1,
Num = 1,
Type = 2
};
await dbContext.GoodsCollections.AddAsync(collection);
await dbContext.SaveChangesAsync();
// Act - 已收藏用户
var result = await service.GetGoodsDetailAsync(1, 1, 100);
// Assert
Assert.Equal(1, result.Goods.CollectionIs);
}
///
/// 测试商品详情查询 - 限购信息
/// Requirements: 2.7
///
[Fact]
public async Task GetGoodsDetail_ReturnsLimitInfo()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
await dbContext.GoodsTypes.AddAsync(new GoodsType { Value = 2, Name = "无限赏", FlName = "无限赏", CornerText = "无限赏" });
await dbContext.SaveChangesAsync();
var goods = new Good
{
Id = 1,
Title = "测试商品",
Type = 2,
Status = 1,
ShowIs = 0,
Price = 10,
Stock = 1,
DailyXiangou = 5,
QuanjuXiangou = 10,
ImgUrl = "img.jpg",
ImgUrlDetail = "detail.jpg"
};
await dbContext.Goods.AddAsync(goods);
await dbContext.SaveChangesAsync();
// Act
var result = await service.GetGoodsDetailAsync(1, 1, 0);
// Assert
Assert.NotNull(result.LimitInfo);
Assert.Equal(5, result.LimitInfo.DailyXiangou);
Assert.Equal(10, result.LimitInfo.QuanjuXiangou);
}
///
/// 测试商品详情查询 - 商品不存在
/// Requirements: 2.1
///
[Fact]
public async Task GetGoodsDetail_ThrowsException_WhenGoodsNotFound()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
// Act & Assert
await Assert.ThrowsAsync(
() => service.GetGoodsDetailAsync(999, 1, 0));
}
///
/// 测试商品详情查询 - 商品已下架
/// Requirements: 2.1
///
[Fact]
public async Task GetGoodsDetail_ThrowsException_WhenGoodsOffline()
{
// Arrange
var dbContext = CreateInMemoryDbContext();
var service = CreateGoodsService(dbContext);
var goods = new Good
{
Id = 1,
Title = "下架商品",
Type = 2,
Status = 0, // 下架
ShowIs = 0,
Price = 10,
Stock = 1,
ImgUrl = "img.jpg",
ImgUrlDetail = "detail.jpg"
};
await dbContext.Goods.AddAsync(goods);
await dbContext.SaveChangesAsync();
// Act & Assert
await Assert.ThrowsAsync(
() => service.GetGoodsDetailAsync(1, 1, 0));
}
#endregion
}