Compare commits
7 Commits
44a3628489
...
5dbde2c847
Author | SHA1 | Date |
---|---|---|
|
5dbde2c847 | |
|
295c91a05c | |
|
7b0478539e | |
|
036168359b | |
|
7d4c6ab16c | |
|
449b821633 | |
|
d6aa832f61 |
|
@ -10,14 +10,53 @@
|
||||||
<Button Color="ButtonColor.Success" @onclick="AddUser">Add User</Button>
|
<Button Color="ButtonColor.Success" @onclick="AddUser">Add User</Button>
|
||||||
<Button Color="ButtonColor.Primary" @onclick="Refresh">Refresh</Button>
|
<Button Color="ButtonColor.Primary" @onclick="Refresh">Refresh</Button>
|
||||||
|
|
||||||
@if(Status != null)
|
@if (Status != null)
|
||||||
{
|
{
|
||||||
@foreach(var user in Status.Users)
|
<h2>Users with access</h2>
|
||||||
{
|
<Grid TItem="User" Data="Status.AccessUsers" Class="table table-bordered mt-3" AllowSorting>
|
||||||
<div>
|
<GridColumns>
|
||||||
<p>@user.Id @user.LatestResponse?.HasAccess @user.LatestResponse?.ExpiresOn</p>
|
<GridColumn TItem="User" HeaderText="Id" PropertyName="Id" SortKeySelector="item => item.Id">
|
||||||
</div>
|
@context.Id
|
||||||
}
|
</GridColumn>
|
||||||
|
<GridColumn TItem="User" HeaderText="Expiration" PropertyName="LatestResponse?.ExpiresOn" SortKeySelector="item => item.LatestResponse.ExpiresOn">
|
||||||
|
@context.LatestResponse?.ExpiresOn
|
||||||
|
</GridColumn>
|
||||||
|
<GridColumn TItem="User" HeaderText="Active">
|
||||||
|
<ChildContent>
|
||||||
|
<Switch Value="context.Active" ValueExpression="() => context.Active" ValueChanged="(_) => ToggleUserActive(context.Id)" />
|
||||||
|
</ChildContent>
|
||||||
|
</GridColumn>
|
||||||
|
</GridColumns>
|
||||||
|
</Grid>
|
||||||
|
<h2>Users in queue</h2>
|
||||||
|
<Grid TItem="User" Data="Status.QueuedUsers" Class="table table-bordered mt-3" AllowSorting>
|
||||||
|
<GridColumns>
|
||||||
|
<GridColumn TItem="User" HeaderText="Id" PropertyName="Id" SortKeySelector="item => item.Id">
|
||||||
|
@context.Id
|
||||||
|
</GridColumn>
|
||||||
|
<GridColumn TItem="User" HeaderText="Queue Postition" PropertyName="LatestResponse?.RequestsAhead">
|
||||||
|
@(context.LatestResponse?.RequestsAhead ?? 0 + 1)
|
||||||
|
</GridColumn>
|
||||||
|
<GridColumn TItem="User" HeaderText="Active">
|
||||||
|
<ChildContent>
|
||||||
|
<Switch Value="context.Active" ValueExpression="() => context.Active" ValueChanged="(_) => ToggleUserActive(context.Id)" />
|
||||||
|
</ChildContent>
|
||||||
|
</GridColumn>
|
||||||
|
</GridColumns>
|
||||||
|
</Grid>
|
||||||
|
<h2>Inactive users</h2>
|
||||||
|
<Grid TItem="User" Data="Status.InactiveUsers" Class="table table-bordered mt-3" AllowSorting>
|
||||||
|
<GridColumns>
|
||||||
|
<GridColumn TItem="User" HeaderText="Id" PropertyName="Id" SortKeySelector="item => item.Id">
|
||||||
|
@context.Id
|
||||||
|
</GridColumn>
|
||||||
|
<GridColumn TItem="User" HeaderText="Active">
|
||||||
|
<ChildContent>
|
||||||
|
<Switch Value="context.Active" ValueExpression="() => context.Active" ValueChanged="(_) => ToggleUserActive(context.Id)" />
|
||||||
|
</ChildContent>
|
||||||
|
</GridColumn>
|
||||||
|
</GridColumns>
|
||||||
|
</Grid>
|
||||||
}
|
}
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
@ -48,4 +87,9 @@
|
||||||
Manager.AddUser();
|
Manager.AddUser();
|
||||||
Status = Manager.GetStatus();
|
Status = Manager.GetStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ToggleUserActive(Guid userId)
|
||||||
|
{
|
||||||
|
Manager.ToggleUserActivity(userId);
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -4,9 +4,8 @@ namespace AccessQueuePlayground.Models
|
||||||
{
|
{
|
||||||
public class AccessQueueStatus
|
public class AccessQueueStatus
|
||||||
{
|
{
|
||||||
public List<User> Users { get; set; } = [];
|
public List<User> AccessUsers { get; set; } = [];
|
||||||
public int QueueSize { get; set; }
|
public List<User> QueuedUsers { get; set; } = [];
|
||||||
public int ActiveTickets { get; set; }
|
public List<User> InactiveUsers { get; set; } = [];
|
||||||
public int UnexpiredTickets { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,13 +52,29 @@ namespace AccessQueuePlayground.Services
|
||||||
var newStatus = new AccessQueueStatus();
|
var newStatus = new AccessQueueStatus();
|
||||||
foreach (var user in userList)
|
foreach (var user in userList)
|
||||||
{
|
{
|
||||||
AccessResponse? response = user.LatestResponse;
|
|
||||||
if (user.Active)
|
if (user.Active)
|
||||||
{
|
{
|
||||||
response = await _accessService.RequestAccess(user.Id);
|
user.LatestResponse = await _accessService.RequestAccess(user.Id);
|
||||||
user.LatestResponse = response;
|
if (user.LatestResponse?.HasAccess ?? false)
|
||||||
|
{
|
||||||
|
newStatus.AccessUsers.Add(user);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newStatus.QueuedUsers.Add(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(user.LatestResponse?.ExpiresOn != null && user.LatestResponse.ExpiresOn > DateTime.UtcNow)
|
||||||
|
{
|
||||||
|
newStatus.AccessUsers.Add(user);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
newStatus.InactiveUsers.Add(user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
newStatus.Users.Add(user);
|
|
||||||
}
|
}
|
||||||
_status = newStatus;
|
_status = newStatus;
|
||||||
NotifyStatusUpdated();
|
NotifyStatusUpdated();
|
||||||
|
|
|
@ -6,9 +6,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AccessQueue": {
|
"AccessQueue": {
|
||||||
"CapacityLimit": 10, // Maximum number of active users
|
"CapacityLimit": 3, // Maximum number of active users
|
||||||
"ActivitySeconds": 5, // Time since last access before a user is considered inactive
|
"ActivitySeconds": 2, // Time since last access before a user is considered inactive
|
||||||
"ExpirationSeconds": 30, // 12 hours - Time before a user access is revoked
|
"ExpirationSeconds": 10, // 12 hours - Time before a user access is revoked
|
||||||
"RollingExpiration": true // Whether to extend expiration time on access
|
"RollingExpiration": true // Whether to extend expiration time on access
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*"
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace AccessQueueService.Data
|
||||||
public int GetActiveTicketsCount(DateTime activeCutoff) => _accessTickets
|
public int GetActiveTicketsCount(DateTime activeCutoff) => _accessTickets
|
||||||
.Count(t => t.Value.ExpiresOn > DateTime.UtcNow && t.Value.LastActive >activeCutoff);
|
.Count(t => t.Value.ExpiresOn > DateTime.UtcNow && t.Value.LastActive >activeCutoff);
|
||||||
public int GetQueueCount() => _accessQueue.Count;
|
public int GetQueueCount() => _accessQueue.Count;
|
||||||
public int IndexOfTicket(Guid userId)
|
public int GetRequestsAhead(Guid userId)
|
||||||
{
|
{
|
||||||
var index = 0;
|
var index = 0;
|
||||||
foreach (var ticket in _accessQueue)
|
foreach (var ticket in _accessQueue)
|
||||||
|
|
|
@ -10,7 +10,7 @@ namespace AccessQueueService.Data
|
||||||
public int GetQueueCount();
|
public int GetQueueCount();
|
||||||
public AccessTicket? GetTicket(Guid userId);
|
public AccessTicket? GetTicket(Guid userId);
|
||||||
public void UpsertTicket(AccessTicket ticket);
|
public void UpsertTicket(AccessTicket ticket);
|
||||||
public int IndexOfTicket(Guid userId);
|
public int GetRequestsAhead(Guid userId);
|
||||||
public void Enqueue(AccessTicket ticket);
|
public void Enqueue(AccessTicket ticket);
|
||||||
public int DeleteExpiredTickets();
|
public int DeleteExpiredTickets();
|
||||||
public bool RemoveUser(Guid userId);
|
public bool RemoveUser(Guid userId);
|
||||||
|
|
|
@ -0,0 +1,102 @@
|
||||||
|
using AccessQueueService.Models;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|
||||||
|
namespace AccessQueueService.Data
|
||||||
|
{
|
||||||
|
public class TakeANumberAccessQueueRepo : IAccessQueueRepo
|
||||||
|
{
|
||||||
|
private readonly Dictionary<Guid, AccessTicket> _accessTickets = [];
|
||||||
|
private readonly Dictionary<Guid, ulong> _queueNumbers = [];
|
||||||
|
private readonly Dictionary<ulong, AccessTicket> _accessQueue = [];
|
||||||
|
|
||||||
|
private ulong _nowServing = 0;
|
||||||
|
private ulong _nextUnusedTicket = 0;
|
||||||
|
|
||||||
|
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() => (int)(_nextUnusedTicket - _nowServing);
|
||||||
|
public int GetRequestsAhead(Guid userId)
|
||||||
|
{
|
||||||
|
if(_queueNumbers.TryGetValue(userId, out var queueNumber))
|
||||||
|
{
|
||||||
|
return queueNumber >= _nowServing ? (int)(queueNumber - _nowServing) : -1;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Enqueue(AccessTicket ticket)
|
||||||
|
{
|
||||||
|
_queueNumbers[ticket.UserId] = _nextUnusedTicket;
|
||||||
|
_accessQueue[_nextUnusedTicket] = ticket;
|
||||||
|
_nextUnusedTicket++;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 && _nowServing < _nextUnusedTicket)
|
||||||
|
{
|
||||||
|
if (_accessQueue.TryGetValue(_nowServing, out var nextUser))
|
||||||
|
{
|
||||||
|
_nowServing++;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,9 +68,8 @@ namespace AccessQueueService.Services
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var indexOfTicket = _accessQueueRepo.IndexOfTicket(userId);
|
var requestsAhead = _accessQueueRepo.GetRequestsAhead(userId);
|
||||||
var requestsAhead = _accessQueueRepo.GetQueueCount() - indexOfTicket - 1;
|
if (requestsAhead == -1)
|
||||||
if (indexOfTicket == -1)
|
|
||||||
{
|
{
|
||||||
_accessQueueRepo.Enqueue(new AccessTicket
|
_accessQueueRepo.Enqueue(new AccessTicket
|
||||||
{
|
{
|
||||||
|
@ -82,7 +81,7 @@ namespace AccessQueueService.Services
|
||||||
return new AccessResponse
|
return new AccessResponse
|
||||||
{
|
{
|
||||||
ExpiresOn = null,
|
ExpiresOn = null,
|
||||||
RequestsAhead = requestsAhead
|
RequestsAhead = _accessQueueRepo.GetQueueCount() - 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,14 @@ namespace AccessQueueServiceTests
|
||||||
const int ACT_MILLIS = 1000 * ACT_SECONDS;
|
const int ACT_MILLIS = 1000 * ACT_SECONDS;
|
||||||
const int CAP_LIMIT = 5;
|
const int CAP_LIMIT = 5;
|
||||||
const int BULK_COUNT = 50000;
|
const int BULK_COUNT = 50000;
|
||||||
private readonly AccessService _accessService;
|
private AccessService _accessService;
|
||||||
|
public static IEnumerable<object[]> RepoImplementations()
|
||||||
|
{
|
||||||
|
yield return new object[] { new DictionaryAccessQueueRepo() };
|
||||||
|
yield return new object[] { new TakeANumberAccessQueueRepo() };
|
||||||
|
}
|
||||||
|
|
||||||
public AccessServiceTests()
|
private void CreateService(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
var inMemorySettings = new Dictionary<string, string?>
|
var inMemorySettings = new Dictionary<string, string?>
|
||||||
{
|
{
|
||||||
|
@ -33,14 +38,16 @@ namespace AccessQueueServiceTests
|
||||||
var configuration = new ConfigurationBuilder()
|
var configuration = new ConfigurationBuilder()
|
||||||
.AddInMemoryCollection(inMemorySettings)
|
.AddInMemoryCollection(inMemorySettings)
|
||||||
.Build();
|
.Build();
|
||||||
var accessQueueRepo = new DictionaryAccessQueueRepo();
|
|
||||||
|
|
||||||
_accessService = new AccessService(configuration, accessQueueRepo);
|
|
||||||
|
_accessService = new AccessService(configuration, repo);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task RequestAccess_ShouldGrantAccess_WhenCapacityIsAvailable()
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RequestAccess_ShouldGrantAccess_WhenCapacityIsAvailable(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
|
CreateService(repo);
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
|
|
||||||
var response = await _accessService.RequestAccess(userId);
|
var response = await _accessService.RequestAccess(userId);
|
||||||
|
@ -53,9 +60,11 @@ namespace AccessQueueServiceTests
|
||||||
Assert.Equal(0, _accessService.QueueCount);
|
Assert.Equal(0, _accessService.QueueCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task RequestAccess_ShouldReturnAccessResponse_WhenUserAlreadyHasTicket()
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RequestAccess_ShouldReturnAccessResponse_WhenUserAlreadyHasTicket(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
|
CreateService(repo);
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
await _accessService.RequestAccess(userId);
|
await _accessService.RequestAccess(userId);
|
||||||
|
|
||||||
|
@ -69,9 +78,11 @@ namespace AccessQueueServiceTests
|
||||||
Assert.Equal(0, _accessService.QueueCount);
|
Assert.Equal(0, _accessService.QueueCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task RequestAccess_ShouldQueueUser_WhenCapacityIsFull()
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RequestAccess_ShouldQueueUser_WhenCapacityIsFull(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
|
CreateService(repo);
|
||||||
for (int i = 0; i < CAP_LIMIT * 2; i++) // Fill double capacity
|
for (int i = 0; i < CAP_LIMIT * 2; i++) // Fill double capacity
|
||||||
{
|
{
|
||||||
await _accessService.RequestAccess(Guid.NewGuid());
|
await _accessService.RequestAccess(Guid.NewGuid());
|
||||||
|
@ -89,9 +100,11 @@ namespace AccessQueueServiceTests
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task RevokeAccess_ShouldReturnTrue_WhenUserHasAccess()
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RevokeAccess_ShouldReturnTrue_WhenUserHasAccess(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
|
CreateService(repo);
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
await _accessService.RequestAccess(userId);
|
await _accessService.RequestAccess(userId);
|
||||||
|
|
||||||
|
@ -100,9 +113,11 @@ namespace AccessQueueServiceTests
|
||||||
Assert.True(result);
|
Assert.True(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task RevokeAccess_ShouldReturnFalse_WhenUserDoesNotHaveAccess()
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RevokeAccess_ShouldReturnFalse_WhenUserDoesNotHaveAccess(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
|
CreateService(repo);
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
|
|
||||||
var result = await _accessService.RevokeAccess(userId);
|
var result = await _accessService.RevokeAccess(userId);
|
||||||
|
@ -110,9 +125,11 @@ namespace AccessQueueServiceTests
|
||||||
Assert.False(result);
|
Assert.False(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task RequestAccess_ShouldQueueUser_AfterAccessRevoked()
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RequestAccess_ShouldQueueUser_AfterAccessRevoked(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
|
CreateService(repo);
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
await _accessService.RequestAccess(userId);
|
await _accessService.RequestAccess(userId);
|
||||||
|
|
||||||
|
@ -131,9 +148,11 @@ namespace AccessQueueServiceTests
|
||||||
Assert.False(responseAfterRevoke.HasAccess);
|
Assert.False(responseAfterRevoke.HasAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task RequestAccess_ShouldNotQueueUser_WhenMultipleRequestsForOtherUsersMade()
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RequestAccess_ShouldNotQueueUser_WhenMultipleRequestsForOtherUsersMade(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
|
CreateService(repo);
|
||||||
for (int i = 0; i < CAP_LIMIT; i++) // Fill slots without awaiting
|
for (int i = 0; i < CAP_LIMIT; i++) // Fill slots without awaiting
|
||||||
{
|
{
|
||||||
_ = _accessService.RequestAccess(Guid.NewGuid());
|
_ = _accessService.RequestAccess(Guid.NewGuid());
|
||||||
|
@ -143,9 +162,11 @@ namespace AccessQueueServiceTests
|
||||||
Assert.False(response.HasAccess);
|
Assert.False(response.HasAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task RequestAccess_ShouldUpdateExpirationTime_WhenRollingExpirationTrue()
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RequestAccess_ShouldUpdateExpirationTime_WhenRollingExpirationTrue(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
|
CreateService(repo);
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
var initialResponse = await _accessService.RequestAccess(userId);
|
var initialResponse = await _accessService.RequestAccess(userId);
|
||||||
await Task.Delay(ACT_MILLIS);
|
await Task.Delay(ACT_MILLIS);
|
||||||
|
@ -153,9 +174,11 @@ namespace AccessQueueServiceTests
|
||||||
Assert.True(updatedResponse.ExpiresOn > initialResponse.ExpiresOn);
|
Assert.True(updatedResponse.ExpiresOn > initialResponse.ExpiresOn);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task RequestAccess_ShouldGrantAccess_WhenUsersWithAccessInactive()
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RequestAccess_ShouldGrantAccess_WhenUsersWithAccessInactive(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
|
CreateService(repo);
|
||||||
for (int i = 0; i < CAP_LIMIT; i++)
|
for (int i = 0; i < CAP_LIMIT; i++)
|
||||||
{
|
{
|
||||||
await _accessService.RequestAccess(Guid.NewGuid());
|
await _accessService.RequestAccess(Guid.NewGuid());
|
||||||
|
@ -168,9 +191,11 @@ namespace AccessQueueServiceTests
|
||||||
Assert.True(response.HasAccess);
|
Assert.True(response.HasAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task RequestAccess_ShouldRevokeAccess_WhenExpired()
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RequestAccess_ShouldRevokeAccess_WhenExpired(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
|
CreateService(repo);
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
var response = await _accessService.RequestAccess(userId);
|
var response = await _accessService.RequestAccess(userId);
|
||||||
Assert.True(response.HasAccess);
|
Assert.True(response.HasAccess);
|
||||||
|
@ -183,9 +208,11 @@ namespace AccessQueueServiceTests
|
||||||
Assert.False(response.HasAccess);
|
Assert.False(response.HasAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task RequestAccess_ShouldRetailAccess_WhenNotExpired()
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RequestAccess_ShouldRetailAccess_WhenNotExpired(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
|
CreateService(repo);
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
var response = await _accessService.RequestAccess(userId);
|
var response = await _accessService.RequestAccess(userId);
|
||||||
Assert.True(response.HasAccess);
|
Assert.True(response.HasAccess);
|
||||||
|
@ -199,9 +226,11 @@ namespace AccessQueueServiceTests
|
||||||
Assert.True(response.HasAccess);
|
Assert.True(response.HasAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task RequestAccess_ShouldProcessBulkRequests()
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RequestAccess_ShouldProcessBulkRequests(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
|
CreateService(repo);
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
await _accessService.RequestAccess(userId);
|
await _accessService.RequestAccess(userId);
|
||||||
for (int i = 0; i < BULK_COUNT; i++)
|
for (int i = 0; i < BULK_COUNT; i++)
|
||||||
|
@ -213,9 +242,11 @@ namespace AccessQueueServiceTests
|
||||||
Assert.True(response.HasAccess);
|
Assert.True(response.HasAccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Theory]
|
||||||
public async Task RequestAccess_ShouldReportLessInQueue_AsTicketsInactivate()
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RequestAccess_ShouldReportLessInQueue_AsTicketsInactivate(IAccessQueueRepo repo)
|
||||||
{
|
{
|
||||||
|
CreateService(repo);
|
||||||
var start = DateTime.UtcNow;
|
var start = DateTime.UtcNow;
|
||||||
for (int i = 0; i < CAP_LIMIT; i++)
|
for (int i = 0; i < CAP_LIMIT; i++)
|
||||||
{
|
{
|
||||||
|
@ -245,6 +276,21 @@ namespace AccessQueueServiceTests
|
||||||
Assert.Equal(0, response.RequestsAhead);
|
Assert.Equal(0, response.RequestsAhead);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(RepoImplementations))]
|
||||||
|
public async Task RequestAccess_ShouldShowCorrectRequestsAhead_WhenAccessRerequested(IAccessQueueRepo repo)
|
||||||
|
{
|
||||||
|
CreateService(repo);
|
||||||
|
for (int i = 0; i < CAP_LIMIT + 3; i++)
|
||||||
|
{
|
||||||
|
await _accessService.RequestAccess(Guid.NewGuid());
|
||||||
|
}
|
||||||
|
var id = Guid.NewGuid();
|
||||||
|
var response = await _accessService.RequestAccess(id);
|
||||||
|
Assert.Equal(3, response.RequestsAhead);
|
||||||
|
response = await _accessService.RequestAccess(id);
|
||||||
|
Assert.Equal(3, response?.RequestsAhead);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue