using System.Diagnostics.Metrics; using MicCheck.Api.Features.Usage; using Moq; using NUnit.Framework; namespace MicCheck.Api.Tests.Unit.Features.Usage; [TestFixture] public class FeatureUsageMetricsTests { private FeatureUsageMetrics _metrics = null!; [SetUp] public void SetUp() { var meterFactory = new Mock(); meterFactory.Setup(f => f.Create(It.IsAny())).Returns(new Meter("test")); _metrics = new FeatureUsageMetrics(meterFactory.Object); } [TearDown] public void TearDown() => _metrics.Dispose(); [Test] public void WhenRecordingEvaluations_ThenAccumulatorIncrementsByFeatureAndEnvironment() { _metrics.RecordEvaluation(environmentId: 1, featureId: 10, featureName: "dark_mode"); _metrics.RecordEvaluation(environmentId: 1, featureId: 10, featureName: "dark_mode"); _metrics.RecordEvaluation(environmentId: 1, featureId: 20, featureName: "beta"); _metrics.RecordEvaluation(environmentId: 2, featureId: 10, featureName: "dark_mode"); var drained = _metrics.DrainAccumulated(); Assert.That(drained, Has.Count.EqualTo(3)); var today = DateOnly.FromDateTime(DateTime.UtcNow); Assert.That(drained[new FeatureUsageBucketKey(1, 10, "dark_mode", today)], Is.EqualTo(2)); Assert.That(drained[new FeatureUsageBucketKey(1, 20, "beta", today)], Is.EqualTo(1)); Assert.That(drained[new FeatureUsageBucketKey(2, 10, "dark_mode", today)], Is.EqualTo(1)); } [Test] public void WhenDrainCalled_ThenAccumulatorIsResetToEmpty() { _metrics.RecordEvaluation(environmentId: 1, featureId: 10, featureName: "dark_mode"); _metrics.DrainAccumulated(); var secondDrain = _metrics.DrainAccumulated(); Assert.That(secondDrain, Is.Empty); } [Test] public void WhenNothingRecorded_ThenDrainReturnsEmptyDictionary() { var drained = _metrics.DrainAccumulated(); Assert.That(drained, Is.Empty); } [Test] public void WhenRecordingConcurrently_ThenCountsAreAccurateAndNoDataLost() { const int threads = 8; const int recordsPerThread = 100; var tasks = Enumerable.Range(0, threads).Select(_ => Task.Run(() => { for (var i = 0; i < recordsPerThread; i++) _metrics.RecordEvaluation(environmentId: 1, featureId: 1, featureName: "concurrent_flag"); })).ToArray(); Task.WaitAll(tasks); var drained = _metrics.DrainAccumulated(); var today = DateOnly.FromDateTime(DateTime.UtcNow); Assert.That(drained[new FeatureUsageBucketKey(1, 1, "concurrent_flag", today)], Is.EqualTo(threads * recordsPerThread)); } }