493 lines
14 KiB
C#
493 lines
14 KiB
C#
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;
|
|
|
|
/// <summary>
|
|
/// DesignatedPrizeService 单元测试
|
|
/// </summary>
|
|
public class DesignatedPrizeServiceTests : IDisposable
|
|
{
|
|
private readonly HoneyBoxDbContext _dbContext;
|
|
private readonly Mock<ILogger<DesignatedPrizeService>> _mockLogger;
|
|
private readonly DesignatedPrizeService _service;
|
|
|
|
public DesignatedPrizeServiceTests()
|
|
{
|
|
var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
|
|
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
|
.Options;
|
|
|
|
_dbContext = new HoneyBoxDbContext(options);
|
|
_mockLogger = new Mock<ILogger<DesignatedPrizeService>>();
|
|
_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<BusinessException>(
|
|
() => _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<BusinessException>(
|
|
() => _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<BusinessException>(
|
|
() => _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<BusinessException>(
|
|
() => _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<BusinessException>(
|
|
() => _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<BusinessException>(
|
|
() => _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<BusinessException>(
|
|
() => _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<BusinessException>(
|
|
() => _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<BusinessException>(
|
|
() => _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<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
|
|
}
|