using Omika.Core.Configuration; using Omika.Core.Observability; namespace Omika.Tests.Observability; public class RateLimitTests { [Fact] public void Storm_IdenticalEvents_IsRateLimited_AndBounded() { // NOTE : RateLimitPerEvent est volontairement abaissé à 100 (défaut = 1000) // pour rendre le test de tempête rapide et lisible. La fenêtre fixe est // de 1 seconde ; 100 000 occurrences mono-thread → ~100 acceptées, le // reste abandonné par la protection anti-log-storm. var options = new ObservabilityOptions { RateLimitPerEvent = 100 }; var (sink, _, counters) = ObservabilityTestHarness.CreatePipeline(options); const long total = 100_000; for (long i = 0; i < total; i++) { sink.Track(ObservabilityTestHarness.NewEvent(TelemetryEventCodes.MobileAppStarted)); } // Tous les événements sont comptabilisés une et une seule fois. Assert.Equal(total, counters.Accepted + counters.DroppedTotal); // La protection rate limit a bien abandonné des occurrences. Assert.True(counters.DroppedRateLimited > 0); // La mémoire est bornée : on n'accepte qu'une fraction du flux. Assert.True(counters.Accepted <= total); } [Fact] public void ExactRateLimit_SingleThreaded_1000Events_RateLimit100() { // Fenêtre fixe = 1 s, exécution séquentielle mono-thread (aucune course). // Le limiter accepte exactement les 100 premières occurrences puis droppe // les 900 suivantes : le résultat est déterministe dans ce cadre. var options = new ObservabilityOptions { RateLimitPerEvent = 100 }; var (sink, _, counters) = ObservabilityTestHarness.CreatePipeline(options); for (int i = 0; i < 1000; i++) { sink.Track(ObservabilityTestHarness.NewEvent(TelemetryEventCodes.MobileAppStarted)); } Assert.Equal(100L, counters.Accepted); Assert.Equal(900L, counters.DroppedRateLimited); Assert.Equal(1000L, counters.Accepted + counters.DroppedTotal); } [Fact] public void ManyDistinctCodes_AreBounded_NoThrow() { var options = new ObservabilityOptions(); var (sink, _, counters) = ObservabilityTestHarness.CreatePipeline(options); // Plus de codes distincts que la capacité interne de suivi (1024) : les // codes excédentaires sont abandonnés sans exception, la mémoire reste bornée. for (int i = 0; i < 5000; i++) { EventCode code = EventCode.Create("MOB", "T" + i.ToString("D7"), 1); sink.Track(ObservabilityTestHarness.NewEvent(code)); } Assert.Equal(5000L, counters.Accepted + counters.DroppedTotal); Assert.True(counters.DroppedRateLimited > 0); } }