Files
mic-check/tests/api/MicCheck.Api.Tests.Unit/Features/Usage/FeatureUsageControllerTests.cs
James Wampler 7a3e2167c2
All checks were successful
CI / build-and-push (push) Successful in 1m0s
CI / deploy-qa (push) Successful in 11s
CI / smoke-qa (push) Successful in 26s
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
2026-07-05 22:54:30 -07:00

94 lines
3.4 KiB
C#

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));
}
}