using HoneyBox.Admin.Business.Controllers; using HoneyBox.Admin.Business.Models; using HoneyBox.Admin.Business.Models.DesignatedPrize; using HoneyBox.Admin.Business.Services; using HoneyBox.Admin.Business.Services.Interfaces; using HoneyBox.Model.Data; using HoneyBox.Model.Entities; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using Moq; using Xunit; namespace HoneyBox.Tests.Integration; /// /// DesignatedPrizeController API 集成测试 /// 测试完整 CRUD 流程和错误响应 /// Requirements: 4.1, 4.2, 4.3, 4.4, 4.5 /// public class DesignatedPrizeControllerTests : IDisposable { private readonly HoneyBoxDbContext _dbContext; private readonly IDesignatedPrizeService _service; private readonly DesignatedPrizeController _controller; public DesignatedPrizeControllerTests() { var options = new DbContextOptionsBuilder() .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) .Options; _dbContext = new HoneyBoxDbContext(options); var mockLogger = new Mock>(); _service = new DesignatedPrizeService(_dbContext, mockLogger.Object); _controller = new DesignatedPrizeController(_service); } public void Dispose() { _dbContext.Dispose(); } #region GetList Tests (Requirements 4.2) /// /// 测试获取配置列表 - 成功返回配置 /// Requirements: 4.2 /// [Fact] public async Task GetList_WithExistingGoods_ReturnsConfigs() { // Arrange await SeedTestDataAsync(); var goods = await _dbContext.Goods.FirstAsync(); // Act var result = await _controller.GetList(goods.Id); // Assert var okResult = Assert.IsType(result); var response = Assert.IsType>>(okResult.Value); Assert.Equal(0, response.Code); Assert.NotNull(response.Data); Assert.Single(response.Data); } /// /// 测试获取配置列表 - 商品不存在返回错误 /// Requirements: 4.2 /// [Fact] public async Task GetList_WithNonExistingGoods_ReturnsNotFoundError() { // Act var result = await _controller.GetList(99999); // Assert var okResult = Assert.IsType(result); var response = Assert.IsType(okResult.Value); Assert.Equal(BusinessErrorCodes.NotFound, response.Code); Assert.Contains("盒子不存在", response.Message); } #endregion #region Create Tests (Requirements 4.1, 4.5) /// /// 测试创建配置 - 成功创建 /// Requirements: 4.1 /// [Fact] public async Task Create_WithValidRequest_ReturnsCreatedConfig() { // 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 _controller.Create(goods.Id, request); // Assert var okResult = Assert.IsType(result); var response = Assert.IsType>(okResult.Value); Assert.Equal(0, response.Code); Assert.NotNull(response.Data); Assert.Equal(goods.Id, response.Data.GoodsId); Assert.Equal(prize.Id, response.Data.GoodsItemId); Assert.Equal(user.Id, response.Data.UserId); } /// /// 测试创建配置 - 商品不存在返回404 /// Requirements: 4.1 /// [Fact] public async Task Create_WithNonExistingGoods_ReturnsNotFoundError() { // Arrange var request = new CreateDesignatedPrizeRequest { GoodsItemId = 1, UserId = 1 }; // Act var result = await _controller.Create(99999, request); // Assert var okResult = Assert.IsType(result); var response = Assert.IsType(okResult.Value); Assert.Equal(BusinessErrorCodes.NotFound, response.Code); Assert.Contains("盒子不存在", response.Message); } /// /// 测试创建配置 - 奖品不存在返回404 /// Requirements: 4.1 /// [Fact] public async Task Create_WithNonExistingPrize_ReturnsNotFoundError() { // 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 var result = await _controller.Create(goods.Id, request); // Assert var okResult = Assert.IsType(result); var response = Assert.IsType(okResult.Value); Assert.Equal(BusinessErrorCodes.NotFound, response.Code); Assert.Contains("奖品不存在", response.Message); } /// /// 测试创建配置 - 用户不存在返回404 /// Requirements: 4.1 /// [Fact] public async Task Create_WithNonExistingUser_ReturnsNotFoundError() { // 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 var result = await _controller.Create(goods.Id, request); // Assert var okResult = Assert.IsType(result); var response = Assert.IsType(okResult.Value); Assert.Equal(BusinessErrorCodes.NotFound, response.Code); Assert.Contains("用户不存在", response.Message); } /// /// 测试创建配置 - 重复配置返回400 /// Requirements: 4.5 /// [Fact] public async Task Create_WithDuplicateConfig_ReturnsConflictError() { // 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 var result = await _controller.Create(existingConfig.GoodsId, request); // Assert var okResult = Assert.IsType(result); var response = Assert.IsType(okResult.Value); Assert.Equal(BusinessErrorCodes.Conflict, response.Code); Assert.Contains("该奖品已配置指定用户", response.Message); } #endregion #region Update Tests (Requirements 4.3) /// /// 测试更新配置 - 成功更新 /// Requirements: 4.3 /// [Fact] public async Task Update_WithValidRequest_ReturnsUpdatedConfig() { // Arrange await SeedTestDataAsync(); var config = await _dbContext.GoodsDesignatedPrizes.FirstAsync(); var request = new UpdateDesignatedPrizeRequest { IsActive = false, Remark = "更新后的备注" }; // Act var result = await _controller.Update(config.GoodsId, config.Id, request); // Assert var okResult = Assert.IsType(result); var response = Assert.IsType>(okResult.Value); Assert.Equal(0, response.Code); Assert.NotNull(response.Data); Assert.False(response.Data.IsActive); Assert.Equal("更新后的备注", response.Data.Remark); } /// /// 测试更新配置 - 配置不存在返回404 /// Requirements: 4.3 /// [Fact] public async Task Update_WithNonExistingConfig_ReturnsNotFoundError() { // Arrange var request = new UpdateDesignatedPrizeRequest { IsActive = false }; // Act var result = await _controller.Update(1, 99999, request); // Assert var okResult = Assert.IsType(result); var response = Assert.IsType(okResult.Value); Assert.Equal(BusinessErrorCodes.NotFound, response.Code); Assert.Contains("配置不存在", response.Message); } #endregion #region Delete Tests (Requirements 4.4) /// /// 测试删除配置 - 成功删除 /// Requirements: 4.4 /// [Fact] public async Task Delete_WithExistingConfig_ReturnsSuccess() { // Arrange await SeedTestDataAsync(); var config = await _dbContext.GoodsDesignatedPrizes.FirstAsync(); // Act var result = await _controller.Delete(config.GoodsId, config.Id); // Assert var okResult = Assert.IsType(result); var response = Assert.IsType(okResult.Value); Assert.Equal(0, response.Code); Assert.Contains("删除成功", response.Message); // Verify deletion var deletedConfig = await _dbContext.GoodsDesignatedPrizes.FindAsync(config.Id); Assert.Null(deletedConfig); } /// /// 测试删除配置 - 配置不存在返回404 /// Requirements: 4.4 /// [Fact] public async Task Delete_WithNonExistingConfig_ReturnsNotFoundError() { // Act var result = await _controller.Delete(1, 99999); // Assert var okResult = Assert.IsType(result); var response = Assert.IsType(okResult.Value); Assert.Equal(BusinessErrorCodes.NotFound, response.Code); Assert.Contains("配置不存在", response.Message); } #endregion #region Full CRUD Flow Test /// /// 测试完整 CRUD 流程 /// Requirements: 4.1, 4.2, 4.3, 4.4 /// [Fact] public async Task FullCrudFlow_WorksCorrectly() { // 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(); // Step 1: Create var createRequest = new CreateDesignatedPrizeRequest { GoodsItemId = prize.Id, UserId = user.Id, Remark = "初始备注" }; var createResult = await _controller.Create(goods.Id, createRequest); var createResponse = Assert.IsType>( Assert.IsType(createResult).Value); Assert.Equal(0, createResponse.Code); var configId = createResponse.Data!.Id; // Step 2: Read (List) var listResult = await _controller.GetList(goods.Id); var listResponse = Assert.IsType>>( Assert.IsType(listResult).Value); Assert.Equal(0, listResponse.Code); Assert.Single(listResponse.Data!); Assert.Equal(configId, listResponse.Data[0].Id); // Step 3: Update var updateRequest = new UpdateDesignatedPrizeRequest { IsActive = false, Remark = "更新后的备注" }; var updateResult = await _controller.Update(goods.Id, configId, updateRequest); var updateResponse = Assert.IsType>( Assert.IsType(updateResult).Value); Assert.Equal(0, updateResponse.Code); Assert.False(updateResponse.Data!.IsActive); Assert.Equal("更新后的备注", updateResponse.Data.Remark); // Step 4: Delete var deleteResult = await _controller.Delete(goods.Id, configId); var deleteResponse = Assert.IsType( Assert.IsType(deleteResult).Value); Assert.Equal(0, deleteResponse.Code); // Verify deletion var finalListResult = await _controller.GetList(goods.Id); var finalListResponse = Assert.IsType>>( Assert.IsType(finalListResult).Value); Assert.Empty(finalListResponse.Data!); } #endregion #region Helper Methods private async Task SeedTestDataAsync() { 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(); 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(); 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(); 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() { 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(); 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(); 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 }