Files

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);
}
}