HaniBlindBox/server/HoneyBox/tests/HoneyBox.Tests/Services/JwtServicePropertyTests.cs
2026-01-04 01:47:02 +08:00

126 lines
4.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;
namespace HoneyBox.Tests.Services;
public class JwtServicePropertyTests
{
private readonly JwtSettings _jwtSettings;
private readonly Mock<ILogger<JwtService>> _mockLogger;
private readonly JwtService _jwtService;
public JwtServicePropertyTests()
{
_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);
}
/// <summary>
/// Property 7: Token验证与授权
/// For any valid token generated by the service, validation should succeed and return a valid ClaimsPrincipal.
/// For any invalid token, validation should fail and return null.
/// Validates: Requirements 3.3, 3.4
/// </summary>
[Property(MaxTest = 100)]
public bool ValidTokenShouldPassValidation(PositiveInt userId, NonEmptyString nickname)
{
// Create a valid user and generate a token
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 validToken = _jwtService.GenerateToken(user);
// Valid token should pass validation
var validPrincipal = _jwtService.ValidateToken(validToken);
var validTokenPasses = validPrincipal != null;
// Invalid token should fail validation
var invalidToken = "invalid.token.string";
var invalidPrincipal = _jwtService.ValidateToken(invalidToken);
var invalidTokenFails = invalidPrincipal == null;
// Null token should fail validation
var nullPrincipal = _jwtService.ValidateToken(null!);
var nullTokenFails = nullPrincipal == null;
return validTokenPasses && invalidTokenFails && nullTokenFails;
}
/// <summary>
/// Property 7 (continued): Token验证与授权 - Claim Extraction
/// For any valid token, the extracted claims should match the original user data.
/// Validates: Requirements 3.3, 3.4
/// </summary>
[Property(MaxTest = 100)]
public bool ValidatedTokenShouldContainCorrectClaims(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 principal = _jwtService.ValidateToken(token);
if (principal == null)
return false;
// Check that the principal contains the correct user ID claim
var userIdClaim = principal.FindFirst(ClaimTypes.NameIdentifier);
var userIdMatches = userIdClaim != null && userIdClaim.Value == userId.Item.ToString();
// Check that the principal contains the correct nickname claim
var nameClaim = principal.FindFirst(ClaimTypes.Name);
var nicknameMatches = nameClaim != null && nameClaim.Value == nickname.Item;
// Check that the principal contains the correct uid claim
var uidClaim = principal.FindFirst("uid");
var uidMatches = uidClaim != null && uidClaim.Value == $"uid{userId.Item}";
return userIdMatches && nicknameMatches && uidMatches;
}
/// <summary>
/// Property 7 (continued): Token验证与授权 - Unauthorized Access
/// For any request with an invalid or missing token, the system should deny access.
/// Validates: Requirements 3.3, 3.4
/// </summary>
[Property(MaxTest = 100)]
public bool InvalidTokenShouldDenyAccess(NonEmptyString invalidToken)
{
// Any random string should not validate as a token
var principal = _jwtService.ValidateToken(invalidToken.Item);
return principal == null;
}
}