up
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
using StellaOps.Authority.Bootstrap;
|
||||
using StellaOps.Authority.Storage.Mongo.Documents;
|
||||
using StellaOps.Authority.Storage.Mongo.Stores;
|
||||
using StellaOps.Cryptography.Audit;
|
||||
using Xunit;
|
||||
|
||||
namespace StellaOps.Authority.Tests.Bootstrap;
|
||||
|
||||
public sealed class BootstrapInviteCleanupServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task SweepExpiredInvitesAsync_ExpiresInvitesAndEmitsAuditRecords()
|
||||
{
|
||||
var now = new DateTimeOffset(2025, 10, 14, 12, 0, 0, TimeSpan.Zero);
|
||||
var timeProvider = new FakeTimeProvider(now);
|
||||
|
||||
var invites = new List<AuthorityBootstrapInviteDocument>
|
||||
{
|
||||
new()
|
||||
{
|
||||
Token = "token-1",
|
||||
Type = BootstrapInviteTypes.User,
|
||||
ExpiresAt = now.AddMinutes(-5),
|
||||
Provider = "standard",
|
||||
Target = "alice@example.com",
|
||||
Status = AuthorityBootstrapInviteStatuses.Pending
|
||||
},
|
||||
new()
|
||||
{
|
||||
Token = "token-2",
|
||||
Type = BootstrapInviteTypes.Client,
|
||||
ExpiresAt = now.AddMinutes(-1),
|
||||
Provider = "standard",
|
||||
Target = "client-1",
|
||||
Status = AuthorityBootstrapInviteStatuses.Reserved
|
||||
}
|
||||
};
|
||||
|
||||
var store = new FakeInviteStore(invites);
|
||||
var sink = new CapturingAuthEventSink();
|
||||
var service = new BootstrapInviteCleanupService(store, sink, timeProvider, NullLogger<BootstrapInviteCleanupService>.Instance);
|
||||
|
||||
await service.SweepExpiredInvitesAsync(CancellationToken.None);
|
||||
|
||||
Assert.True(store.ExpireCalled);
|
||||
Assert.Equal(2, sink.Events.Count);
|
||||
Assert.All(sink.Events, record => Assert.Equal("authority.bootstrap.invite.expired", record.EventType));
|
||||
Assert.Contains(sink.Events, record => record.Properties.Any(property => property.Name == "invite.token" && property.Value.Value == "token-1"));
|
||||
Assert.Contains(sink.Events, record => record.Properties.Any(property => property.Name == "invite.token" && property.Value.Value == "token-2"));
|
||||
}
|
||||
|
||||
private sealed class FakeInviteStore : IAuthorityBootstrapInviteStore
|
||||
{
|
||||
private readonly IReadOnlyList<AuthorityBootstrapInviteDocument> invites;
|
||||
|
||||
public FakeInviteStore(IReadOnlyList<AuthorityBootstrapInviteDocument> invites)
|
||||
=> this.invites = invites;
|
||||
|
||||
public bool ExpireCalled { get; private set; }
|
||||
|
||||
public ValueTask<AuthorityBootstrapInviteDocument> CreateAsync(AuthorityBootstrapInviteDocument document, CancellationToken cancellationToken)
|
||||
=> throw new NotImplementedException();
|
||||
|
||||
public ValueTask<BootstrapInviteReservationResult> TryReserveAsync(string token, string expectedType, DateTimeOffset now, string? reservedBy, CancellationToken cancellationToken)
|
||||
=> ValueTask.FromResult(new BootstrapInviteReservationResult(BootstrapInviteReservationStatus.NotFound, null));
|
||||
|
||||
public ValueTask<bool> ReleaseAsync(string token, CancellationToken cancellationToken)
|
||||
=> ValueTask.FromResult(false);
|
||||
|
||||
public ValueTask<bool> MarkConsumedAsync(string token, string? consumedBy, DateTimeOffset consumedAt, CancellationToken cancellationToken)
|
||||
=> ValueTask.FromResult(false);
|
||||
|
||||
public ValueTask<IReadOnlyList<AuthorityBootstrapInviteDocument>> ExpireAsync(DateTimeOffset now, CancellationToken cancellationToken)
|
||||
{
|
||||
ExpireCalled = true;
|
||||
return ValueTask.FromResult(invites);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class CapturingAuthEventSink : IAuthEventSink
|
||||
{
|
||||
public List<AuthEventRecord> Events { get; } = new();
|
||||
|
||||
public ValueTask WriteAsync(AuthEventRecord record, CancellationToken cancellationToken)
|
||||
{
|
||||
Events.Add(record);
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user