125 lines
3.8 KiB
C#
125 lines
3.8 KiB
C#
using ItWorx.KnowledgeHub.Domain.Automation;
|
|
|
|
namespace ItWorx.KnowledgeHub.Domain.Tests;
|
|
|
|
public sealed class ServiceAccountTests
|
|
{
|
|
private static readonly DateTimeOffset Now = new(2026, 8, 1, 12, 0, 0, TimeSpan.Zero);
|
|
|
|
[Fact]
|
|
public void Create_starts_enabled()
|
|
{
|
|
var account = ServiceAccount.Create(Guid.NewGuid(), "CI-bot", "Beschrijving.", Guid.NewGuid(), Now);
|
|
|
|
Assert.True(account.Enabled);
|
|
Assert.Null(account.DisabledAt);
|
|
}
|
|
|
|
[Fact]
|
|
public void Disable_is_idempotent_and_records_the_first_disable_time_only()
|
|
{
|
|
var account = ServiceAccount.Create(Guid.NewGuid(), "CI-bot", "B.", Guid.NewGuid(), Now);
|
|
|
|
account.Disable(Now);
|
|
account.Disable(Now.AddHours(1));
|
|
|
|
Assert.False(account.Enabled);
|
|
Assert.Equal(Now, account.DisabledAt);
|
|
}
|
|
}
|
|
|
|
public sealed class ServiceAccountTokenTests
|
|
{
|
|
private static readonly DateTimeOffset Now = new(2026, 8, 1, 12, 0, 0, TimeSpan.Zero);
|
|
|
|
[Fact]
|
|
public void Create_rejects_a_token_with_no_valid_scopes()
|
|
{
|
|
var exception = Record.Exception(() =>
|
|
ServiceAccountToken.Create(Guid.NewGuid(), "pub123", "hash", ["not-a-real-scope"], null, Now));
|
|
|
|
Assert.IsType<ArgumentException>(exception);
|
|
}
|
|
|
|
[Fact]
|
|
public void Create_normalizes_and_deduplicates_scopes()
|
|
{
|
|
var token = ServiceAccountToken.Create(
|
|
Guid.NewGuid(), "pub123", "hash", [Scopes.PagesRead, Scopes.PagesRead, "bogus"], null, Now);
|
|
|
|
Assert.Equal([Scopes.PagesRead], token.Scopes);
|
|
}
|
|
|
|
[Fact]
|
|
public void A_never_expiring_never_revoked_token_is_active()
|
|
{
|
|
var token = ServiceAccountToken.Create(Guid.NewGuid(), "pub", "hash", [Scopes.PagesRead], null, Now);
|
|
|
|
Assert.True(token.IsActive(Now.AddYears(10)));
|
|
}
|
|
|
|
[Fact]
|
|
public void An_expired_token_is_not_active()
|
|
{
|
|
var token = ServiceAccountToken.Create(Guid.NewGuid(), "pub", "hash", [Scopes.PagesRead], Now.AddDays(1), Now);
|
|
|
|
Assert.False(token.IsActive(Now.AddDays(2)));
|
|
}
|
|
|
|
[Fact]
|
|
public void A_revoked_token_is_not_active_even_before_its_expiry()
|
|
{
|
|
var token = ServiceAccountToken.Create(Guid.NewGuid(), "pub", "hash", [Scopes.PagesRead], Now.AddYears(1), Now);
|
|
|
|
token.Revoke(Now.AddHours(1));
|
|
|
|
Assert.False(token.IsActive(Now.AddHours(2)));
|
|
}
|
|
|
|
[Fact]
|
|
public void HasScope_is_true_only_for_a_granted_scope()
|
|
{
|
|
var token = ServiceAccountToken.Create(Guid.NewGuid(), "pub", "hash", [Scopes.PagesRead], null, Now);
|
|
|
|
Assert.True(token.HasScope(Scopes.PagesRead));
|
|
Assert.False(token.HasScope(Scopes.PagesWrite));
|
|
}
|
|
}
|
|
|
|
public sealed class OutboxMessageTests
|
|
{
|
|
private static readonly DateTimeOffset Now = new(2026, 8, 1, 12, 0, 0, TimeSpan.Zero);
|
|
|
|
[Fact]
|
|
public void Create_starts_unprocessed_and_immediately_available()
|
|
{
|
|
var message = OutboxMessage.Create("page.created.v1", "page", Guid.NewGuid().ToString(), "{}", Now);
|
|
|
|
Assert.False(message.IsProcessed);
|
|
Assert.Equal(Now, message.AvailableAt);
|
|
Assert.Equal(0, message.Attempts);
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkFailed_increments_attempts_and_delays_the_next_availability()
|
|
{
|
|
var message = OutboxMessage.Create("page.created.v1", "page", Guid.NewGuid().ToString(), "{}", Now);
|
|
|
|
message.MarkFailed("transient error", TimeSpan.FromMinutes(5), Now);
|
|
|
|
Assert.Equal(1, message.Attempts);
|
|
Assert.Equal(Now.AddMinutes(5), message.AvailableAt);
|
|
Assert.False(message.IsProcessed);
|
|
}
|
|
|
|
[Fact]
|
|
public void MarkProcessed_sets_the_processed_timestamp()
|
|
{
|
|
var message = OutboxMessage.Create("page.created.v1", "page", Guid.NewGuid().ToString(), "{}", Now);
|
|
|
|
message.MarkProcessed(Now.AddSeconds(1));
|
|
|
|
Assert.True(message.IsProcessed);
|
|
}
|
|
}
|