HaniBlindBox/server/HoneyBox/tests/HoneyBox.Tests/Integration/DesignatedPrizeControllerTests.cs
2026-02-01 19:30:51 +08:00

536 lines
17 KiB
C#

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;
/// <summary>
/// DesignatedPrizeController API 集成测试
/// 测试完整 CRUD 流程和错误响应
/// Requirements: 4.1, 4.2, 4.3, 4.4, 4.5
/// </summary>
public class DesignatedPrizeControllerTests : IDisposable
{
private readonly HoneyBoxDbContext _dbContext;
private readonly IDesignatedPrizeService _service;
private readonly DesignatedPrizeController _controller;
public DesignatedPrizeControllerTests()
{
var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
_dbContext = new HoneyBoxDbContext(options);
var mockLogger = new Mock<ILogger<DesignatedPrizeService>>();
_service = new DesignatedPrizeService(_dbContext, mockLogger.Object);
_controller = new DesignatedPrizeController(_service);
}
public void Dispose()
{
_dbContext.Dispose();
}
#region GetList Tests (Requirements 4.2)
/// <summary>
/// 测试获取配置列表 - 成功返回配置
/// Requirements: 4.2
/// </summary>
[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<OkObjectResult>(result);
var response = Assert.IsType<ApiResponse<List<DesignatedPrizeDto>>>(okResult.Value);
Assert.Equal(0, response.Code);
Assert.NotNull(response.Data);
Assert.Single(response.Data);
}
/// <summary>
/// 测试获取配置列表 - 商品不存在返回错误
/// Requirements: 4.2
/// </summary>
[Fact]
public async Task GetList_WithNonExistingGoods_ReturnsNotFoundError()
{
// Act
var result = await _controller.GetList(99999);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var response = Assert.IsType<ApiResponse>(okResult.Value);
Assert.Equal(BusinessErrorCodes.NotFound, response.Code);
Assert.Contains("盒子不存在", response.Message);
}
#endregion
#region Create Tests (Requirements 4.1, 4.5)
/// <summary>
/// 测试创建配置 - 成功创建
/// Requirements: 4.1
/// </summary>
[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<OkObjectResult>(result);
var response = Assert.IsType<ApiResponse<DesignatedPrizeDto>>(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);
}
/// <summary>
/// 测试创建配置 - 商品不存在返回404
/// Requirements: 4.1
/// </summary>
[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<OkObjectResult>(result);
var response = Assert.IsType<ApiResponse>(okResult.Value);
Assert.Equal(BusinessErrorCodes.NotFound, response.Code);
Assert.Contains("盒子不存在", response.Message);
}
/// <summary>
/// 测试创建配置 - 奖品不存在返回404
/// Requirements: 4.1
/// </summary>
[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<OkObjectResult>(result);
var response = Assert.IsType<ApiResponse>(okResult.Value);
Assert.Equal(BusinessErrorCodes.NotFound, response.Code);
Assert.Contains("奖品不存在", response.Message);
}
/// <summary>
/// 测试创建配置 - 用户不存在返回404
/// Requirements: 4.1
/// </summary>
[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<OkObjectResult>(result);
var response = Assert.IsType<ApiResponse>(okResult.Value);
Assert.Equal(BusinessErrorCodes.NotFound, response.Code);
Assert.Contains("用户不存在", response.Message);
}
/// <summary>
/// 测试创建配置 - 重复配置返回400
/// Requirements: 4.5
/// </summary>
[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<OkObjectResult>(result);
var response = Assert.IsType<ApiResponse>(okResult.Value);
Assert.Equal(BusinessErrorCodes.Conflict, response.Code);
Assert.Contains("该奖品已配置指定用户", response.Message);
}
#endregion
#region Update Tests (Requirements 4.3)
/// <summary>
/// 测试更新配置 - 成功更新
/// Requirements: 4.3
/// </summary>
[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<OkObjectResult>(result);
var response = Assert.IsType<ApiResponse<DesignatedPrizeDto>>(okResult.Value);
Assert.Equal(0, response.Code);
Assert.NotNull(response.Data);
Assert.False(response.Data.IsActive);
Assert.Equal("更新后的备注", response.Data.Remark);
}
/// <summary>
/// 测试更新配置 - 配置不存在返回404
/// Requirements: 4.3
/// </summary>
[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<OkObjectResult>(result);
var response = Assert.IsType<ApiResponse>(okResult.Value);
Assert.Equal(BusinessErrorCodes.NotFound, response.Code);
Assert.Contains("配置不存在", response.Message);
}
#endregion
#region Delete Tests (Requirements 4.4)
/// <summary>
/// 测试删除配置 - 成功删除
/// Requirements: 4.4
/// </summary>
[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<OkObjectResult>(result);
var response = Assert.IsType<ApiResponse>(okResult.Value);
Assert.Equal(0, response.Code);
Assert.Contains("删除成功", response.Message);
// Verify deletion
var deletedConfig = await _dbContext.GoodsDesignatedPrizes.FindAsync(config.Id);
Assert.Null(deletedConfig);
}
/// <summary>
/// 测试删除配置 - 配置不存在返回404
/// Requirements: 4.4
/// </summary>
[Fact]
public async Task Delete_WithNonExistingConfig_ReturnsNotFoundError()
{
// Act
var result = await _controller.Delete(1, 99999);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var response = Assert.IsType<ApiResponse>(okResult.Value);
Assert.Equal(BusinessErrorCodes.NotFound, response.Code);
Assert.Contains("配置不存在", response.Message);
}
#endregion
#region Full CRUD Flow Test
/// <summary>
/// 测试完整 CRUD 流程
/// Requirements: 4.1, 4.2, 4.3, 4.4
/// </summary>
[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<ApiResponse<DesignatedPrizeDto>>(
Assert.IsType<OkObjectResult>(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<ApiResponse<List<DesignatedPrizeDto>>>(
Assert.IsType<OkObjectResult>(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<ApiResponse<DesignatedPrizeDto>>(
Assert.IsType<OkObjectResult>(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<ApiResponse>(
Assert.IsType<OkObjectResult>(deleteResult).Value);
Assert.Equal(0, deleteResponse.Code);
// Verify deletion
var finalListResult = await _controller.GetList(goods.Id);
var finalListResponse = Assert.IsType<ApiResponse<List<DesignatedPrizeDto>>>(
Assert.IsType<OkObjectResult>(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<GoodsItem>
{
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
}