482 lines
13 KiB
C#
482 lines
13 KiB
C#
using HoneyBox.Admin.Business.Models;
|
|
using HoneyBox.Admin.Business.Models.Reward;
|
|
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>
|
|
/// RewardService 单元测试
|
|
/// </summary>
|
|
public class RewardServiceTests : IDisposable
|
|
{
|
|
private readonly HoneyBoxDbContext _dbContext;
|
|
private readonly Mock<ILogger<RewardService>> _mockLogger;
|
|
private readonly RewardService _rewardService;
|
|
|
|
public RewardServiceTests()
|
|
{
|
|
// 使用 InMemory 数据库
|
|
var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
|
|
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
|
|
.Options;
|
|
|
|
_dbContext = new HoneyBoxDbContext(options);
|
|
_mockLogger = new Mock<ILogger<RewardService>>();
|
|
|
|
_rewardService = new RewardService(_dbContext, _mockLogger.Object);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_dbContext.Dispose();
|
|
}
|
|
|
|
#region GetRewardsAsync Tests
|
|
|
|
[Fact]
|
|
public async Task GetRewardsAsync_WithNoFilters_ReturnsAllRewards()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
var request = new RewardListRequest { Page = 1, PageSize = 10 };
|
|
|
|
// Act
|
|
var result = await _rewardService.GetRewardsAsync(request);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Equal(3, result.Total);
|
|
Assert.Equal(3, result.List.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetRewardsAsync_WithTypeFilter_ReturnsFilteredRewards()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
var request = new RewardListRequest { Page = 1, PageSize = 10, RewardType = 1 };
|
|
|
|
// Act
|
|
var result = await _rewardService.GetRewardsAsync(request);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Equal(1, result.Total);
|
|
Assert.All(result.List, r => Assert.Equal(1, r.RewardType));
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetRewardsAsync_WithKeywordFilter_ReturnsFilteredRewards()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
var request = new RewardListRequest { Page = 1, PageSize = 10, Keyword = "钻石" };
|
|
|
|
// Act
|
|
var result = await _rewardService.GetRewardsAsync(request);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Equal(1, result.Total);
|
|
Assert.Contains("钻石", result.List[0].Description);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetRewardsAsync_WithRewardIdFilter_ReturnsFilteredRewards()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
var request = new RewardListRequest { Page = 1, PageSize = 10, RewardId = "reward_001" };
|
|
|
|
// Act
|
|
var result = await _rewardService.GetRewardsAsync(request);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Equal(1, result.Total);
|
|
Assert.Equal("reward_001", result.List[0].RewardId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetRewardsAsync_WithPagination_ReturnsCorrectPage()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
var request = new RewardListRequest { Page = 1, PageSize = 2 };
|
|
|
|
// Act
|
|
var result = await _rewardService.GetRewardsAsync(request);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Equal(3, result.Total);
|
|
Assert.Equal(2, result.List.Count);
|
|
Assert.Equal(1, result.Page);
|
|
Assert.Equal(2, result.PageSize);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region GetRewardByIdAsync Tests
|
|
|
|
[Fact]
|
|
public async Task GetRewardByIdAsync_WithExistingId_ReturnsReward()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
|
|
// Act
|
|
var result = await _rewardService.GetRewardByIdAsync(1);
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Equal(1, result.Id);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetRewardByIdAsync_WithNonExistingId_ReturnsNull()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
|
|
// Act
|
|
var result = await _rewardService.GetRewardByIdAsync(999);
|
|
|
|
// Assert
|
|
Assert.Null(result);
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
#region GetRewardsByRewardIdAsync Tests
|
|
|
|
[Fact]
|
|
public async Task GetRewardsByRewardIdAsync_WithExistingRewardId_ReturnsRewards()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
|
|
// Act
|
|
var result = await _rewardService.GetRewardsByRewardIdAsync("reward_001");
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Single(result);
|
|
Assert.Equal("reward_001", result[0].RewardId);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetRewardsByRewardIdAsync_WithNonExistingRewardId_ReturnsEmptyList()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
|
|
// Act
|
|
var result = await _rewardService.GetRewardsByRewardIdAsync("non_existing");
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Empty(result);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task GetRewardsByRewardIdAsync_WithEmptyRewardId_ReturnsEmptyList()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
|
|
// Act
|
|
var result = await _rewardService.GetRewardsByRewardIdAsync("");
|
|
|
|
// Assert
|
|
Assert.NotNull(result);
|
|
Assert.Empty(result);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region CreateRewardAsync Tests
|
|
|
|
[Fact]
|
|
public async Task CreateRewardAsync_WithValidDiamondReward_CreatesReward()
|
|
{
|
|
// Arrange
|
|
var request = new RewardCreateRequest
|
|
{
|
|
RewardType = 1,
|
|
RewardValue = 100,
|
|
Description = "测试钻石奖励"
|
|
};
|
|
|
|
// Act
|
|
var id = await _rewardService.CreateRewardAsync(request);
|
|
|
|
// Assert
|
|
Assert.True(id > 0);
|
|
var reward = await _dbContext.Rewards.FindAsync(id);
|
|
Assert.NotNull(reward);
|
|
Assert.Equal(1, reward.RewardType);
|
|
Assert.Equal(100, reward.RewardValue);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateRewardAsync_WithValidCouponReward_CreatesReward()
|
|
{
|
|
// Arrange
|
|
var request = new RewardCreateRequest
|
|
{
|
|
RewardType = 4,
|
|
RewardExtend = 1,
|
|
RewardValue = 0,
|
|
Description = "测试优惠券奖励"
|
|
};
|
|
|
|
// Act
|
|
var id = await _rewardService.CreateRewardAsync(request);
|
|
|
|
// Assert
|
|
Assert.True(id > 0);
|
|
var reward = await _dbContext.Rewards.FindAsync(id);
|
|
Assert.NotNull(reward);
|
|
Assert.Equal(4, reward.RewardType);
|
|
Assert.Equal(1, reward.RewardExtend);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateRewardAsync_WithInvalidType_ThrowsException()
|
|
{
|
|
// Arrange
|
|
var request = new RewardCreateRequest
|
|
{
|
|
RewardType = 5,
|
|
RewardValue = 100
|
|
};
|
|
|
|
// Act & Assert
|
|
var ex = await Assert.ThrowsAsync<BusinessException>(
|
|
() => _rewardService.CreateRewardAsync(request));
|
|
Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateRewardAsync_WithCouponTypeButNoCouponId_ThrowsException()
|
|
{
|
|
// Arrange
|
|
var request = new RewardCreateRequest
|
|
{
|
|
RewardType = 4,
|
|
RewardValue = 0
|
|
};
|
|
|
|
// Act & Assert
|
|
var ex = await Assert.ThrowsAsync<BusinessException>(
|
|
() => _rewardService.CreateRewardAsync(request));
|
|
Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task CreateRewardAsync_WithNonCouponTypeButZeroValue_ThrowsException()
|
|
{
|
|
// Arrange
|
|
var request = new RewardCreateRequest
|
|
{
|
|
RewardType = 1,
|
|
RewardValue = 0
|
|
};
|
|
|
|
// Act & Assert
|
|
var ex = await Assert.ThrowsAsync<BusinessException>(
|
|
() => _rewardService.CreateRewardAsync(request));
|
|
Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region UpdateRewardAsync Tests
|
|
|
|
[Fact]
|
|
public async Task UpdateRewardAsync_WithExistingReward_UpdatesReward()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
var request = new RewardUpdateRequest
|
|
{
|
|
RewardType = 1,
|
|
RewardValue = 200,
|
|
Description = "更新后的描述"
|
|
};
|
|
|
|
// Act
|
|
var result = await _rewardService.UpdateRewardAsync(1, request);
|
|
|
|
// Assert
|
|
Assert.True(result);
|
|
var reward = await _dbContext.Rewards.FindAsync(1);
|
|
Assert.NotNull(reward);
|
|
Assert.Equal(200, reward.RewardValue);
|
|
Assert.Equal("更新后的描述", reward.Description);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task UpdateRewardAsync_WithNonExistingReward_ThrowsException()
|
|
{
|
|
// Arrange
|
|
var request = new RewardUpdateRequest
|
|
{
|
|
RewardType = 1,
|
|
RewardValue = 100
|
|
};
|
|
|
|
// Act & Assert
|
|
var ex = await Assert.ThrowsAsync<BusinessException>(
|
|
() => _rewardService.UpdateRewardAsync(999, request));
|
|
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region DeleteRewardAsync Tests
|
|
|
|
[Fact]
|
|
public async Task DeleteRewardAsync_WithExistingReward_DeletesReward()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
|
|
// Act
|
|
var result = await _rewardService.DeleteRewardAsync(1);
|
|
|
|
// Assert
|
|
Assert.True(result);
|
|
var reward = await _dbContext.Rewards.FindAsync(1);
|
|
Assert.Null(reward);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task DeleteRewardAsync_WithNonExistingReward_ThrowsException()
|
|
{
|
|
// Act & Assert
|
|
var ex = await Assert.ThrowsAsync<BusinessException>(
|
|
() => _rewardService.DeleteRewardAsync(999));
|
|
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
#region BatchUpdateRewardsAsync Tests
|
|
|
|
[Fact]
|
|
public async Task BatchUpdateRewardsAsync_WithValidRequest_UpdatesRewards()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
var request = new RewardBatchRequest
|
|
{
|
|
RewardId = "reward_001",
|
|
Rewards = new List<RewardItem>
|
|
{
|
|
new() { RewardType = 1, RewardValue = 50, Description = "新钻石奖励" },
|
|
new() { RewardType = 2, RewardValue = 100, Description = "新UU币奖励" }
|
|
}
|
|
};
|
|
|
|
// Act
|
|
var result = await _rewardService.BatchUpdateRewardsAsync(request);
|
|
|
|
// Assert
|
|
Assert.True(result);
|
|
var rewards = await _dbContext.Rewards.Where(r => r.RewardId == "reward_001").ToListAsync();
|
|
Assert.Equal(2, rewards.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BatchUpdateRewardsAsync_WithEmptyRewardId_ThrowsException()
|
|
{
|
|
// Arrange
|
|
var request = new RewardBatchRequest
|
|
{
|
|
RewardId = "",
|
|
Rewards = new List<RewardItem>()
|
|
};
|
|
|
|
// Act & Assert
|
|
var ex = await Assert.ThrowsAsync<BusinessException>(
|
|
() => _rewardService.BatchUpdateRewardsAsync(request));
|
|
Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task BatchUpdateRewardsAsync_WithEmptyRewardsList_DeletesExistingRewards()
|
|
{
|
|
// Arrange
|
|
await SeedTestRewards();
|
|
var request = new RewardBatchRequest
|
|
{
|
|
RewardId = "reward_001",
|
|
Rewards = new List<RewardItem>()
|
|
};
|
|
|
|
// Act
|
|
var result = await _rewardService.BatchUpdateRewardsAsync(request);
|
|
|
|
// Assert
|
|
Assert.True(result);
|
|
var rewards = await _dbContext.Rewards.Where(r => r.RewardId == "reward_001").ToListAsync();
|
|
Assert.Empty(rewards);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Helper Methods
|
|
|
|
private async Task SeedTestRewards()
|
|
{
|
|
var rewards = new List<Reward>
|
|
{
|
|
new()
|
|
{
|
|
Id = 1,
|
|
RewardId = "reward_001",
|
|
RewardType = 1,
|
|
RewardValue = 100,
|
|
Description = "钻石奖励",
|
|
CreatedAt = DateTime.Now,
|
|
UpdatedAt = DateTime.Now
|
|
},
|
|
new()
|
|
{
|
|
Id = 2,
|
|
RewardId = "reward_002",
|
|
RewardType = 2,
|
|
RewardValue = 50,
|
|
Description = "UU币奖励",
|
|
CreatedAt = DateTime.Now.AddMinutes(-1),
|
|
UpdatedAt = DateTime.Now.AddMinutes(-1)
|
|
},
|
|
new()
|
|
{
|
|
Id = 3,
|
|
RewardId = "reward_003",
|
|
RewardType = 3,
|
|
RewardValue = 30,
|
|
Description = "达达卷奖励",
|
|
CreatedAt = DateTime.Now.AddMinutes(-2),
|
|
UpdatedAt = DateTime.Now.AddMinutes(-2)
|
|
}
|
|
};
|
|
|
|
_dbContext.Rewards.AddRange(rewards);
|
|
await _dbContext.SaveChangesAsync();
|
|
}
|
|
|
|
#endregion
|
|
}
|