using NATS.Server.Auth; using Shouldly; namespace NATS.Server.Auth.Tests.Auth; // Go reference: server/accounts.go — account expiry / SetExpirationTimer public sealed class AccountExpirationTests { // 1. ExpiresAt_Default_IsNull // Go reference: accounts.go — account.expiry zero-value [Fact] public void ExpiresAt_Default_IsNull() { var account = new Account("test-account"); account.ExpiresAt.ShouldBeNull(); } // 2. SetExpiration_SetsExpiresAt // Go reference: accounts.go — SetExpirationTimer stores expiry value [Fact] public void SetExpiration_SetsExpiresAt() { var account = new Account("test-account"); var expiresAt = new DateTime(2030, 6, 15, 12, 0, 0, DateTimeKind.Utc); account.SetExpiration(expiresAt); account.ExpiresAt.ShouldBe(expiresAt); } // 3. IsExpired_FutureDate_ReturnsFalse // Go reference: accounts.go — isExpired() returns false when expiry is in future [Fact] public void IsExpired_FutureDate_ReturnsFalse() { var account = new Account("test-account"); account.SetExpiration(DateTime.UtcNow.AddHours(1)); account.IsExpired.ShouldBeFalse(); } // 4. IsExpired_PastDate_ReturnsTrue // Go reference: accounts.go — isExpired() returns true when past expiry [Fact] public void IsExpired_PastDate_ReturnsTrue() { var account = new Account("test-account"); account.SetExpiration(DateTime.UtcNow.AddHours(-1)); account.IsExpired.ShouldBeTrue(); } // 5. ClearExpiration_RemovesExpiry // Go reference: accounts.go — clearing expiry resets the field to zero [Fact] public void ClearExpiration_RemovesExpiry() { var account = new Account("test-account"); account.SetExpiration(DateTime.UtcNow.AddHours(1)); account.ExpiresAt.ShouldNotBeNull(); account.ClearExpiration(); account.ExpiresAt.ShouldBeNull(); } // 6. SetExpirationFromTtl_CalculatesCorrectly // Go reference: accounts.go — SetExpirationTimer(ttl) sets expiry = now + ttl [Fact] public void SetExpirationFromTtl_CalculatesCorrectly() { var account = new Account("test-account"); var before = DateTime.UtcNow; account.SetExpirationFromTtl(TimeSpan.FromHours(1)); var after = DateTime.UtcNow; account.ExpiresAt.ShouldNotBeNull(); account.ExpiresAt!.Value.ShouldBeGreaterThanOrEqualTo(before.AddHours(1)); account.ExpiresAt.Value.ShouldBeLessThanOrEqualTo(after.AddHours(1)); } // 7. TimeToExpiry_NoExpiry_ReturnsNull // Go reference: accounts.go — no expiry set returns nil duration [Fact] public void TimeToExpiry_NoExpiry_ReturnsNull() { var account = new Account("test-account"); account.TimeToExpiry.ShouldBeNull(); } // 8. TimeToExpiry_Expired_ReturnsZero // Go reference: accounts.go — already-expired account has zero remaining time [Fact] public void TimeToExpiry_Expired_ReturnsZero() { var account = new Account("test-account"); account.SetExpiration(DateTime.UtcNow.AddHours(-1)); account.TimeToExpiry.ShouldBe(TimeSpan.Zero); } // 9. TimeToExpiry_Future_ReturnsPositive // Go reference: accounts.go — unexpired account returns positive remaining duration [Fact] public void TimeToExpiry_Future_ReturnsPositive() { var account = new Account("test-account"); account.SetExpiration(DateTime.UtcNow.AddHours(1)); var tte = account.TimeToExpiry; tte.ShouldNotBeNull(); tte!.Value.ShouldBeGreaterThan(TimeSpan.Zero); } // 10. GetExpirationInfo_ReturnsCompleteInfo // Go reference: accounts.go — expiry fields exposed for monitoring / JWT renewal [Fact] public void GetExpirationInfo_ReturnsCompleteInfo() { var account = new Account("info-account"); var expiresAt = DateTime.UtcNow.AddHours(2); account.SetExpiration(expiresAt); var info = account.GetExpirationInfo(); info.AccountName.ShouldBe("info-account"); info.HasExpiration.ShouldBeTrue(); info.ExpiresAt.ShouldBe(expiresAt); info.IsExpired.ShouldBeFalse(); info.TimeToExpiry.ShouldNotBeNull(); info.TimeToExpiry!.Value.ShouldBeGreaterThan(TimeSpan.Zero); } }