using FluentAssertions; using StellaOps.Router.Gateway.RateLimit; using Xunit; namespace StellaOps.Router.Gateway.Tests; public sealed class InMemoryValkeyRateLimitStoreTests { [Fact] public async Task IncrementAndCheckAsync_UsesSmallestWindowAsRepresentativeWhenAllowed() { var store = new InMemoryValkeyRateLimitStore(); var rules = new[] { new RateLimitRule { PerSeconds = 3600, MaxRequests = 1000 }, new RateLimitRule { PerSeconds = 60, MaxRequests = 10 }, }; var result = await store.IncrementAndCheckAsync("svc", rules, CancellationToken.None); result.Allowed.Should().BeTrue(); result.WindowSeconds.Should().Be(60); result.Limit.Should().Be(10); result.CurrentCount.Should().Be(1); result.RetryAfterSeconds.Should().Be(0); } [Fact] public async Task IncrementAndCheckAsync_DeniesWhenLimitExceeded() { var store = new InMemoryValkeyRateLimitStore(); var rules = new[] { new RateLimitRule { PerSeconds = 300, MaxRequests = 1 }, new RateLimitRule { PerSeconds = 3600, MaxRequests = 1000 }, }; (await store.IncrementAndCheckAsync("svc", rules, CancellationToken.None)).Allowed.Should().BeTrue(); var denied = await store.IncrementAndCheckAsync("svc", rules, CancellationToken.None); denied.Allowed.Should().BeFalse(); denied.WindowSeconds.Should().Be(300); denied.Limit.Should().Be(1); denied.CurrentCount.Should().Be(2); denied.RetryAfterSeconds.Should().BeGreaterThan(0); } }