diff --git a/AccessQueueService/Data/DictionaryAccessQueueRepo.cs b/AccessQueueService/Data/DictionaryAccessQueueRepo.cs deleted file mode 100644 index fc92f43..0000000 --- a/AccessQueueService/Data/DictionaryAccessQueueRepo.cs +++ /dev/null @@ -1,99 +0,0 @@ -using AccessQueueService.Models; -using Microsoft.Extensions.Configuration; - -namespace AccessQueueService.Data -{ - public class DictionaryAccessQueueRepo : IAccessQueueRepo - { - private readonly Dictionary _accessTickets = new(); - private readonly Queue _accessQueue = new(); - - public int GetUnexpiredTicketsCount() => _accessTickets.Count(t => t.Value.ExpiresOn > DateTime.UtcNow); - public int GetActiveTicketsCount(DateTime activeCutoff) => _accessTickets - .Count(t => t.Value.ExpiresOn > DateTime.UtcNow && t.Value.LastActive >activeCutoff); - public int GetQueueCount() => _accessQueue.Count; - public int GetRequestsAhead(Guid userId) - { - var index = 0; - foreach (var ticket in _accessQueue) - { - if (ticket.UserId == userId) - { - return index; - } - index++; - } - return -1; - } - - public void Enqueue(AccessTicket ticket) - { - _accessQueue.Enqueue(ticket); - } - - public int DeleteExpiredTickets() - { - var cutoff = DateTime.UtcNow; - var expiredTickets = _accessTickets.Where(t => t.Value.ExpiresOn < cutoff); - int count = 0; - foreach (var ticket in expiredTickets) - { - count++; - _accessTickets.Remove(ticket.Key); - } - return count; - } - - public void RemoveUser(Guid userId) - { - _accessTickets.Remove(userId); - } - - public bool DidDequeueUntilFull(int activeSeconds, int expirationSeconds, int capacityLimit) - { - var now = DateTime.UtcNow; - var activeCutoff = now.AddSeconds(-activeSeconds); - var numberOfActiveUsers = _accessTickets.Count(t => t.Value.ExpiresOn > now && t.Value.LastActive > activeCutoff); - var openSpots = capacityLimit - numberOfActiveUsers; - int filledSpots = 0; - while (filledSpots < openSpots) - { - if (_accessQueue.TryDequeue(out var nextUser)) - { - if (nextUser.LastActive < activeCutoff) - { - // User is inactive, throw away their ticket - continue; - } - _accessTickets[nextUser.UserId] = new AccessTicket - { - UserId = nextUser.UserId, - ExpiresOn = now.AddSeconds(expirationSeconds), - LastActive = now - }; - filledSpots++; - } - else - { - break; - } - } - return filledSpots == openSpots; - } - - public AccessTicket? GetTicket(Guid userId) - { - return _accessTickets.TryGetValue(userId, out var ticket) ? ticket : null; - } - - public void UpsertTicket(AccessTicket ticket) - { - _accessTickets[ticket.UserId] = ticket; - } - - bool IAccessQueueRepo.RemoveUser(Guid userId) - { - return _accessTickets.Remove(userId); - } - } -} diff --git a/AccessQueueService/Program.cs b/AccessQueueService/Program.cs index 818c166..746706c 100644 --- a/AccessQueueService/Program.cs +++ b/AccessQueueService/Program.cs @@ -10,7 +10,7 @@ builder.Services.AddControllers(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); builder.Services.AddSingleton(); -builder.Services.AddSingleton(); +builder.Services.AddSingleton(); var app = builder.Build(); diff --git a/AccessQueueServiceTests/AccessServiceTests.cs b/AccessQueueServiceTests/AccessServiceTests.cs index fc01359..9cf3be2 100644 --- a/AccessQueueServiceTests/AccessServiceTests.cs +++ b/AccessQueueServiceTests/AccessServiceTests.cs @@ -18,14 +18,9 @@ namespace AccessQueueServiceTests const int ACT_MILLIS = 1000 * ACT_SECONDS; const int CAP_LIMIT = 5; const int BULK_COUNT = 50000; - private AccessService _accessService; - public static IEnumerable RepoImplementations() - { - yield return new object[] { new DictionaryAccessQueueRepo() }; - yield return new object[] { new TakeANumberAccessQueueRepo() }; - } + private readonly AccessService _accessService; - private void CreateService(IAccessQueueRepo repo) + public AccessServiceTests() { var inMemorySettings = new Dictionary { @@ -38,16 +33,14 @@ namespace AccessQueueServiceTests var configuration = new ConfigurationBuilder() .AddInMemoryCollection(inMemorySettings) .Build(); + var accessQueueRepo = new TakeANumberAccessQueueRepo(); - - _accessService = new AccessService(configuration, repo); + _accessService = new AccessService(configuration, accessQueueRepo); } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RequestAccess_ShouldGrantAccess_WhenCapacityIsAvailable(IAccessQueueRepo repo) + [Fact] + public async Task RequestAccess_ShouldGrantAccess_WhenCapacityIsAvailable() { - CreateService(repo); var userId = Guid.NewGuid(); var response = await _accessService.RequestAccess(userId); @@ -60,11 +53,9 @@ namespace AccessQueueServiceTests Assert.Equal(0, _accessService.QueueCount); } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RequestAccess_ShouldReturnAccessResponse_WhenUserAlreadyHasTicket(IAccessQueueRepo repo) + [Fact] + public async Task RequestAccess_ShouldReturnAccessResponse_WhenUserAlreadyHasTicket() { - CreateService(repo); var userId = Guid.NewGuid(); await _accessService.RequestAccess(userId); @@ -78,11 +69,9 @@ namespace AccessQueueServiceTests Assert.Equal(0, _accessService.QueueCount); } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RequestAccess_ShouldQueueUser_WhenCapacityIsFull(IAccessQueueRepo repo) + [Fact] + public async Task RequestAccess_ShouldQueueUser_WhenCapacityIsFull() { - CreateService(repo); for (int i = 0; i < CAP_LIMIT * 2; i++) // Fill double capacity { await _accessService.RequestAccess(Guid.NewGuid()); @@ -100,11 +89,9 @@ namespace AccessQueueServiceTests } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RevokeAccess_ShouldReturnTrue_WhenUserHasAccess(IAccessQueueRepo repo) + [Fact] + public async Task RevokeAccess_ShouldReturnTrue_WhenUserHasAccess() { - CreateService(repo); var userId = Guid.NewGuid(); await _accessService.RequestAccess(userId); @@ -113,11 +100,9 @@ namespace AccessQueueServiceTests Assert.True(result); } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RevokeAccess_ShouldReturnFalse_WhenUserDoesNotHaveAccess(IAccessQueueRepo repo) + [Fact] + public async Task RevokeAccess_ShouldReturnFalse_WhenUserDoesNotHaveAccess() { - CreateService(repo); var userId = Guid.NewGuid(); var result = await _accessService.RevokeAccess(userId); @@ -125,11 +110,9 @@ namespace AccessQueueServiceTests Assert.False(result); } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RequestAccess_ShouldQueueUser_AfterAccessRevoked(IAccessQueueRepo repo) + [Fact] + public async Task RequestAccess_ShouldQueueUser_AfterAccessRevoked() { - CreateService(repo); var userId = Guid.NewGuid(); await _accessService.RequestAccess(userId); @@ -148,11 +131,9 @@ namespace AccessQueueServiceTests Assert.False(responseAfterRevoke.HasAccess); } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RequestAccess_ShouldNotQueueUser_WhenMultipleRequestsForOtherUsersMade(IAccessQueueRepo repo) + [Fact] + public async Task RequestAccess_ShouldNotQueueUser_WhenMultipleRequestsForOtherUsersMade() { - CreateService(repo); for (int i = 0; i < CAP_LIMIT; i++) // Fill slots without awaiting { _ = _accessService.RequestAccess(Guid.NewGuid()); @@ -162,11 +143,9 @@ namespace AccessQueueServiceTests Assert.False(response.HasAccess); } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RequestAccess_ShouldUpdateExpirationTime_WhenRollingExpirationTrue(IAccessQueueRepo repo) + [Fact] + public async Task RequestAccess_ShouldUpdateExpirationTime_WhenRollingExpirationTrue() { - CreateService(repo); var userId = Guid.NewGuid(); var initialResponse = await _accessService.RequestAccess(userId); await Task.Delay(ACT_MILLIS); @@ -174,11 +153,9 @@ namespace AccessQueueServiceTests Assert.True(updatedResponse.ExpiresOn > initialResponse.ExpiresOn); } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RequestAccess_ShouldGrantAccess_WhenUsersWithAccessInactive(IAccessQueueRepo repo) + [Fact] + public async Task RequestAccess_ShouldGrantAccess_WhenUsersWithAccessInactive() { - CreateService(repo); for (int i = 0; i < CAP_LIMIT; i++) { await _accessService.RequestAccess(Guid.NewGuid()); @@ -191,11 +168,9 @@ namespace AccessQueueServiceTests Assert.True(response.HasAccess); } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RequestAccess_ShouldRevokeAccess_WhenExpired(IAccessQueueRepo repo) + [Fact] + public async Task RequestAccess_ShouldRevokeAccess_WhenExpired() { - CreateService(repo); var userId = Guid.NewGuid(); var response = await _accessService.RequestAccess(userId); Assert.True(response.HasAccess); @@ -208,11 +183,9 @@ namespace AccessQueueServiceTests Assert.False(response.HasAccess); } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RequestAccess_ShouldRetailAccess_WhenNotExpired(IAccessQueueRepo repo) + [Fact] + public async Task RequestAccess_ShouldRetailAccess_WhenNotExpired() { - CreateService(repo); var userId = Guid.NewGuid(); var response = await _accessService.RequestAccess(userId); Assert.True(response.HasAccess); @@ -226,11 +199,9 @@ namespace AccessQueueServiceTests Assert.True(response.HasAccess); } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RequestAccess_ShouldProcessBulkRequests(IAccessQueueRepo repo) + [Fact] + public async Task RequestAccess_ShouldProcessBulkRequests() { - CreateService(repo); var userId = Guid.NewGuid(); await _accessService.RequestAccess(userId); for (int i = 0; i < BULK_COUNT; i++) @@ -242,11 +213,9 @@ namespace AccessQueueServiceTests Assert.True(response.HasAccess); } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RequestAccess_ShouldReportLessInQueue_AsTicketsInactivate(IAccessQueueRepo repo) + [Fact] + public async Task RequestAccess_ShouldReportLessInQueue_AsTicketsInactivate() { - CreateService(repo); var start = DateTime.UtcNow; for (int i = 0; i < CAP_LIMIT; i++) { @@ -276,11 +245,9 @@ namespace AccessQueueServiceTests Assert.Equal(0, response.RequestsAhead); } - [Theory] - [MemberData(nameof(RepoImplementations))] - public async Task RequestAccess_ShouldShowCorrectRequestsAhead_WhenAccessRerequested(IAccessQueueRepo repo) + [Fact] + public async Task RequestAccess_ShouldShowCorrectRequestsAhead_WhenAccessRerequested() { - CreateService(repo); for (int i = 0; i < CAP_LIMIT; i++) { await _accessService.RequestAccess(Guid.NewGuid()); @@ -297,7 +264,7 @@ namespace AccessQueueServiceTests Assert.Equal(0, response1.RequestsAhead); Assert.Equal(1, response2.RequestsAhead); Assert.Equal(2, response3.RequestsAhead); - + response1 = await _accessService.RequestAccess(id1); response2 = await _accessService.RequestAccess(id2); response3 = await _accessService.RequestAccess(id3); @@ -306,6 +273,7 @@ namespace AccessQueueServiceTests Assert.Equal(1, response2.RequestsAhead); Assert.Equal(2, response3.RequestsAhead); } + } } } \ No newline at end of file