using HoneyBox.Admin.Business.Models; using HoneyBox.Admin.Business.Models.DesignatedPrize; using HoneyBox.Admin.Business.Services; using HoneyBox.Model.Data; using HoneyBox.Model.Entities; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Moq; using Xunit; namespace HoneyBox.Tests.Services; /// /// DesignatedPrizeService 单元测试 /// public class DesignatedPrizeServiceTests : IDisposable { private readonly HoneyBoxDbContext _dbContext; private readonly Mock> _mockLogger; private readonly DesignatedPrizeService _service; public DesignatedPrizeServiceTests() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; _dbContext = new HoneyBoxDbContext(options); _mockLogger = new Mock>(); _service = new DesignatedPrizeService(_dbContext, _mockLogger.Object); } public void Dispose() { _dbContext.Dispose(); } #region GetByGoodsIdAsync Tests [Fact] public async Task GetByGoodsIdAsync_WithExistingGoods_ReturnsConfigs() { // Arrange await SeedTestDataAsync(); var goods = await _dbContext.Goods.FirstAsync(); // Act var result = await _service.GetByGoodsIdAsync(goods.Id); // Assert Assert.NotNull(result); Assert.Single(result); } [Fact] public async Task GetByGoodsIdAsync_WithNonExistingGoods_ThrowsException() { // Act & Assert var exception = await Assert.ThrowsAsync( () => _service.GetByGoodsIdAsync(99999)); Assert.Equal(BusinessErrorCodes.NotFound, exception.Code); Assert.Contains("盒子不存在", exception.Message); } [Fact] public async Task GetByGoodsIdAsync_WithDeletedGoods_ThrowsException() { // Arrange var deletedGoods = new Good { Title = "已删除商品", ImgUrl = "http://test.com/deleted.jpg", ImgUrlDetail = "http://test.com/deleted_detail.jpg", Price = 10, Type = 1, Status = 1, Stock = 10, DeletedAt = DateTime.Now, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; _dbContext.Goods.Add(deletedGoods); await _dbContext.SaveChangesAsync(); // Act & Assert var exception = await Assert.ThrowsAsync( () => _service.GetByGoodsIdAsync(deletedGoods.Id)); Assert.Equal(BusinessErrorCodes.NotFound, exception.Code); } [Fact] public async Task GetByGoodsIdAsync_ReturnsConfigsWithUserAndPrizeInfo() { // Arrange await SeedTestDataAsync(); var goods = await _dbContext.Goods.FirstAsync(); // Act var result = await _service.GetByGoodsIdAsync(goods.Id); // Assert Assert.NotNull(result); var config = result.First(); Assert.NotNull(config.GoodsItemTitle); Assert.NotNull(config.UserNickname); } #endregion #region CreateAsync Tests [Fact] public async Task CreateAsync_WithValidRequest_CreatesConfig() { // Arrange await SeedGoodsWithPrizesAndUsersAsync(); var goods = await _dbContext.Goods.FirstAsync(); var prize = await _dbContext.GoodsItems.FirstAsync(gi => gi.GoodsId == goods.Id); var user = await _dbContext.Users.FirstAsync(); var request = new CreateDesignatedPrizeRequest { GoodsItemId = prize.Id, UserId = user.Id, Remark = "测试备注" }; // Act var result = await _service.CreateAsync(goods.Id, request); // Assert Assert.NotNull(result); Assert.Equal(goods.Id, result.GoodsId); Assert.Equal(prize.Id, result.GoodsItemId); Assert.Equal(user.Id, result.UserId); Assert.True(result.IsActive); Assert.Equal("测试备注", result.Remark); } [Fact] public async Task CreateAsync_WithNonExistingGoods_ThrowsException() { // Arrange var request = new CreateDesignatedPrizeRequest { GoodsItemId = 1, UserId = 1 }; // Act & Assert var exception = await Assert.ThrowsAsync( () => _service.CreateAsync(99999, request)); Assert.Equal(BusinessErrorCodes.NotFound, exception.Code); Assert.Contains("盒子不存在", exception.Message); } [Fact] public async Task CreateAsync_WithNonExistingPrize_ThrowsException() { // Arrange await SeedGoodsWithPrizesAndUsersAsync(); var goods = await _dbContext.Goods.FirstAsync(); var user = await _dbContext.Users.FirstAsync(); var request = new CreateDesignatedPrizeRequest { GoodsItemId = 99999, UserId = user.Id }; // Act & Assert var exception = await Assert.ThrowsAsync( () => _service.CreateAsync(goods.Id, request)); Assert.Equal(BusinessErrorCodes.NotFound, exception.Code); Assert.Contains("奖品不存在", exception.Message); } [Fact] public async Task CreateAsync_WithNonExistingUser_ThrowsException() { // Arrange await SeedGoodsWithPrizesAndUsersAsync(); var goods = await _dbContext.Goods.FirstAsync(); var prize = await _dbContext.GoodsItems.FirstAsync(gi => gi.GoodsId == goods.Id); var request = new CreateDesignatedPrizeRequest { GoodsItemId = prize.Id, UserId = 99999 }; // Act & Assert var exception = await Assert.ThrowsAsync( () => _service.CreateAsync(goods.Id, request)); Assert.Equal(BusinessErrorCodes.NotFound, exception.Code); Assert.Contains("用户不存在", exception.Message); } [Fact] public async Task CreateAsync_WithDuplicateConfig_ThrowsException() { // Arrange await SeedTestDataAsync(); var existingConfig = await _dbContext.GoodsDesignatedPrizes.FirstAsync(); var user = await _dbContext.Users.FirstAsync(); var request = new CreateDesignatedPrizeRequest { GoodsItemId = existingConfig.GoodsItemId, UserId = user.Id }; // Act & Assert var exception = await Assert.ThrowsAsync( () => _service.CreateAsync(existingConfig.GoodsId, request)); Assert.Equal(BusinessErrorCodes.Conflict, exception.Code); Assert.Contains("该奖品已配置指定用户", exception.Message); } #endregion #region UpdateAsync Tests [Fact] public async Task UpdateAsync_WithValidRequest_UpdatesConfig() { // Arrange await SeedTestDataAsync(); var config = await _dbContext.GoodsDesignatedPrizes.FirstAsync(); var request = new UpdateDesignatedPrizeRequest { IsActive = false, Remark = "更新后的备注" }; // Act var result = await _service.UpdateAsync(config.Id, request); // Assert Assert.NotNull(result); Assert.False(result.IsActive); Assert.Equal("更新后的备注", result.Remark); } [Fact] public async Task UpdateAsync_WithNewUserId_UpdatesUser() { // Arrange await SeedTestDataAsync(); var config = await _dbContext.GoodsDesignatedPrizes.FirstAsync(); // Add another user var newUser = new User { OpenId = "new_openid", Uid = "new_uid", Nickname = "新用户", HeadImg = "http://test.com/new.jpg", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; _dbContext.Users.Add(newUser); await _dbContext.SaveChangesAsync(); var request = new UpdateDesignatedPrizeRequest { UserId = newUser.Id }; // Act var result = await _service.UpdateAsync(config.Id, request); // Assert Assert.NotNull(result); Assert.Equal(newUser.Id, result.UserId); } [Fact] public async Task UpdateAsync_WithNonExistingConfig_ThrowsException() { // Arrange var request = new UpdateDesignatedPrizeRequest { IsActive = false }; // Act & Assert var exception = await Assert.ThrowsAsync( () => _service.UpdateAsync(99999, request)); Assert.Equal(BusinessErrorCodes.NotFound, exception.Code); Assert.Contains("配置不存在", exception.Message); } [Fact] public async Task UpdateAsync_WithNonExistingNewUser_ThrowsException() { // Arrange await SeedTestDataAsync(); var config = await _dbContext.GoodsDesignatedPrizes.FirstAsync(); var request = new UpdateDesignatedPrizeRequest { UserId = 99999 }; // Act & Assert var exception = await Assert.ThrowsAsync( () => _service.UpdateAsync(config.Id, request)); Assert.Equal(BusinessErrorCodes.NotFound, exception.Code); Assert.Contains("用户不存在", exception.Message); } #endregion #region DeleteAsync Tests [Fact] public async Task DeleteAsync_WithExistingConfig_DeletesConfig() { // Arrange await SeedTestDataAsync(); var config = await _dbContext.GoodsDesignatedPrizes.FirstAsync(); var configId = config.Id; // Act var result = await _service.DeleteAsync(configId); // Assert Assert.True(result); var deletedConfig = await _dbContext.GoodsDesignatedPrizes.FindAsync(configId); Assert.Null(deletedConfig); } [Fact] public async Task DeleteAsync_WithNonExistingConfig_ThrowsException() { // Act & Assert var exception = await Assert.ThrowsAsync( () => _service.DeleteAsync(99999)); Assert.Equal(BusinessErrorCodes.NotFound, exception.Code); Assert.Contains("配置不存在", exception.Message); } #endregion #region Helper Methods private async Task SeedTestDataAsync() { // Create user var user = new User { OpenId = "test_openid", Uid = "test_uid", Nickname = "测试用户", HeadImg = "http://test.com/avatar.jpg", Mobile = "13800138000", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; _dbContext.Users.Add(user); await _dbContext.SaveChangesAsync(); // Create goods var goods = new Good { Title = "测试商品", ImgUrl = "http://test.com/goods.jpg", ImgUrlDetail = "http://test.com/goods_detail.jpg", Price = 100, Type = 1, Status = 1, Stock = 100, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; _dbContext.Goods.Add(goods); await _dbContext.SaveChangesAsync(); // Create prize var prize = new GoodsItem { GoodsId = goods.Id, Num = 1, Title = "A赏", ImgUrl = "http://test.com/prize.jpg", Stock = 1, SurplusStock = 1, Price = 500, Money = 300, ScMoney = 250, RealPro = 1, GoodsType = 1, PrizeCode = "PC001", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; _dbContext.GoodsItems.Add(prize); await _dbContext.SaveChangesAsync(); // Create designated prize config var config = new GoodsDesignatedPrize { GoodsId = goods.Id, GoodsItemId = prize.Id, UserId = user.Id, IsActive = true, Remark = "测试配置", CreatedAt = DateTime.Now }; _dbContext.GoodsDesignatedPrizes.Add(config); await _dbContext.SaveChangesAsync(); } private async Task SeedGoodsWithPrizesAndUsersAsync() { // Create user var user = new User { OpenId = "test_openid", Uid = "test_uid", Nickname = "测试用户", HeadImg = "http://test.com/avatar.jpg", Mobile = "13800138000", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; _dbContext.Users.Add(user); await _dbContext.SaveChangesAsync(); // Create goods var goods = new Good { Title = "测试商品", ImgUrl = "http://test.com/goods.jpg", ImgUrlDetail = "http://test.com/goods_detail.jpg", Price = 100, Type = 1, Status = 1, Stock = 100, CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; _dbContext.Goods.Add(goods); await _dbContext.SaveChangesAsync(); // Create prizes var prizes = new List { new() { GoodsId = goods.Id, Num = 1, Title = "A赏", ImgUrl = "http://test.com/prize1.jpg", Stock = 1, SurplusStock = 1, Price = 500, Money = 300, ScMoney = 250, RealPro = 1, GoodsType = 1, PrizeCode = "PC001", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }, new() { GoodsId = goods.Id, Num = 2, Title = "B赏", ImgUrl = "http://test.com/prize2.jpg", Stock = 5, SurplusStock = 5, Price = 200, Money = 100, ScMoney = 80, RealPro = 5, GoodsType = 1, PrizeCode = "PC002", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now } }; _dbContext.GoodsItems.AddRange(prizes); await _dbContext.SaveChangesAsync(); } #endregion }