267 lines
9.1 KiB
C#
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
|
|
}
|