HaniBlindBox/server/HoneyBox/tests/HoneyBox.Tests/Services/DashboardServicePropertyTests.cs
2026-01-17 03:24:20 +08:00

267 lines
9.1 KiB
C#

using FsCheck;
using FsCheck.Xunit;
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>
/// DashboardService 属性测试
/// </summary>
public class DashboardServicePropertyTests
{
private readonly Mock<ILogger<DashboardService>> _mockLogger = new();
#region Property 15: Dashboard Statistics Accuracy
/// <summary>
/// **Feature: admin-business-migration, Property 15: Dashboard Statistics Accuracy**
/// For any dashboard overview request, the today's registrations count should match
/// the actual count of users registered today.
/// Validates: Requirements 8.1
/// </summary>
[Property(MaxTest = 100)]
public bool DashboardOverview_TodayRegistrations_ShouldMatchActualCount(PositiveInt todayCount, PositiveInt yesterdayCount)
{
var actualTodayCount = todayCount.Get % 20; // 0-19 users today
var actualYesterdayCount = yesterdayCount.Get % 20; // 0-19 users yesterday
var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
using var dbContext = new HoneyBoxDbContext(options);
var service = new DashboardService(dbContext, _mockLogger.Object);
// Seed today's users
var today = DateTime.Today;
for (int i = 1; i <= actualTodayCount; i++)
{
dbContext.Users.Add(new User
{
Id = i,
Uid = $"TODAY{i:D3}",
Nickname = $"今日用户{i}",
Mobile = $"1380013800{i}",
OpenId = $"openid_today_{i}",
HeadImg = $"http://test.com/head{i}.jpg",
CreatedAt = today.AddHours(i % 24),
UpdatedAt = DateTime.Now
});
}
// Seed yesterday's users
for (int i = 1; i <= actualYesterdayCount; i++)
{
var id = actualTodayCount + i;
dbContext.Users.Add(new User
{
Id = id,
Uid = $"YEST{i:D3}",
Nickname = $"昨日用户{i}",
Mobile = $"1390013900{i}",
OpenId = $"openid_yest_{i}",
HeadImg = $"http://test.com/head{id}.jpg",
CreatedAt = today.AddDays(-1).AddHours(i % 24),
UpdatedAt = DateTime.Now
});
}
dbContext.SaveChanges();
// Get dashboard overview
var result = service.GetOverviewAsync().GetAwaiter().GetResult();
// Verify today's registrations count
return result.TodayRegistrations == actualTodayCount;
}
/// <summary>
/// **Feature: admin-business-migration, Property 15: Dashboard Statistics Accuracy**
/// For any dashboard overview request, the today's consumption should match
/// the sum of prices from today's paid orders.
/// Validates: Requirements 8.2
/// </summary>
[Property(MaxTest = 100)]
public bool DashboardOverview_TodayConsumption_ShouldMatchActualSum(PositiveInt todayOrderCount, PositiveInt yesterdayOrderCount)
{
var actualTodayCount = todayOrderCount.Get % 15; // 0-14 orders today
var actualYesterdayCount = yesterdayOrderCount.Get % 15; // 0-14 orders yesterday
var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
using var dbContext = new HoneyBoxDbContext(options);
var service = new DashboardService(dbContext, _mockLogger.Object);
// Seed a user
dbContext.Users.Add(new User
{
Id = 1,
Uid = "U001",
Nickname = "测试用户",
Mobile = "13800138001",
OpenId = "openid1",
HeadImg = "http://test.com/head.jpg",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
});
dbContext.SaveChanges();
// Seed today's orders
var today = DateTime.Today;
decimal expectedTodayConsumption = 0;
for (int i = 1; i <= actualTodayCount; i++)
{
var price = 100 * i;
expectedTodayConsumption += price;
dbContext.Orders.Add(CreateOrder(1, $"TODAY{i:D3}", price, today.AddHours(i % 24)));
}
// Seed yesterday's orders
for (int i = 1; i <= actualYesterdayCount; i++)
{
var price = 200 * i;
dbContext.Orders.Add(CreateOrder(1, $"YEST{i:D3}", price, today.AddDays(-1).AddHours(i % 24)));
}
dbContext.SaveChanges();
// Get dashboard overview
var result = service.GetOverviewAsync().GetAwaiter().GetResult();
// Verify today's consumption
return result.TodayConsumption == expectedTodayConsumption;
}
/// <summary>
/// **Feature: admin-business-migration, Property 15: Dashboard Statistics Accuracy**
/// For any dashboard overview request, the total users count should match
/// the actual count of all users in the database.
/// Validates: Requirements 8.1
/// </summary>
[Property(MaxTest = 100)]
public bool DashboardOverview_TotalUsers_ShouldMatchActualCount(PositiveInt userCount)
{
var actualUserCount = (userCount.Get % 50) + 1; // 1-50 users
var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
using var dbContext = new HoneyBoxDbContext(options);
var service = new DashboardService(dbContext, _mockLogger.Object);
// Seed users
for (int i = 1; i <= actualUserCount; i++)
{
dbContext.Users.Add(new User
{
Id = i,
Uid = $"U{i:D3}",
Nickname = $"用户{i}",
Mobile = $"1380013800{i % 10}",
OpenId = $"openid{i}",
HeadImg = $"http://test.com/head{i}.jpg",
CreatedAt = DateTime.Now.AddDays(-i % 30),
UpdatedAt = DateTime.Now
});
}
dbContext.SaveChanges();
// Get dashboard overview
var result = service.GetOverviewAsync().GetAwaiter().GetResult();
// Verify total users count
return result.TotalUsers == actualUserCount;
}
/// <summary>
/// **Feature: admin-business-migration, Property 15: Dashboard Statistics Accuracy**
/// For any dashboard overview request, the total orders count should match
/// the actual count of paid orders in the database.
/// Validates: Requirements 8.2
/// </summary>
[Property(MaxTest = 100)]
public bool DashboardOverview_TotalOrders_ShouldMatchActualCount(PositiveInt paidCount, PositiveInt unpaidCount)
{
var actualPaidCount = paidCount.Get % 30; // 0-29 paid orders
var actualUnpaidCount = unpaidCount.Get % 20; // 0-19 unpaid orders
var options = new DbContextOptionsBuilder<HoneyBoxDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
using var dbContext = new HoneyBoxDbContext(options);
var service = new DashboardService(dbContext, _mockLogger.Object);
// Seed a user
dbContext.Users.Add(new User
{
Id = 1,
Uid = "U001",
Nickname = "测试用户",
Mobile = "13800138001",
OpenId = "openid1",
HeadImg = "http://test.com/head.jpg",
CreatedAt = DateTime.Now,
UpdatedAt = DateTime.Now
});
dbContext.SaveChanges();
// Seed paid orders (status = 1)
for (int i = 1; i <= actualPaidCount; i++)
{
dbContext.Orders.Add(CreateOrder(1, $"PAID{i:D3}", 100, DateTime.Now, 1));
}
// Seed unpaid orders (status = 0)
for (int i = 1; i <= actualUnpaidCount; i++)
{
dbContext.Orders.Add(CreateOrder(1, $"UNPAID{i:D3}", 100, DateTime.Now, 0));
}
dbContext.SaveChanges();
// Get dashboard overview
var result = service.GetOverviewAsync().GetAwaiter().GetResult();
// Verify total orders count (only paid orders)
return result.TotalOrders == actualPaidCount;
}
#endregion
#region Helper Methods
private Order CreateOrder(int userId, string orderNum, decimal price, DateTime createdAt, byte status = 1)
{
return new Order
{
UserId = userId,
OrderNum = orderNum,
Price = price,
UseMoney = 0,
UseIntegral = 0,
UseScore = 0,
Status = status,
CreatedAt = createdAt,
UpdatedAt = DateTime.Now,
GoodsId = 1,
GoodsTitle = "商品",
GoodsPrice = price,
OrderTotal = price,
OrderZheTotal = price,
Zhe = 1,
Num = 1,
PrizeNum = 1,
Addtime = (int)DateTimeOffset.Now.ToUnixTimeSeconds()
};
}
#endregion
}