536 lines
17 KiB
C#
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
|
|
}
|