208 lines
5.5 KiB
C#
208 lines
5.5 KiB
C#
using System.IdentityModel.Tokens.Jwt;
|
|
using System.Security.Claims;
|
|
using FsCheck;
|
|
using FsCheck.Xunit;
|
|
using HoneyBox.Core.Services;
|
|
using HoneyBox.Model.Entities;
|
|
using HoneyBox.Model.Models.Auth;
|
|
using Microsoft.Extensions.Logging;
|
|
using Moq;
|
|
using Xunit;
|
|
|
|
namespace HoneyBox.Tests.Services;
|
|
|
|
public class JwtServiceTests
|
|
{
|
|
private readonly JwtSettings _jwtSettings;
|
|
private readonly Mock<ILogger<JwtService>> _mockLogger;
|
|
private readonly JwtService _jwtService;
|
|
|
|
public JwtServiceTests()
|
|
{
|
|
_jwtSettings = new JwtSettings
|
|
{
|
|
Secret = "your-secret-key-must-be-at-least-32-characters-long-for-hs256",
|
|
Issuer = "HoneyBox",
|
|
Audience = "HoneyBoxUsers",
|
|
ExpirationMinutes = 1440,
|
|
RefreshTokenExpirationDays = 7
|
|
};
|
|
|
|
_mockLogger = new Mock<ILogger<JwtService>>();
|
|
_jwtService = new JwtService(_jwtSettings, _mockLogger.Object);
|
|
}
|
|
|
|
[Fact]
|
|
public void GenerateToken_WithValidUser_ReturnsValidToken()
|
|
{
|
|
// Arrange
|
|
var user = new User
|
|
{
|
|
Id = 1,
|
|
Nickname = "TestUser",
|
|
Uid = "uid123",
|
|
OpenId = "openid123",
|
|
HeadImg = "https://example.com/avatar.jpg",
|
|
CreatedAt = DateTime.UtcNow,
|
|
UpdatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
// Act
|
|
var token = _jwtService.GenerateToken(user);
|
|
|
|
// Assert
|
|
Assert.NotNull(token);
|
|
Assert.NotEmpty(token);
|
|
|
|
// Verify token can be read
|
|
var handler = new JwtSecurityTokenHandler();
|
|
var jwtToken = handler.ReadJwtToken(token);
|
|
Assert.NotNull(jwtToken);
|
|
Assert.Equal(_jwtSettings.Issuer, jwtToken.Issuer);
|
|
Assert.Equal(_jwtSettings.Audience, jwtToken.Audiences.First());
|
|
}
|
|
|
|
[Fact]
|
|
public void GenerateToken_WithNullUser_ThrowsArgumentNullException()
|
|
{
|
|
// Act & Assert
|
|
Assert.Throws<ArgumentNullException>(() => _jwtService.GenerateToken(null!));
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidateToken_WithValidToken_ReturnsClaimsPrincipal()
|
|
{
|
|
// Arrange
|
|
var user = new User
|
|
{
|
|
Id = 1,
|
|
Nickname = "TestUser",
|
|
Uid = "uid123",
|
|
OpenId = "openid123",
|
|
HeadImg = "https://example.com/avatar.jpg",
|
|
CreatedAt = DateTime.UtcNow,
|
|
UpdatedAt = DateTime.UtcNow
|
|
};
|
|
var token = _jwtService.GenerateToken(user);
|
|
|
|
// Act
|
|
var principal = _jwtService.ValidateToken(token);
|
|
|
|
// Assert
|
|
Assert.NotNull(principal);
|
|
var userIdClaim = principal.FindFirst(ClaimTypes.NameIdentifier);
|
|
Assert.NotNull(userIdClaim);
|
|
Assert.Equal("1", userIdClaim.Value);
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidateToken_WithInvalidToken_ReturnsNull()
|
|
{
|
|
// Act
|
|
var principal = _jwtService.ValidateToken("invalid.token.here");
|
|
|
|
// Assert
|
|
Assert.Null(principal);
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidateToken_WithNullToken_ReturnsNull()
|
|
{
|
|
// Act
|
|
var principal = _jwtService.ValidateToken(null!);
|
|
|
|
// Assert
|
|
Assert.Null(principal);
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidateToken_WithEmptyToken_ReturnsNull()
|
|
{
|
|
// Act
|
|
var principal = _jwtService.ValidateToken(string.Empty);
|
|
|
|
// Assert
|
|
Assert.Null(principal);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetUserIdFromToken_WithValidToken_ReturnsCorrectUserId()
|
|
{
|
|
// Arrange
|
|
var user = new User
|
|
{
|
|
Id = 42,
|
|
Nickname = "TestUser",
|
|
Uid = "uid123",
|
|
OpenId = "openid123",
|
|
HeadImg = "https://example.com/avatar.jpg",
|
|
CreatedAt = DateTime.UtcNow,
|
|
UpdatedAt = DateTime.UtcNow
|
|
};
|
|
var token = _jwtService.GenerateToken(user);
|
|
|
|
// Act
|
|
var userId = _jwtService.GetUserIdFromToken(token);
|
|
|
|
// Assert
|
|
Assert.NotNull(userId);
|
|
Assert.Equal(42, userId);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetUserIdFromToken_WithInvalidToken_ReturnsNull()
|
|
{
|
|
// Act
|
|
var userId = _jwtService.GetUserIdFromToken("invalid.token.here");
|
|
|
|
// Assert
|
|
Assert.Null(userId);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetUserIdFromToken_WithNullToken_ReturnsNull()
|
|
{
|
|
// Act
|
|
var userId = _jwtService.GetUserIdFromToken(null!);
|
|
|
|
// Assert
|
|
Assert.Null(userId);
|
|
}
|
|
|
|
[Fact]
|
|
public void GetUserIdFromToken_WithEmptyToken_ReturnsNull()
|
|
{
|
|
// Act
|
|
var userId = _jwtService.GetUserIdFromToken(string.Empty);
|
|
|
|
// Assert
|
|
Assert.Null(userId);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Property 3: 登录成功Token生成
|
|
/// For any valid user, generating a token should produce a valid JWT that contains the user's ID
|
|
/// and can be validated to extract the same user ID.
|
|
/// Validates: Requirements 3.1, 3.2
|
|
/// </summary>
|
|
[Property(MaxTest = 100)]
|
|
public bool GeneratedTokenContainsCorrectUserId(PositiveInt userId, NonEmptyString nickname)
|
|
{
|
|
var user = new User
|
|
{
|
|
Id = userId.Item,
|
|
Nickname = nickname.Item,
|
|
Uid = $"uid{userId.Item}",
|
|
OpenId = $"openid{userId.Item}",
|
|
HeadImg = "https://example.com/avatar.jpg",
|
|
CreatedAt = DateTime.UtcNow,
|
|
UpdatedAt = DateTime.UtcNow
|
|
};
|
|
|
|
var token = _jwtService.GenerateToken(user);
|
|
var extractedUserId = _jwtService.GetUserIdFromToken(token);
|
|
|
|
return extractedUserId == userId.Item;
|
|
}
|
|
}
|