我们正在尝试通过跳跃到未来(通过使用我们的 IClock 抽象)来加速一些集成测试(.NET 8)。
这本身工作正常,但在创建一个新的 JWT 令牌(也具有正确的时间戳)后,验证失败并显示
www-authenticate: Bearer error="invalid_token",error_description="令牌在 '07/26/2024 17:56:05'" 之前无效
所以看起来身份验证中间件仍在使用系统时间,这有点奇怪,因为我们将时间提供者传递给了
AddJwtBearer
:
services.AddSingleton<IClock>(ClockMutable.Default);
services
.AddAuthentication()
.AddJwtBearer(AuthenticationSchemes.Default, c => {
...
c.TimeProvider = ClockMutable.Default;
...
});
也不会调用
ClockMutable.Default
实例的任何属性。
我需要做什么才能
AddJwtBearer
使用提供的TimeProvider
?
编辑: IClock 实现
public class ClockMutable : TimeProvider, IClock {
private DateTimeOffset? stoppedOn;
private TimeSpan offset;
public DateTimeOffset Now => (stoppedOn != null ? stoppedOn.Value.ToLocalTime() : DateTimeOffset.Now) + offset;
public DateTimeOffset UtcNow => (stoppedOn != null ? stoppedOn.Value : DateTimeOffset.UtcNow) + offset;
public DateTime UtcDateTime => DateTime.UtcNow;
public void Resume() {
if(stoppedOn == null) return;
offset += stoppedOn.Value - DateTimeOffset.UtcNow;
stoppedOn = null;
}
public void Pause() {
if(stoppedOn != null) return;
stoppedOn = DateTimeOffset.UtcNow;
}
public void SkipForward(TimeSpan delta) {
if(delta < TimeSpan.Zero) throw new Exception();
offset += delta;
}
public void Set(DateTimeOffset future) {
if(future < UtcNow) throw new Exception();
offset = TimeSpan.Zero;
stoppedOn = future;
}
public void Reset() {
stoppedOn = null;
offset = TimeSpan.Zero;
}
public override DateTimeOffset GetUtcNow() => UtcNow;
}
由于未调用实例,可能是您的提供商中未持续注册服务的问题。我这里有一个解决方法。
1.在IntegrationTestFixture.cs中再次注册实例
public class IntegrationTestFixture : IDisposable
{
public ServiceProvider ServiceProvider { get; private set; }
public MockClock MockClock { get; private set; }
public IntegrationTestFixture()
{
var serviceCollection = new ServiceCollection();
// Register the instance here
var initialTime = new DateTime(2024, 7, 30);
MockClock = new MockClock(initialTime);
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.Development.json", optional: true, reloadOnChange: true)
.Build();
serviceCollection.AddSingleton<IConfiguration>(configuration);
serviceCollection.AddSingleton<IClock>(MockClock);
serviceCollection.AddSingleton<TimeProvider>(new CustomTimeProvider(MockClock));
serviceCollection.AddTransient<TokenService>();
serviceCollection.AddTransient<MyService>();
ServiceProvider = serviceCollection.BuildServiceProvider();
}
public void Dispose()
{
if (ServiceProvider is IDisposable disposable)
{
disposable.Dispose();
}
}
}
2。然后在测试 MyServiceIntegrationTests.cs 中,令牌在模拟时间验证。
public class MyServiceIntegrationTests :
IClassFixture<IntegrationTestFixture>
{
private readonly IntegrationTestFixture _fixture;
public MyServiceIntegrationTests(IntegrationTestFixture fixture)
{
_fixture = fixture;
}
[Fact]
public void Test_DoSomething()
{
var tokenService = _fixture.ServiceProvider.GetService<TokenService>();
if (tokenService == null)
throw new InvalidOperationException("Failed to retrieve TokenService from service provider.");
var token = tokenService.GenerateToken();
// Act
// Simulate advancing time to check if the token will be valid after a certain time
_fixture.MockClock.Advance(TimeSpan.FromMinutes(15));
var isTokenValidAfter15Minutes = tokenService.ValidateToken(token);
_fixture.MockClock.Advance(TimeSpan.FromMinutes(20));
var isTokenValidAfter35Minutes = tokenService.ValidateToken(token); // token should now be expired
// Assert
Assert.True(isTokenValidAfter15Minutes);
Assert.False(isTokenValidAfter35Minutes);
}
}