public async void UnexpectedExceptionThrownByRedisDuringRemove() { // Given var connectionMultplexerMock = new Mock <IConnectionMultiplexer>(); var configuration = new ConcurrentRequestLimiterConfiguration(); var loggerMock = new Mock <ILogger <RedisConcurrentRequestsManager> >(); connectionMultplexerMock.Setup(mock => mock.GetDatabase( Moq.It.IsAny <int>(), Moq.It.IsAny <object>())) .Throws <Exception>(); var manager = new RedisConcurrentRequestsManager( connectionMultplexerMock.Object, configuration, loggerMock.Object); // When a request is removed var result = await manager.RemoveAsync( Guid.NewGuid().ToString(), Guid.NewGuid().ToString()); // Then it should log a warning loggerMock.Verify(mock => mock.Log( LogLevel.Warning, Moq.It.IsAny <EventId>(), Moq.It.IsAny <FormattedLogValues>(), Moq.It.IsAny <Exception>(), Moq.It.IsAny <Func <object, Exception, string> >())); // And it should return false result.Should().BeFalse(); }
public async void MultipleConcurrentRequestsAddedUnderCapacity() { // Given const string ClientId = "tester"; var configuration = new ConcurrentRequestLimiterConfiguration(); var loggerMock = new Mock <ILogger <InMemoryConcurrentRequestsManager> >(); var manager = new InMemoryConcurrentRequestsManager(configuration, loggerMock.Object); // When multiple requests are added var tasks = new List <Task <AddConcurrentRequestResult> >(); tasks.Add(manager.AddAsync(ClientId, Guid.NewGuid().ToString(), DateTimeOffset.UtcNow.ToUnixTimeSeconds())); tasks.Add(manager.AddAsync(ClientId, Guid.NewGuid().ToString(), DateTimeOffset.UtcNow.ToUnixTimeSeconds())); var results = await Task.WhenAll(tasks); // Then the manager should allow first request results[0].IsAllowed.Should().BeTrue(); results[0].Limit.Should().Be(configuration.Capacity); results[0].Remaining.Should().Be(configuration.Capacity - 1); // And the manager should allow second request results[1].IsAllowed.Should().BeTrue(); results[1].Limit.Should().Be(configuration.Capacity); results[1].Remaining.Should().Be(configuration.Capacity - 2); }
/// <summary> /// Initializes a new instance of <see cref="InMemoryConcurrentRequestsManager"/> class. /// </summary> /// <param name="configuration"> /// The concurrent request limiter configuration. /// </param> /// <param name="logger"> /// The logger. /// </param> public InMemoryConcurrentRequestsManager( ConcurrentRequestLimiterConfiguration configuration, ILogger <InMemoryConcurrentRequestsManager> logger) { _configuration = configuration; _logger = logger; }
/// <summary> /// Initializes a new instance of <see cref="RedisConcurrentRequestsManager"/> /// class. /// </summary> /// <param name="redisClient"> /// The Redis API client. /// </param> /// <param name="configuration"> /// The concurrent request limiter configuration options. /// </param> /// <param name="logger"> /// The logger. /// </param> public RedisConcurrentRequestsManager( IConnectionMultiplexer redisClient, ConcurrentRequestLimiterConfiguration configuration, ILogger <RedisConcurrentRequestsManager> logger) { _redisClient = redisClient; _configuration = configuration; _logger = logger; }
public void ValidateWithDefaultConfiguration() { // Given var configuration = new ConcurrentRequestLimiterConfiguration(); // When configuration is validated var exception = Record.Exception(() => configuration.Validate()); // Then it should successfully validate exception.Should().BeNull(); }
public async void RemovingARequestWhichWasNeverAdded() { // Given const string ClientId = "tester"; var configuration = new ConcurrentRequestLimiterConfiguration(); var loggerMock = new Mock <ILogger <InMemoryConcurrentRequestsManager> >(); var manager = new InMemoryConcurrentRequestsManager(configuration, loggerMock.Object); // When a request is removed var result = await manager.RemoveAsync(ClientId, Guid.NewGuid().ToString()); // Then the manager should not remove anything result.Should().BeFalse(); }
public void ValidateWithNullKeyPrefix() { // Given var configuration = new ConcurrentRequestLimiterConfiguration { KeysPrefix = null }; // When configuration is validated var exception = Record.Exception(() => configuration.Validate()); // It should throw ArgumentNullException exception.Should().NotBeNull(); exception.Should().BeOfType <ArgumentNullException>(); exception.Message.Should().Contain("Keys prefix must be provided."); ((ArgumentNullException)exception).ParamName.Should().Be("KeysPrefix"); }
public void ValidateWithZeroTimeToLive() { // Given var configuration = new ConcurrentRequestLimiterConfiguration { RequestTimeToLive = 0 }; // When configuration is validated var exception = Record.Exception(() => configuration.Validate()); // Then it should throw ArgumentOutOfRangeException exception.Should().NotBeNull(); exception.Should().BeOfType <ArgumentOutOfRangeException>(); exception.Message.Should().Contain("Request time-to-live must be greater than 0."); ((ArgumentOutOfRangeException)exception).ParamName.Should().Be("RequestTimeToLive"); }
public async void APreviouslyAddedRequestIsRemoved() { // Given const string ClientId = "tester"; const string RequestId = "123"; var configuration = new ConcurrentRequestLimiterConfiguration(); var loggerMock = new Mock <ILogger <InMemoryConcurrentRequestsManager> >(); var manager = new InMemoryConcurrentRequestsManager(configuration, loggerMock.Object); await manager.AddAsync(ClientId, RequestId, DateTimeOffset.UtcNow.ToUnixTimeSeconds()); // When a request is removed var result = await manager.RemoveAsync(ClientId, RequestId); // Then the manager should remove the request result.Should().BeTrue(); }
public async void RequestIsNotRemoved() { // Given var connectionMultplexerMock = new Mock <IConnectionMultiplexer>(); var databaseMock = new Mock <IDatabase>(); var configuration = new ConcurrentRequestLimiterConfiguration(); var loggerMock = new Mock <ILogger <RedisConcurrentRequestsManager> >(); databaseMock.Setup(mock => mock.SortedSetRemoveAsync( Moq.It.IsAny <RedisKey>(), Moq.It.IsAny <RedisValue>(), Moq.It.IsAny <CommandFlags>())) .ReturnsAsync(false); connectionMultplexerMock.Setup(mock => mock.GetDatabase( Moq.It.IsAny <int>(), Moq.It.IsAny <object>())) .Returns(databaseMock.Object); var manager = new RedisConcurrentRequestsManager( connectionMultplexerMock.Object, configuration, loggerMock.Object); // When a request is removed var result = await manager.RemoveAsync( Guid.NewGuid().ToString(), Guid.NewGuid().ToString()); // Then it should log a debug message loggerMock.Verify(mock => mock.Log( LogLevel.Debug, Moq.It.IsAny <EventId>(), Moq.It.IsAny <FormattedLogValues>(), Moq.It.IsAny <Exception>(), Moq.It.IsAny <Func <object, Exception, string> >())); // And it should return false result.Should().BeFalse(); }
public async void ARequestIsAddedOverCapacity() { // Given const string ClientId = "tester"; var configuration = new ConcurrentRequestLimiterConfiguration(); var loggerMock = new Mock <ILogger <InMemoryConcurrentRequestsManager> >(); var manager = new InMemoryConcurrentRequestsManager(configuration, loggerMock.Object); for (var i = 0; i < configuration.Capacity; i++) { await manager.AddAsync(ClientId, Guid.NewGuid().ToString(), DateTimeOffset.UtcNow.ToUnixTimeSeconds()); } // When a request is added over capacity var result = await manager.AddAsync(ClientId, Guid.NewGuid().ToString(), DateTimeOffset.UtcNow.ToUnixTimeSeconds()); // Then the manager should not allow the request result.IsAllowed.Should().BeFalse(); result.Limit.Should().Be(configuration.Capacity); result.Remaining.Should().Be(0); }