Remove FluentValidation; validator/DI cleanup (#7)
## Summary - Remove FluentValidation dependency; replace with plain IModelValidator<T>/ValidationResult pattern (ported from BuyEngine's Guard/validation approach), wired via a ModelValidationActionFilter - Move FeatureUsage code into MicCheck.Api.Features.Usage namespace - Convert logic-free data classes to records per CLAUDE.md (WebhookEvent, FeatureStateResult, ProjectPermissionRequirement, AuditLog, Tag, FeatureUsageDaily) - Extract IAuditService interface, drop virtual-method mock seam on AuditService - Split Program.cs DI registrations into per-namespace DependencyRegistration classes ## Test plan - [x] dotnet build (API + tests) clean - [x] dotnet test MicCheck.Api.Tests.Unit — 646/646 passing - [x] pre-push hook (full solution build/test + admin build) passed Co-authored-by: miccheck-ci <ci@miccheck.local> Reviewed-on: #7
This commit was merged in pull request #7.
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
using MicCheck.Api.Data;
|
||||
using MicCheck.Api.Features.Usage;
|
||||
using MicCheck.Api.Tests.Unit.TestSupport;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace MicCheck.Api.Tests.Unit.Features.Usage;
|
||||
|
||||
[TestFixture]
|
||||
public class FeatureUsageControllerTests
|
||||
{
|
||||
private Mock<IMicCheckDbContext> _db = null!;
|
||||
private List<FeatureUsageDaily> _usage = null!;
|
||||
private IMemoryCache _cache = null!;
|
||||
private FeatureUsageController _controller = null!;
|
||||
private const int EnvironmentId = 1;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_db = new Mock<IMicCheckDbContext>();
|
||||
_usage = [];
|
||||
_db.SetupDbSet(c => c.FeatureUsageDaily, _usage);
|
||||
_cache = new MemoryCache(new MemoryCacheOptions());
|
||||
|
||||
_controller = new FeatureUsageController(new FeatureUsageQueryService(_db.Object), _cache);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task WhenGettingDashboardUsageWithNoData_ThenEmptyResultsAreReturned()
|
||||
{
|
||||
var result = await _controller.GetDashboardUsage(EnvironmentId);
|
||||
|
||||
var ok = result.Result as OkObjectResult;
|
||||
var response = (DashboardUsageResponse)ok!.Value!;
|
||||
Assert.That(response.TopFeaturesLastDay, Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task WhenGettingDashboardUsageASecondTime_ThenTheCachedResultIsReturnedWithoutQueryingAgain()
|
||||
{
|
||||
var first = await _controller.GetDashboardUsage(EnvironmentId);
|
||||
_usage.Add(new FeatureUsageDaily
|
||||
{
|
||||
EnvironmentId = EnvironmentId,
|
||||
FeatureId = 1,
|
||||
FeatureName = "flag",
|
||||
UsageDate = DateOnly.FromDateTime(DateTime.UtcNow),
|
||||
Count = 100,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
});
|
||||
|
||||
var second = await _controller.GetDashboardUsage(EnvironmentId);
|
||||
|
||||
var firstOk = (DashboardUsageResponse)((OkObjectResult)first.Result!).Value!;
|
||||
var secondOk = (DashboardUsageResponse)((OkObjectResult)second.Result!).Value!;
|
||||
Assert.That(secondOk, Is.SameAs(firstOk));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task WhenGettingDashboardUsageForDifferentDayRanges_ThenEachRangeIsCachedSeparately()
|
||||
{
|
||||
var sevenDays = await _controller.GetDashboardUsage(EnvironmentId, days: 7);
|
||||
var fourteenDays = await _controller.GetDashboardUsage(EnvironmentId, days: 14);
|
||||
|
||||
var sevenOk = (DashboardUsageResponse)((OkObjectResult)sevenDays.Result!).Value!;
|
||||
var fourteenOk = (DashboardUsageResponse)((OkObjectResult)fourteenDays.Result!).Value!;
|
||||
Assert.That(sevenOk, Is.Not.SameAs(fourteenOk));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task WhenDaysExceedsTheMaximum_ThenItIsClampedTo90()
|
||||
{
|
||||
_usage.Add(new FeatureUsageDaily
|
||||
{
|
||||
EnvironmentId = EnvironmentId,
|
||||
FeatureId = 1,
|
||||
FeatureName = "flag",
|
||||
UsageDate = DateOnly.FromDateTime(DateTime.UtcNow),
|
||||
Count = 5,
|
||||
UpdatedAt = DateTimeOffset.UtcNow
|
||||
});
|
||||
|
||||
var clamped = await _controller.GetDashboardUsage(EnvironmentId, days: 1000);
|
||||
var explicitNinety = await _controller.GetDashboardUsage(EnvironmentId, days: 90);
|
||||
|
||||
var clampedOk = (DashboardUsageResponse)((OkObjectResult)clamped.Result!).Value!;
|
||||
var explicitOk = (DashboardUsageResponse)((OkObjectResult)explicitNinety.Result!).Value!;
|
||||
Assert.That(clampedOk.DailyUsage.Count, Is.EqualTo(explicitOk.DailyUsage.Count));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user