using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Xunit;
using BuildFlow.Platform.Application.Auth.Commands;
using BuildFlow.Platform.Application.Auth.Handlers;
using BuildFlow.Shared.Domain.Exceptions;
namespace BuildFlow.Platform.Tests.Auth;
///
/// 刷新令牌 Command 处理器单元测试。覆盖正常刷新 + Rotation、过期/吊销、不存在等路径。
///
public class RefreshTokenCommandHandlerTests
{
private static async Task<(Platform.Infrastructure.Persistence.PlatformDbContext db, string refreshToken)> SeedTokenAsync(
bool revoked = false, DateTime? expiresAt = null, int permissionVersion = 0)
{
var db = PlatformTestDb.CreateDbContext();
var userId = Guid.NewGuid();
await PlatformTestDb.SeedUserAsync(db, userId, "refresher", permissionVersion: permissionVersion);
var token = "valid-refresh-token";
db.RefreshTokens.Add(new BuildFlow.Platform.Domain.Entities.RefreshToken.RefreshTokenEntity
{
Id = Guid.NewGuid(),
UserId = userId,
Token = token,
ExpiresAt = expiresAt ?? DateTime.UtcNow.AddDays(1),
IsRevoked = revoked,
CreatedBy = "test",
CreatedAt = DateTime.UtcNow,
UpdatedBy = "test",
UpdatedAt = DateTime.UtcNow,
});
await db.SaveChangesAsync();
return (db, token);
}
[Fact]
public async Task Handle_ValidToken_ShouldRotateAndIssueNewToken()
{
// Arrange
var (db, token) = await SeedTokenAsync();
var handler = new RefreshTokenCommandHandler(db, PlatformTestDb.CreateConfig());
// Act
var result = await handler.Handle(new RefreshTokenCommand(token), CancellationToken.None);
// Assert:新 token 签发 + 旧 token 被吊销(Rotation)
result.AccessToken.Should().NotBeNullOrWhiteSpace();
result.RefreshToken.Should().NotBe(token);
var oldToken = await db.RefreshTokens.FirstAsync(rt => rt.Token == token);
oldToken.IsRevoked.Should().BeTrue();
}
[Fact]
public async Task Handle_RevokedToken_ShouldThrowExpired()
{
// Arrange
var (db, token) = await SeedTokenAsync(revoked: true);
var handler = new RefreshTokenCommandHandler(db, PlatformTestDb.CreateConfig());
// Act
var act = () => handler.Handle(new RefreshTokenCommand(token), CancellationToken.None);
// Assert
await act.Should().ThrowAsync()
.WithMessage("*刷新令牌已过期或已吊销*");
}
[Fact]
public async Task Handle_NonExistentToken_ShouldThrowUnauthorized()
{
// Arrange
var db = PlatformTestDb.CreateDbContext();
var handler = new RefreshTokenCommandHandler(db, PlatformTestDb.CreateConfig());
// Act
var act = () => handler.Handle(new RefreshTokenCommand("nonexistent"), CancellationToken.None);
// Assert
await act.Should().ThrowAsync()
.WithMessage("*无效的刷新令牌*");
}
}