using HoneyBox.Admin.Business.Models;
using HoneyBox.Admin.Business.Models.Reward;
using HoneyBox.Admin.Business.Models.SignConfig;
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;
///
/// SignConfigService 单元测试
///
public class SignConfigServiceTests : IDisposable
{
private readonly HoneyBoxDbContext _dbContext;
private readonly Mock> _mockLogger;
private readonly SignConfigService _signConfigService;
public SignConfigServiceTests()
{
// 使用 InMemory 数据库
var options = new DbContextOptionsBuilder()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
_dbContext = new HoneyBoxDbContext(options);
_mockLogger = new Mock>();
_signConfigService = new SignConfigService(_dbContext, _mockLogger.Object);
}
public void Dispose()
{
_dbContext.Dispose();
}
#region GetSignConfigsAsync Tests
[Fact]
public async Task GetSignConfigsAsync_WithDailyType_ReturnsDailyConfigs()
{
// Arrange
await SeedTestSignConfigs();
var request = new SignConfigListRequest { Page = 1, PageSize = 10, Type = 1 };
// Act
var result = await _signConfigService.GetSignConfigsAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Total);
Assert.All(result.List, s => Assert.Equal(1, s.Type));
}
[Fact]
public async Task GetSignConfigsAsync_WithCumulativeType_ReturnsCumulativeConfigs()
{
// Arrange
await SeedTestSignConfigs();
var request = new SignConfigListRequest { Page = 1, PageSize = 10, Type = 2 };
// Act
var result = await _signConfigService.GetSignConfigsAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(1, result.Total);
Assert.All(result.List, s => Assert.Equal(2, s.Type));
}
[Fact]
public async Task GetSignConfigsAsync_WithKeywordFilter_ReturnsFilteredConfigs()
{
// Arrange
await SeedTestSignConfigs();
var request = new SignConfigListRequest { Page = 1, PageSize = 10, Type = 1, Keyword = "第1天" };
// Act
var result = await _signConfigService.GetSignConfigsAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(1, result.Total);
Assert.Contains("第1天", result.List[0].Title);
}
[Fact]
public async Task GetSignConfigsAsync_WithPagination_ReturnsCorrectPage()
{
// Arrange
await SeedTestSignConfigs();
var request = new SignConfigListRequest { Page = 1, PageSize = 1, Type = 1 };
// Act
var result = await _signConfigService.GetSignConfigsAsync(request);
// Assert
Assert.NotNull(result);
Assert.Equal(2, result.Total);
Assert.Single(result.List);
Assert.Equal(1, result.Page);
Assert.Equal(1, result.PageSize);
}
[Fact]
public async Task GetSignConfigsAsync_WithRewards_ReturnsConfigsWithRewards()
{
// Arrange
await SeedTestSignConfigsWithRewards();
var request = new SignConfigListRequest { Page = 1, PageSize = 10, Type = 1 };
// Act
var result = await _signConfigService.GetSignConfigsAsync(request);
// Assert
Assert.NotNull(result);
Assert.True(result.List.Count > 0);
Assert.True(result.List[0].Rewards.Count > 0);
}
#endregion
#region GetSignConfigByIdAsync Tests
[Fact]
public async Task GetSignConfigByIdAsync_WithExistingId_ReturnsConfig()
{
// Arrange
await SeedTestSignConfigs();
// Act
var result = await _signConfigService.GetSignConfigByIdAsync(1);
// Assert
Assert.NotNull(result);
Assert.Equal(1, result.Id);
}
[Fact]
public async Task GetSignConfigByIdAsync_WithNonExistingId_ReturnsNull()
{
// Arrange
await SeedTestSignConfigs();
// Act
var result = await _signConfigService.GetSignConfigByIdAsync(999);
// Assert
Assert.Null(result);
}
#endregion
#region CreateSignConfigAsync Tests
[Fact]
public async Task CreateSignConfigAsync_WithValidRequest_CreatesConfig()
{
// Arrange
var request = new SignConfigCreateRequest
{
Type = 1,
Day = 1,
Title = "测试签到配置",
Icon = "https://example.com/icon.png",
Sort = 1,
Description = "测试描述",
Rewards = new List
{
new() { RewardType = 1, RewardValue = 100, Description = "钻石奖励" }
}
};
// Act
var id = await _signConfigService.CreateSignConfigAsync(request);
// Assert
Assert.True(id > 0);
var config = await _dbContext.SignConfigs.FindAsync(id);
Assert.NotNull(config);
Assert.Equal((byte)1, config.Type);
Assert.Equal((byte)1, config.Day);
Assert.Equal("测试签到配置", config.Title);
}
[Fact]
public async Task CreateSignConfigAsync_WithInvalidType_ThrowsException()
{
// Arrange
var request = new SignConfigCreateRequest
{
Type = 3,
Day = 1,
Title = "测试签到配置"
};
// Act & Assert
var ex = await Assert.ThrowsAsync(
() => _signConfigService.CreateSignConfigAsync(request));
Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
}
[Fact]
public async Task CreateSignConfigAsync_WithEmptyTitle_ThrowsException()
{
// Arrange
var request = new SignConfigCreateRequest
{
Type = 1,
Day = 1,
Title = ""
};
// Act & Assert
var ex = await Assert.ThrowsAsync(
() => _signConfigService.CreateSignConfigAsync(request));
Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
}
[Fact]
public async Task CreateSignConfigAsync_WithZeroDay_ThrowsException()
{
// Arrange
var request = new SignConfigCreateRequest
{
Type = 1,
Day = 0,
Title = "测试签到配置"
};
// Act & Assert
var ex = await Assert.ThrowsAsync(
() => _signConfigService.CreateSignConfigAsync(request));
Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
}
#endregion
#region UpdateSignConfigAsync Tests
[Fact]
public async Task UpdateSignConfigAsync_WithExistingConfig_UpdatesConfig()
{
// Arrange
await SeedTestSignConfigs();
var request = new SignConfigUpdateRequest
{
Type = 1,
Day = 2,
Title = "更新后的标题",
Icon = "https://example.com/new-icon.png",
Sort = 10,
Description = "更新后的描述"
};
// Act
var result = await _signConfigService.UpdateSignConfigAsync(1, request);
// Assert
Assert.True(result);
var config = await _dbContext.SignConfigs.FindAsync(1);
Assert.NotNull(config);
Assert.Equal((byte)2, config.Day);
Assert.Equal("更新后的标题", config.Title);
}
[Fact]
public async Task UpdateSignConfigAsync_WithNonExistingConfig_ThrowsException()
{
// Arrange
var request = new SignConfigUpdateRequest
{
Type = 1,
Day = 1,
Title = "测试"
};
// Act & Assert
var ex = await Assert.ThrowsAsync(
() => _signConfigService.UpdateSignConfigAsync(999, request));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
#endregion
#region DeleteSignConfigAsync Tests
[Fact]
public async Task DeleteSignConfigAsync_WithExistingConfig_DeletesConfig()
{
// Arrange
await SeedTestSignConfigs();
// Act
var result = await _signConfigService.DeleteSignConfigAsync(1);
// Assert
Assert.True(result);
var config = await _dbContext.SignConfigs.FindAsync(1);
Assert.Null(config);
}
[Fact]
public async Task DeleteSignConfigAsync_WithNonExistingConfig_ThrowsException()
{
// Act & Assert
var ex = await Assert.ThrowsAsync(
() => _signConfigService.DeleteSignConfigAsync(999));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
[Fact]
public async Task DeleteSignConfigAsync_WithRewards_DeletesConfigAndRewards()
{
// Arrange
await SeedTestSignConfigsWithRewards();
var configBefore = await _dbContext.SignConfigs.FindAsync(1);
var rewardsBefore = await _dbContext.Rewards.Where(r => r.RewardId == configBefore!.RewardId).ToListAsync();
Assert.NotEmpty(rewardsBefore);
// Act
var result = await _signConfigService.DeleteSignConfigAsync(1);
// Assert
Assert.True(result);
var config = await _dbContext.SignConfigs.FindAsync(1);
Assert.Null(config);
var rewardsAfter = await _dbContext.Rewards.Where(r => r.RewardId == configBefore!.RewardId).ToListAsync();
Assert.Empty(rewardsAfter);
}
#endregion
#region UpdateSignConfigStatusAsync Tests
[Fact]
public async Task UpdateSignConfigStatusAsync_WithValidStatus_UpdatesStatus()
{
// Arrange
await SeedTestSignConfigs();
// Act
var result = await _signConfigService.UpdateSignConfigStatusAsync(1, 0);
// Assert
Assert.True(result);
var config = await _dbContext.SignConfigs.FindAsync(1);
Assert.NotNull(config);
Assert.Equal(0, config.Status);
}
[Fact]
public async Task UpdateSignConfigStatusAsync_WithInvalidStatus_ThrowsException()
{
// Arrange
await SeedTestSignConfigs();
// Act & Assert
var ex = await Assert.ThrowsAsync(
() => _signConfigService.UpdateSignConfigStatusAsync(1, 2));
Assert.Equal(BusinessErrorCodes.ValidationFailed, ex.Code);
}
[Fact]
public async Task UpdateSignConfigStatusAsync_WithNonExistingConfig_ThrowsException()
{
// Act & Assert
var ex = await Assert.ThrowsAsync(
() => _signConfigService.UpdateSignConfigStatusAsync(999, 1));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
#endregion
#region UpdateSignConfigSortAsync Tests
[Fact]
public async Task UpdateSignConfigSortAsync_WithValidSort_UpdatesSort()
{
// Arrange
await SeedTestSignConfigs();
// Act
var result = await _signConfigService.UpdateSignConfigSortAsync(1, 100);
// Assert
Assert.True(result);
var config = await _dbContext.SignConfigs.FindAsync(1);
Assert.NotNull(config);
Assert.Equal(100, config.Sort);
}
[Fact]
public async Task UpdateSignConfigSortAsync_WithNonExistingConfig_ThrowsException()
{
// Act & Assert
var ex = await Assert.ThrowsAsync(
() => _signConfigService.UpdateSignConfigSortAsync(999, 1));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
#endregion
#region UpdateSignConfigRewardAsync Tests
[Fact]
public async Task UpdateSignConfigRewardAsync_WithValidRequest_UpdatesRewards()
{
// Arrange
await SeedTestSignConfigsWithRewards();
var request = new SignConfigRewardRequest
{
Rewards = new List
{
new() { RewardType = 1, RewardValue = 200, Description = "新钻石奖励" },
new() { RewardType = 2, RewardValue = 100, Description = "新UU币奖励" }
}
};
// Act
var result = await _signConfigService.UpdateSignConfigRewardAsync(1, request);
// Assert
Assert.True(result);
var config = await _dbContext.SignConfigs.FindAsync(1);
var rewards = await _dbContext.Rewards.Where(r => r.RewardId == config!.RewardId).ToListAsync();
Assert.Equal(2, rewards.Count);
}
[Fact]
public async Task UpdateSignConfigRewardAsync_WithEmptyRewards_DeletesExistingRewards()
{
// Arrange
await SeedTestSignConfigsWithRewards();
var request = new SignConfigRewardRequest
{
Rewards = new List()
};
// Act
var result = await _signConfigService.UpdateSignConfigRewardAsync(1, request);
// Assert
Assert.True(result);
var config = await _dbContext.SignConfigs.FindAsync(1);
var rewards = await _dbContext.Rewards.Where(r => r.RewardId == config!.RewardId).ToListAsync();
Assert.Empty(rewards);
}
[Fact]
public async Task UpdateSignConfigRewardAsync_WithNonExistingConfig_ThrowsException()
{
// Arrange
var request = new SignConfigRewardRequest
{
Rewards = new List()
};
// Act & Assert
var ex = await Assert.ThrowsAsync(
() => _signConfigService.UpdateSignConfigRewardAsync(999, request));
Assert.Equal(BusinessErrorCodes.NotFound, ex.Code);
}
#endregion
#region Helper Methods
private async Task SeedTestSignConfigs()
{
var configs = new List
{
new()
{
Id = 1,
Type = 1,
Day = 1,
Title = "第1天签到",
Icon = "https://example.com/icon1.png",
Status = 1,
Sort = 1,
RewardId = "sign_001",
Description = "第1天签到奖励",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
},
new()
{
Id = 2,
Type = 1,
Day = 2,
Title = "第2天签到",
Icon = "https://example.com/icon2.png",
Status = 1,
Sort = 2,
RewardId = "sign_002",
Description = "第2天签到奖励",
CreatedAt = DateTime.Now.AddMinutes(-1),
UpdatedAt = DateTime.Now.AddMinutes(-1)
},
new()
{
Id = 3,
Type = 2,
Day = 7,
Title = "累计7天签到",
Icon = "https://example.com/icon3.png",
Status = 1,
Sort = 1,
RewardId = "sign_003",
Description = "累计7天签到奖励",
CreatedAt = DateTime.Now.AddMinutes(-2),
UpdatedAt = DateTime.Now.AddMinutes(-2)
}
};
_dbContext.SignConfigs.AddRange(configs);
await _dbContext.SaveChangesAsync();
}
private async Task SeedTestSignConfigsWithRewards()
{
var configs = new List
{
new()
{
Id = 1,
Type = 1,
Day = 1,
Title = "第1天签到",
Icon = "https://example.com/icon1.png",
Status = 1,
Sort = 1,
RewardId = "sign_reward_001",
Description = "第1天签到奖励",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
}
};
var rewards = new List
{
new()
{
Id = 100,
RewardId = "sign_reward_001",
RewardType = 1,
RewardValue = 100,
Description = "钻石奖励",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
},
new()
{
Id = 101,
RewardId = "sign_reward_001",
RewardType = 2,
RewardValue = 50,
Description = "UU币奖励",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
}
};
_dbContext.SignConfigs.AddRange(configs);
_dbContext.Rewards.AddRange(rewards);
await _dbContext.SaveChangesAsync();
}
#endregion
}