Files
natsdotnet/tests/NATS.Server.Auth.Tests/Auth/ActivationExpirationTests.cs
Joseph Doherty 36b9dfa654 refactor: extract NATS.Server.Auth.Tests project
Move 50 auth/accounts/permissions/JWT/NKey test files from
NATS.Server.Tests into a dedicated NATS.Server.Auth.Tests project.
Update namespaces, replace private GetFreePort/ReadUntilAsync helpers
with TestUtilities calls, replace Task.Delay with TaskCompletionSource
in test doubles, and add InternalsVisibleTo.

690 tests pass.
2026-03-12 15:54:07 -04:00

201 lines
7.4 KiB
C#

// Tests for Account JWT activation claim expiration: RegisterActivation,
// CheckActivationExpiry, IsActivationExpired, GetExpiredActivations,
// RemoveExpiredActivations, and ActiveActivationCount.
// Go reference: server/accounts.go — checkActivation (~line 2943),
// activationExpired (~line 2920).
using NATS.Server.Auth;
namespace NATS.Server.Auth.Tests.Auth;
public class ActivationExpirationTests
{
// Helpers for well-past / well-future dates to avoid timing flakiness.
private static DateTime WellFuture => DateTime.UtcNow.AddDays(30);
private static DateTime WellPast => DateTime.UtcNow.AddDays(-30);
private static ActivationClaim ValidClaim(string subject) => new()
{
Subject = subject,
IssuedAt = DateTime.UtcNow.AddDays(-1),
ExpiresAt = WellFuture,
Issuer = "AABC123",
};
private static ActivationClaim ExpiredClaim(string subject) => new()
{
Subject = subject,
IssuedAt = DateTime.UtcNow.AddDays(-60),
ExpiresAt = WellPast,
Issuer = "AABC123",
};
// ---------------------------------------------------------------------------
// RegisterActivation
// ---------------------------------------------------------------------------
[Fact]
public void RegisterActivation_StoresActivation()
{
// Go ref: accounts.go — checkActivation stores the decoded activation claim.
var account = new Account("test");
var claim = ValidClaim("svc.foo");
account.RegisterActivation("svc.foo", claim);
var result = account.CheckActivationExpiry("svc.foo");
result.Found.ShouldBeTrue();
}
// ---------------------------------------------------------------------------
// CheckActivationExpiry
// ---------------------------------------------------------------------------
[Fact]
public void CheckActivationExpiry_Valid_NotExpired()
{
// Go ref: accounts.go — act.Expires > tn ⇒ checkActivation returns true (not expired).
var account = new Account("test");
account.RegisterActivation("svc.valid", ValidClaim("svc.valid"));
var result = account.CheckActivationExpiry("svc.valid");
result.Found.ShouldBeTrue();
result.IsExpired.ShouldBeFalse();
result.ExpiresAt.ShouldNotBeNull();
result.TimeToExpiry.ShouldNotBeNull();
result.TimeToExpiry!.Value.ShouldBeGreaterThan(TimeSpan.Zero);
}
[Fact]
public void CheckActivationExpiry_Expired_ReturnsExpired()
{
// Go ref: accounts.go — act.Expires <= tn ⇒ checkActivation returns false (expired).
var account = new Account("test");
account.RegisterActivation("svc.expired", ExpiredClaim("svc.expired"));
var result = account.CheckActivationExpiry("svc.expired");
result.Found.ShouldBeTrue();
result.IsExpired.ShouldBeTrue();
result.ExpiresAt.ShouldNotBeNull();
result.TimeToExpiry.ShouldBe(TimeSpan.Zero);
}
[Fact]
public void CheckActivationExpiry_NotFound()
{
// Go ref: accounts.go — checkActivation returns false when claim is nil/empty token.
var account = new Account("test");
var result = account.CheckActivationExpiry("svc.unknown");
result.Found.ShouldBeFalse();
result.IsExpired.ShouldBeFalse();
result.ExpiresAt.ShouldBeNull();
result.TimeToExpiry.ShouldBeNull();
}
// ---------------------------------------------------------------------------
// IsActivationExpired
// ---------------------------------------------------------------------------
[Fact]
public void IsActivationExpired_Valid_ReturnsFalse()
{
// Go ref: accounts.go — act.Expires > tn ⇒ not expired.
var account = new Account("test");
account.RegisterActivation("svc.ok", ValidClaim("svc.ok"));
account.IsActivationExpired("svc.ok").ShouldBeFalse();
}
[Fact]
public void IsActivationExpired_Expired_ReturnsTrue()
{
// Go ref: accounts.go — act.Expires <= tn ⇒ expired, activationExpired fires.
var account = new Account("test");
account.RegisterActivation("svc.past", ExpiredClaim("svc.past"));
account.IsActivationExpired("svc.past").ShouldBeTrue();
}
// ---------------------------------------------------------------------------
// GetExpiredActivations
// ---------------------------------------------------------------------------
[Fact]
public void GetExpiredActivations_ReturnsOnlyExpired()
{
// Go ref: accounts.go — activationExpired is called only for expired claims.
var account = new Account("test");
account.RegisterActivation("svc.a", ValidClaim("svc.a"));
account.RegisterActivation("svc.b", ExpiredClaim("svc.b"));
account.RegisterActivation("svc.c", ValidClaim("svc.c"));
account.RegisterActivation("svc.d", ExpiredClaim("svc.d"));
var expired = account.GetExpiredActivations();
expired.Count.ShouldBe(2);
expired.ShouldContain("svc.b");
expired.ShouldContain("svc.d");
expired.ShouldNotContain("svc.a");
expired.ShouldNotContain("svc.c");
}
// ---------------------------------------------------------------------------
// RemoveExpiredActivations
// ---------------------------------------------------------------------------
[Fact]
public void RemoveExpiredActivations_RemovesAndReturnsCount()
{
// Go ref: accounts.go — activationExpired removes the import when activation expires.
var account = new Account("test");
account.RegisterActivation("svc.live", ValidClaim("svc.live"));
account.RegisterActivation("svc.gone1", ExpiredClaim("svc.gone1"));
account.RegisterActivation("svc.gone2", ExpiredClaim("svc.gone2"));
var removed = account.RemoveExpiredActivations();
removed.ShouldBe(2);
// The expired ones should no longer be found.
account.CheckActivationExpiry("svc.gone1").Found.ShouldBeFalse();
account.CheckActivationExpiry("svc.gone2").Found.ShouldBeFalse();
// The live one should still be registered.
account.CheckActivationExpiry("svc.live").Found.ShouldBeTrue();
}
// ---------------------------------------------------------------------------
// ActiveActivationCount
// ---------------------------------------------------------------------------
[Fact]
public void ActiveActivationCount_ExcludesExpired()
{
// Go ref: accounts.go — only non-expired activations are considered active.
var account = new Account("test");
account.RegisterActivation("svc.1", ValidClaim("svc.1"));
account.RegisterActivation("svc.2", ValidClaim("svc.2"));
account.RegisterActivation("svc.3", ExpiredClaim("svc.3"));
account.ActiveActivationCount.ShouldBe(2);
}
// ---------------------------------------------------------------------------
// ActivationClaim.TimeToExpiry
// ---------------------------------------------------------------------------
[Fact]
public void ActivationClaim_TimeToExpiry_Zero_WhenExpired()
{
// Go ref: accounts.go — expired activation has no remaining time.
var claim = ExpiredClaim("svc.expired");
claim.IsExpired.ShouldBeTrue();
claim.TimeToExpiry.ShouldBe(TimeSpan.Zero);
}
}