using System; using System.Text; using FluentAssertions; using NexusReader.UI.Shared.Services; using Xunit; namespace NexusReader.Application.Tests.Services; public class JwtTokenValidatorTests { private string CreateMockToken(long exp) { // {"alg":"HS256","typ":"JWT"} var header = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"; var payloadJson = $"{{\"exp\":{exp}}}"; var payloadBytes = Encoding.UTF8.GetBytes(payloadJson); var payload = Convert.ToBase64String(payloadBytes) .Replace('+', '-') .Replace('/', '_') .TrimEnd('='); return $"{header}.{payload}.signature"; } [Fact] public void IsExpired_WithNullOrEmptyToken_ShouldReturnTrue() { JwtTokenValidator.IsExpired(null).Should().BeTrue(); JwtTokenValidator.IsExpired("").Should().BeTrue(); JwtTokenValidator.IsExpired(" ").Should().BeTrue(); } [Fact] public void IsExpired_WithMalformedToken_ShouldReturnTrue() { JwtTokenValidator.IsExpired("not.a.valid.token.format.here").Should().BeTrue(); JwtTokenValidator.IsExpired("part1.part2").Should().BeTrue(); JwtTokenValidator.IsExpired("justonestring").Should().BeTrue(); } [Fact] public void IsExpired_WithExpiredToken_ShouldReturnTrue() { // Expired 1 hour ago var expiredTime = DateTimeOffset.UtcNow.AddHours(-1).ToUnixTimeSeconds(); var token = CreateMockToken(expiredTime); JwtTokenValidator.IsExpired(token).Should().BeTrue(); } [Fact] public void IsExpired_WithValidToken_ShouldReturnFalse() { // Valid for 1 hour in the future var futureTime = DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds(); var token = CreateMockToken(futureTime); JwtTokenValidator.IsExpired(token).Should().BeFalse(); } [Fact] public void IsExpired_WithTokenInsideSkewBuffer_ShouldReturnTrue() { // Expiring in 5 seconds (within the 10-second skew buffer) var skewTime = DateTimeOffset.UtcNow.AddSeconds(5).ToUnixTimeSeconds(); var token = CreateMockToken(skewTime); JwtTokenValidator.IsExpired(token).Should().BeTrue(); } }