Browse Source

Bump Quartz.NET to 4.0.0 on .NET 10 and refactor the OpenIddict package to work with both 3.x and 4.x

pull/2540/head
Kévin Chalet 5 days ago
parent
commit
34d4a2e3cb
  1. 8
      Directory.Packages.props
  2. 1
      sandbox/OpenIddict.Sandbox.AspNet.Client/OpenIddict.Sandbox.AspNet.Client.csproj
  3. 1
      sandbox/OpenIddict.Sandbox.AspNetCore.Client/OpenIddict.Sandbox.AspNetCore.Client.csproj
  4. 1
      sandbox/OpenIddict.Sandbox.AspNetCore.Server/OpenIddict.Sandbox.AspNetCore.Server.csproj
  5. 4
      src/OpenIddict.Quartz/OpenIddict.Quartz.csproj
  6. 27
      src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs
  7. 36
      src/OpenIddict.Quartz/OpenIddictQuartzExtensions.cs
  8. 33
      src/OpenIddict.Quartz/OpenIddictQuartzJob.cs
  9. 44
      test/OpenIddict.Quartz.Tests/OpenIddictQuartzConfigurationTests.cs
  10. 15
      test/OpenIddict.Quartz.Tests/OpenIddictQuartzExtensionsTests.cs
  11. 206
      test/OpenIddict.Quartz.Tests/OpenIddictQuartzJobTests.cs

8
Directory.Packages.props

@ -48,7 +48,8 @@
<PackageVersion Include="MongoDB.Driver" Version="3.11.0" />
<PackageVersion Include="MSBuild.Microsoft.VisualStudio.Web.targets" Version="14.0.0.3" />
<PackageVersion Include="NuGet.Build.Tasks.Pack" Version="6.14.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.19.1" />
<PackageVersion Include="Quartz" Version="[3.20.1, 4.0.0)" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="[3.20.1, 4.0.0)" />
<PackageVersion Include="SharpCompress" Version="1.0.0" />
<PackageVersion Include="Snappier" Version="1.3.1" />
<PackageVersion Include="System.Buffers" Version="4.6.1" />
@ -92,7 +93,7 @@
<PackageVersion Include="Microsoft.Owin.Security.OAuth" Version="4.2.3" />
<PackageVersion Include="Microsoft.Web.Infrastructure" Version="2.0.1" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="Quartz.Extensions.Hosting" Version="3.19.1" />
<PackageVersion Include="Quartz.Extensions.Hosting" Version="[3.20.1, 4.0.0)" />
<PackageVersion Include="SourceGear.sqlite3" Version="3.53.4" />
<PackageVersion Include="Spectre.Console" Version="0.57.2" />
<PackageVersion Include="SQLite.CodeFirst" Version="1.8.0" />
@ -126,7 +127,7 @@
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="10.0.11" />
<PackageVersion Include="MongoDB.Bson" Version="3.11.0" />
<PackageVersion Include="MongoDB.Driver" Version="3.11.0" />
<PackageVersion Include="Quartz.Extensions.DependencyInjection" Version="3.19.1" />
<PackageVersion Include="Quartz" Version="4.0.0" />
<PackageVersion Include="SharpCompress" Version="1.0.0" />
<PackageVersion Include="Snappier" Version="1.3.1" />
<PackageVersion Include="System.Security.Cryptography.Xml" Version="10.0.11" />
@ -154,7 +155,6 @@
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.11" />
<PackageVersion Include="Microsoft.Maui.Controls" Version="10.0.90" />
<PackageVersion Include="Microsoft.Maui.Controls.Compatibility" Version="10.0.90" />
<PackageVersion Include="Quartz.Extensions.Hosting" Version="3.19.1" />
<PackageVersion Include="Spectre.Console" Version="0.57.2" />
</ItemGroup>

1
sandbox/OpenIddict.Sandbox.AspNet.Client/OpenIddict.Sandbox.AspNet.Client.csproj

@ -16,7 +16,6 @@
<ItemGroup>
<ProjectReference Include="..\..\src\OpenIddict.EntityFramework\OpenIddict.EntityFramework.csproj" />
<ProjectReference Include="..\..\src\OpenIddict.Quartz\OpenIddict.Quartz.csproj" />
<ProjectReference Include="..\..\src\OpenIddict.Owin\OpenIddict.Owin.csproj" />
</ItemGroup>

1
sandbox/OpenIddict.Sandbox.AspNetCore.Client/OpenIddict.Sandbox.AspNetCore.Client.csproj

@ -13,7 +13,6 @@
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<PackageReference Include="Quartz.Extensions.Hosting" />
</ItemGroup>
</Project>

1
sandbox/OpenIddict.Sandbox.AspNetCore.Server/OpenIddict.Sandbox.AspNetCore.Server.csproj

@ -16,7 +16,6 @@
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<PackageReference Include="Quartz.Extensions.Hosting" />
</ItemGroup>
</Project>

4
src/OpenIddict.Quartz/OpenIddict.Quartz.csproj

@ -14,6 +14,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Quartz" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' ">
<PackageReference Include="Quartz.Extensions.DependencyInjection" />
</ItemGroup>

27
src/OpenIddict.Quartz/OpenIddictQuartzConfiguration.cs

@ -14,7 +14,7 @@ namespace OpenIddict.Quartz;
/// Contains the methods required to ensure that the OpenIddict Quartz.NET configuration is valid.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictQuartzConfiguration : IConfigureOptions<QuartzOptions>, IPostConfigureOptions<OpenIddictQuartzOptions>
public sealed class OpenIddictQuartzConfiguration : IPostConfigureOptions<OpenIddictQuartzOptions>
{
private readonly IServiceProvider _provider;
@ -25,31 +25,6 @@ public sealed class OpenIddictQuartzConfiguration : IConfigureOptions<QuartzOpti
public OpenIddictQuartzConfiguration(IServiceProvider provider)
=> _provider = provider ?? throw new ArgumentNullException(nameof(provider));
/// <inheritdoc/>
public void Configure(QuartzOptions options)
{
ArgumentNullException.ThrowIfNull(options);
options.AddJob<OpenIddictQuartzJob>(builder =>
{
builder.StoreDurably()
.WithIdentity(OpenIddictQuartzJob.Identity)
.WithDescription(SR.GetResourceString(SR.ID8001));
});
options.AddTrigger(builder =>
{
// Note: this trigger uses a quite long interval (1 hour), which means it may be potentially never
// reached if the application is shut down or recycled. As such, this trigger is set up to fire
// between 1 and 10 minutes after the application starts to ensure the job is executed at least once.
builder.ForJob(OpenIddictQuartzJob.Identity)
.WithIdentity(SR.GetResourceString(SR.ID8004), SR.GetResourceString(SR.ID8005))
.WithSimpleSchedule(options => options.WithIntervalInHours(1).RepeatForever())
.WithDescription(SR.GetResourceString(SR.ID8002))
.StartAt(DateBuilder.FutureDate(new Random().Next(1, 10), IntervalUnit.Minute));
});
}
/// <inheritdoc/>
public void PostConfigure(string? name, OpenIddictQuartzOptions options)
{

36
src/OpenIddict.Quartz/OpenIddictQuartzExtensions.cs

@ -25,16 +25,38 @@ public static class OpenIddictQuartzExtensions
{
ArgumentNullException.ThrowIfNull(builder);
builder.Services.AddQuartz();
// Note: the AddQuartz() method MUST only be called once to avoid adding multiple jobs and triggers.
if (!builder.Services.Any(static descriptor => descriptor.ServiceType == typeof(OpenIddictQuartzJob)))
{
builder.Services.AddQuartz(options =>
{
options.AddJob<OpenIddictQuartzJob>(static builder =>
{
builder.StoreDurably()
.WithIdentity(OpenIddictQuartzJob.Identity)
.WithDescription(SR.GetResourceString(SR.ID8001));
});
// The OpenIddict job is registered as a service to allow
// Quartz.NET's DI integration to resolve it from the DI.
builder.Services.TryAddTransient<OpenIddictQuartzJob>();
options.AddTrigger(static builder =>
{
// Note: this trigger uses a quite long interval (1 hour), which means it may be potentially never
// reached if the application is shut down or recycled. As such, this trigger is set up to fire
// between 1 and 10 minutes after the application starts to ensure the job is executed at least once.
builder.ForJob(OpenIddictQuartzJob.Identity)
.WithIdentity(SR.GetResourceString(SR.ID8004), SR.GetResourceString(SR.ID8005))
.WithSimpleSchedule(options => options.WithInterval(TimeSpan.FromHours(1)).RepeatForever())
.WithDescription(SR.GetResourceString(SR.ID8002))
.StartAt(TimeProvider.System.GetUtcNow() + TimeSpan.FromMinutes(new Random().Next(1, 10)));
});
});
}
// Note: TryAddEnumerable() is used here to ensure the initializers are registered only once.
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<
IConfigureOptions<QuartzOptions>, OpenIddictQuartzConfiguration>());
#if !NET
// Note: unlike Quartz.NET 4.0+, Quartz.NET 3.x doesn't automatically register the job as a service.
builder.Services.TryAddTransient<OpenIddictQuartzJob>();
#endif
// Note: TryAddEnumerable() is used here to ensure the initializer is registered only once.
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<
IPostConfigureOptions<OpenIddictQuartzOptions>, OpenIddictQuartzConfiguration>());

33
src/OpenIddict.Quartz/OpenIddictQuartzJob.cs

@ -16,24 +16,21 @@ namespace OpenIddict.Quartz;
[DisallowConcurrentExecution, EditorBrowsable(EditorBrowsableState.Advanced)]
public sealed class OpenIddictQuartzJob : IJob
{
private readonly IOptionsMonitor<OpenIddictQuartzOptions> _options;
private readonly IServiceProvider _provider;
#if !NET
/// <summary>
/// Creates a new instance of the <see cref="OpenIddictQuartzJob"/> class.
/// </summary>
public OpenIddictQuartzJob() => throw new InvalidOperationException(SR.GetResourceString(SR.ID0082));
#endif
/// <summary>
/// Creates a new instance of the <see cref="OpenIddictQuartzJob"/> class.
/// </summary>
/// <param name="options">The OpenIddict Quartz.NET options.</param>
/// <param name="provider">The service provider.</param>
public OpenIddictQuartzJob(IOptionsMonitor<OpenIddictQuartzOptions> options, IServiceProvider provider)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
}
public OpenIddictQuartzJob(IServiceProvider provider)
=> _provider = provider ?? throw new ArgumentNullException(nameof(provider));
/// <summary>
/// Gets the default identity assigned to this job.
@ -43,20 +40,24 @@ public sealed class OpenIddictQuartzJob : IJob
group: SR.GetResourceString(SR.ID8005));
/// <inheritdoc/>
#if NET
public async ValueTask Execute(IJobExecutionContext context, CancellationToken cancellationToken = default)
#else
public async Task Execute(IJobExecutionContext context)
#endif
{
ArgumentNullException.ThrowIfNull(context);
List<Exception>? exceptions = null;
// Note: this job is registered as a transient service. As such, it cannot directly depend on scoped services
// like the core managers. To work around this limitation, a scope is manually created for each invocation.
await using var scope = _provider.CreateAsyncScope();
var options = scope.ServiceProvider.GetRequiredService<IOptionsMonitor<OpenIddictQuartzOptions>>().CurrentValue;
// Important: since authorizations that still have tokens attached are never
// pruned, the tokens MUST be deleted before deleting the authorizations.
if (!_options.CurrentValue.DisableTokenPruning)
if (!options.DisableTokenPruning)
{
var manager = scope.ServiceProvider.GetService<IOpenIddictTokenManager>()
?? throw new JobExecutionException(new InvalidOperationException(SR.GetResourceString(SR.ID0278)))
@ -66,7 +67,7 @@ public sealed class OpenIddictQuartzJob : IJob
UnscheduleFiringTrigger = true
};
var threshold = _options.CurrentValue.TimeProvider.GetUtcNow() - _options.CurrentValue.MinimumTokenLifespan;
var threshold = options.TimeProvider.GetUtcNow() - options.MinimumTokenLifespan;
try
{
@ -101,7 +102,7 @@ public sealed class OpenIddictQuartzJob : IJob
}
}
if (!_options.CurrentValue.DisableAuthorizationPruning)
if (!options.DisableAuthorizationPruning)
{
var manager = scope.ServiceProvider.GetService<IOpenIddictAuthorizationManager>()
?? throw new JobExecutionException(new InvalidOperationException(SR.GetResourceString(SR.ID0278)))
@ -111,7 +112,7 @@ public sealed class OpenIddictQuartzJob : IJob
UnscheduleFiringTrigger = true
};
var threshold = _options.CurrentValue.TimeProvider.GetUtcNow() - _options.CurrentValue.MinimumAuthorizationLifespan;
var threshold = options.TimeProvider.GetUtcNow() - options.MinimumAuthorizationLifespan;
try
{
@ -149,7 +150,7 @@ public sealed class OpenIddictQuartzJob : IJob
// Important: since sessions that still have tokens attached are never
// pruned, the tokens MUST be deleted before deleting the sessions.
if (!_options.CurrentValue.DisableSessionPruning)
if (!options.DisableSessionPruning)
{
var manager = scope.ServiceProvider.GetService<IOpenIddictSessionManager>()
?? throw new JobExecutionException(new InvalidOperationException(SR.GetResourceString(SR.ID0278)))
@ -159,7 +160,7 @@ public sealed class OpenIddictQuartzJob : IJob
UnscheduleFiringTrigger = true
};
var threshold = _options.CurrentValue.TimeProvider.GetUtcNow() - _options.CurrentValue.MinimumSessionLifespan;
var threshold = options.TimeProvider.GetUtcNow() - options.MinimumSessionLifespan;
try
{
@ -199,7 +200,7 @@ public sealed class OpenIddictQuartzJob : IJob
throw new JobExecutionException(new AggregateException(exceptions))
{
// Only refire the job if the maximum refire count set in the options wasn't reached.
RefireImmediately = context.RefireCount < _options.CurrentValue.MaximumRefireCount
RefireImmediately = context.RefireCount < options.MaximumRefireCount
};
}
}

44
test/OpenIddict.Quartz.Tests/OpenIddictQuartzConfigurationTests.cs

@ -1,44 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Quartz;
using Xunit;
namespace OpenIddict.Quartz.Tests;
public class OpenIddictQuartzConfigurationTests
{
[Fact]
public void UseQuartz_RegistersJobDetails()
{
// Arrange
var options = new QuartzOptions();
var configuration = new OpenIddictQuartzConfiguration(new ServiceCollection().BuildServiceProvider());
// Act
configuration.Configure(options);
// Assert
Assert.Single(options.JobDetails);
Assert.Equal(OpenIddictQuartzJob.Identity, options.JobDetails[0].Key);
Assert.Equal(SR.GetResourceString(SR.ID8003), options.JobDetails[0].Key.Name);
Assert.Equal(SR.GetResourceString(SR.ID8005), options.JobDetails[0].Key.Group);
Assert.Equal(SR.GetResourceString(SR.ID8001), options.JobDetails[0].Description);
}
[Fact]
public void UseQuartz_RegistersTriggerDetails()
{
// Arrange
var options = new QuartzOptions();
var configuration = new OpenIddictQuartzConfiguration(new ServiceCollection().BuildServiceProvider());
// Act
configuration.Configure(options);
// Assert
Assert.Single(options.Triggers);
Assert.Equal(OpenIddictQuartzJob.Identity, options.Triggers[0].JobKey);
Assert.Equal(SR.GetResourceString(SR.ID8004), options.Triggers[0].Key.Name);
Assert.Equal(SR.GetResourceString(SR.ID8005), options.Triggers[0].Key.Group);
Assert.Equal(SR.GetResourceString(SR.ID8002), options.Triggers[0].Description);
}
}

15
test/OpenIddict.Quartz.Tests/OpenIddictQuartzExtensionsTests.cs

@ -1,6 +1,5 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Quartz;
using Xunit;
namespace OpenIddict.Quartz.Tests;
@ -43,9 +42,15 @@ public class OpenIddictQuartzExtensionsTests
builder.UseQuartz();
// Assert
#if NET
Assert.Contains(services, service => service.ServiceType == typeof(OpenIddictQuartzJob) &&
service.ImplementationType == typeof(OpenIddictQuartzJob) &&
service.Lifetime is ServiceLifetime.Scoped);
#else
Assert.Contains(services, service => service.ServiceType == typeof(OpenIddictQuartzJob) &&
service.ImplementationType == typeof(OpenIddictQuartzJob) &&
service.Lifetime is ServiceLifetime.Transient);
#endif
}
[Fact]
@ -62,11 +67,9 @@ public class OpenIddictQuartzExtensionsTests
// Assert
Assert.Single(services, service => service.ServiceType == typeof(OpenIddictQuartzJob) &&
service.ImplementationType == typeof(OpenIddictQuartzJob) &&
service.Lifetime is ServiceLifetime.Transient);
service.ImplementationType == typeof(OpenIddictQuartzJob));
Assert.Single(services, service => service.ServiceType == typeof(IConfigureOptions<QuartzOptions>) &&
service.ImplementationType == typeof(OpenIddictQuartzConfiguration) &&
service.Lifetime is ServiceLifetime.Singleton);
Assert.Single(services, service => service.ServiceType == typeof(IPostConfigureOptions<OpenIddictQuartzOptions>) &&
service.ImplementationType == typeof(OpenIddictQuartzConfiguration));
}
}

206
test/OpenIddict.Quartz.Tests/OpenIddictQuartzJobTests.cs

@ -8,34 +8,37 @@ namespace OpenIddict.Quartz.Tests;
public class OpenIddictQuartzJobTests
{
#if !NET
[Fact]
public void Constructor_ThrowsAnException()
public void Constructor_ThrowsAnExceptionWhenServiceProviderCannotBeResolved()
{
// Arrange, act and assert
var exception = Assert.Throws<InvalidOperationException>(() => new OpenIddictQuartzJob());
Assert.Equal(SR.GetResourceString(SR.ID0082), exception.Message);
}
#endif
[Fact]
public async Task Execute_UsesServiceScope()
{
// Arrange
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
});
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == Mock.Of<IOpenIddictAuthorizationManager>() &&
provider.GetService(typeof(IOpenIddictSessionManager)) == Mock.Of<IOpenIddictSessionManager>() &&
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>());
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>() &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var scope = Mock.Of<IServiceScope>(scope => scope.ServiceProvider == provider);
var factory = Mock.Of<IServiceScopeFactory>(factory => factory.CreateScope() == scope);
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
});
var job = new OpenIddictQuartzJob(monitor,
Mock.Of<IServiceProvider>(provider => provider.GetService(typeof(IServiceScopeFactory)) == factory));
var job = new OpenIddictQuartzJob(Mock.Of<IServiceProvider>(provider => provider.GetService(typeof(IServiceScopeFactory)) == factory));
// Act
await job.Execute(Mock.Of<IJobExecutionContext>());
@ -48,15 +51,22 @@ public class OpenIddictQuartzJobTests
public async Task Execute_IgnoresPruningWhenTokenPruningIsDisabled()
{
// Arrange
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
DisableTokenPruning = true,
TimeProvider = TimeProvider.System
});
var manager = new Mock<IOpenIddictAuthorizationManager>();
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == Mock.Of<IOpenIddictAuthorizationManager>() &&
provider.GetService(typeof(IOpenIddictSessionManager)) == Mock.Of<IOpenIddictSessionManager>() &&
provider.GetService(typeof(IOpenIddictTokenManager)) == manager.Object);
provider.GetService(typeof(IOpenIddictTokenManager)) == manager.Object &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var job = CreateJob(provider, options => options.DisableTokenPruning = true);
var job = CreateJob(provider);
// Act
await job.Execute(Mock.Of<IJobExecutionContext>());
@ -69,15 +79,22 @@ public class OpenIddictQuartzJobTests
public async Task Execute_IgnoresPruningWhenAuthorizationPruningIsDisabled()
{
// Arrange
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
DisableAuthorizationPruning = true,
TimeProvider = TimeProvider.System
});
var manager = new Mock<IOpenIddictAuthorizationManager>();
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == manager.Object &&
provider.GetService(typeof(IOpenIddictSessionManager)) == Mock.Of<IOpenIddictSessionManager>() &&
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>());
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>() &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var job = CreateJob(provider, options => options.DisableAuthorizationPruning = true);
var job = CreateJob(provider);
// Act
await job.Execute(Mock.Of<IJobExecutionContext>());
@ -90,15 +107,22 @@ public class OpenIddictQuartzJobTests
public async Task Execute_IgnoresPruningWhenSessionPruningIsDisabled()
{
// Arrange
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
DisableSessionPruning = true,
TimeProvider = TimeProvider.System
});
var manager = new Mock<IOpenIddictSessionManager>();
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == Mock.Of<IOpenIddictAuthorizationManager>() &&
provider.GetService(typeof(IOpenIddictSessionManager)) == manager.Object &&
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>());
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>() &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var job = CreateJob(provider, options => options.DisableSessionPruning = true);
var job = CreateJob(provider);
// Act
await job.Execute(Mock.Of<IJobExecutionContext>());
@ -111,15 +135,23 @@ public class OpenIddictQuartzJobTests
public async Task Execute_UnschedulesTriggersWhenTokenManagerIsMissing()
{
// Arrange
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
});
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == Mock.Of<IOpenIddictAuthorizationManager>() &&
provider.GetService(typeof(IOpenIddictSessionManager)) == Mock.Of<IOpenIddictSessionManager>() &&
provider.GetService(typeof(IOpenIddictTokenManager)) == null);
provider.GetService(typeof(IOpenIddictTokenManager)) == null &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var job = CreateJob(provider);
// Act and assert
var exception = await Assert.ThrowsAsync<JobExecutionException>(() => job.Execute(Mock.Of<IJobExecutionContext>()));
var exception = await Assert.ThrowsAsync<JobExecutionException>(async () =>
await job.Execute(Mock.Of<IJobExecutionContext>()));
Assert.False(exception.RefireImmediately);
Assert.True(exception.UnscheduleAllTriggers);
@ -133,13 +165,21 @@ public class OpenIddictQuartzJobTests
public async Task Execute_UnschedulesTriggersWhenAuthorizationManagerIsMissing()
{
// Arrange
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
});
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == null);
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == null &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var job = CreateJob(provider);
// Act and assert
var exception = await Assert.ThrowsAsync<JobExecutionException>(() => job.Execute(Mock.Of<IJobExecutionContext>()));
var exception = await Assert.ThrowsAsync<JobExecutionException>(async () =>
await job.Execute(Mock.Of<IJobExecutionContext>()));
Assert.False(exception.RefireImmediately);
Assert.True(exception.UnscheduleAllTriggers);
@ -153,13 +193,21 @@ public class OpenIddictQuartzJobTests
public async Task Execute_UnschedulesTriggersWhenSessionManagerIsMissing()
{
// Arrange
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
});
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictSessionManager)) == null);
provider.GetService(typeof(IOpenIddictSessionManager)) == null &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var job = CreateJob(provider);
// Act and assert
var exception = await Assert.ThrowsAsync<JobExecutionException>(() => job.Execute(Mock.Of<IJobExecutionContext>()));
var exception = await Assert.ThrowsAsync<JobExecutionException>(async () =>
await job.Execute(Mock.Of<IJobExecutionContext>()));
Assert.False(exception.RefireImmediately);
Assert.True(exception.UnscheduleAllTriggers);
@ -173,42 +221,62 @@ public class OpenIddictQuartzJobTests
public async Task Execute_RethrowsOutOfMemoryExceptionsThrownDuringTokenPruning()
{
// Arrange
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
});
var manager = new Mock<IOpenIddictTokenManager>();
manager.Setup(manager => manager.PruneAsync(It.IsAny<DateTimeOffset>(), It.IsAny<CancellationToken>()))
.Throws(new OutOfMemoryException());
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == Mock.Of<IOpenIddictAuthorizationManager>() &&
provider.GetService(typeof(IOpenIddictTokenManager)) == manager.Object);
provider.GetService(typeof(IOpenIddictTokenManager)) == manager.Object &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var job = CreateJob(provider);
// Act and assert
await Assert.ThrowsAsync<OutOfMemoryException>(() => job.Execute(Mock.Of<IJobExecutionContext>()));
await Assert.ThrowsAsync<OutOfMemoryException>(async () => await job.Execute(Mock.Of<IJobExecutionContext>()));
}
[Fact]
public async Task Execute_RethrowsOutOfMemoryExceptionsThrownDuringAuthorizationPruning()
{
// Arrange
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
});
var manager = new Mock<IOpenIddictAuthorizationManager>();
manager.Setup(manager => manager.PruneAsync(It.IsAny<DateTimeOffset>(), It.IsAny<CancellationToken>()))
.Throws(new OutOfMemoryException());
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == manager.Object &&
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>());
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>() &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var job = CreateJob(provider);
// Act and assert
await Assert.ThrowsAsync<OutOfMemoryException>(() => job.Execute(Mock.Of<IJobExecutionContext>()));
await Assert.ThrowsAsync<OutOfMemoryException>(async () => await job.Execute(Mock.Of<IJobExecutionContext>()));
}
[Fact]
public async Task Execute_RethrowsOutOfMemoryExceptionsThrownDuringSessionPruning()
{
// Arrange
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
});
var manager = new Mock<IOpenIddictSessionManager>();
manager.Setup(manager => manager.PruneAsync(It.IsAny<DateTimeOffset>(), It.IsAny<CancellationToken>()))
.Throws(new OutOfMemoryException());
@ -216,12 +284,13 @@ public class OpenIddictQuartzJobTests
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == Mock.Of<IOpenIddictAuthorizationManager>() &&
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>() &&
provider.GetService(typeof(IOpenIddictSessionManager)) == manager.Object);
provider.GetService(typeof(IOpenIddictSessionManager)) == manager.Object &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var job = CreateJob(provider);
// Act and assert
await Assert.ThrowsAsync<OutOfMemoryException>(() => job.Execute(Mock.Of<IJobExecutionContext>()));
await Assert.ThrowsAsync<OutOfMemoryException>(async () => await job.Execute(Mock.Of<IJobExecutionContext>()));
}
[Fact]
@ -230,20 +299,27 @@ public class OpenIddictQuartzJobTests
// Arrange
var token = new CancellationToken(canceled: true);
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
});
var manager = new Mock<IOpenIddictTokenManager>();
manager.Setup(manager => manager.PruneAsync(It.IsAny<DateTimeOffset>(), It.IsAny<CancellationToken>()))
.Throws(new OperationCanceledException(token));
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == Mock.Of<IOpenIddictAuthorizationManager>() &&
provider.GetService(typeof(IOpenIddictTokenManager)) == manager.Object);
provider.GetService(typeof(IOpenIddictTokenManager)) == manager.Object &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var context = Mock.Of<IJobExecutionContext>(context => context.CancellationToken == token);
var job = CreateJob(provider);
// Act and assert
var exception = await Assert.ThrowsAsync<JobExecutionException>(() => job.Execute(context));
var exception = await Assert.ThrowsAsync<JobExecutionException>(async () => await job.Execute(context));
Assert.False(exception.RefireImmediately);
@ -256,20 +332,27 @@ public class OpenIddictQuartzJobTests
// Arrange
var token = new CancellationToken(canceled: true);
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
});
var manager = new Mock<IOpenIddictAuthorizationManager>();
manager.Setup(manager => manager.PruneAsync(It.IsAny<DateTimeOffset>(), It.IsAny<CancellationToken>()))
.Throws(new OperationCanceledException(token));
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == manager.Object &&
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>());
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>() &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var context = Mock.Of<IJobExecutionContext>(context => context.CancellationToken == token);
var job = CreateJob(provider);
// Act and assert
var exception = await Assert.ThrowsAsync<JobExecutionException>(() => job.Execute(context));
var exception = await Assert.ThrowsAsync<JobExecutionException>(async () => await job.Execute(context));
Assert.False(exception.RefireImmediately);
@ -282,6 +365,12 @@ public class OpenIddictQuartzJobTests
// Arrange
var token = new CancellationToken(canceled: true);
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
});
var manager = new Mock<IOpenIddictSessionManager>();
manager.Setup(manager => manager.PruneAsync(It.IsAny<DateTimeOffset>(), It.IsAny<CancellationToken>()))
.Throws(new OperationCanceledException(token));
@ -289,14 +378,15 @@ public class OpenIddictQuartzJobTests
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == Mock.Of<IOpenIddictAuthorizationManager>() &&
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>() &&
provider.GetService(typeof(IOpenIddictSessionManager)) == manager.Object);
provider.GetService(typeof(IOpenIddictSessionManager)) == manager.Object &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var context = Mock.Of<IJobExecutionContext>(context => context.CancellationToken == token);
var job = CreateJob(provider);
// Act and assert
var exception = await Assert.ThrowsAsync<JobExecutionException>(() => job.Execute(context));
var exception = await Assert.ThrowsAsync<JobExecutionException>(async () => await job.Execute(context));
Assert.False(exception.RefireImmediately);
@ -307,6 +397,12 @@ public class OpenIddictQuartzJobTests
public async Task Execute_AllowsRefiringWhenExceptionsAreThrown()
{
// Arrange
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
});
var provider = new Mock<IServiceProvider>();
provider.Setup(provider => provider.GetService(typeof(IOpenIddictAuthorizationManager)))
.Returns(CreateAuthorizationManager(new ApplicationException()));
@ -317,12 +413,15 @@ public class OpenIddictQuartzJobTests
provider.Setup(provider => provider.GetService(typeof(IOpenIddictTokenManager)))
.Returns(CreateTokenManager(new ApplicationException()));
provider.Setup(provider => provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)))
.Returns(monitor);
var context = Mock.Of<IJobExecutionContext>(context => context.RefireCount == 0);
var job = CreateJob(provider.Object);
// Act and assert
var exception = await Assert.ThrowsAsync<JobExecutionException>(() => job.Execute(context));
var exception = await Assert.ThrowsAsync<JobExecutionException>(async () => await job.Execute(context));
Assert.True(exception.RefireImmediately);
Assert.IsType<AggregateException>(exception.InnerException);
@ -363,6 +462,12 @@ public class OpenIddictQuartzJobTests
public async Task Execute_AllowsRefiringWhenAggregateExceptionsAreThrown()
{
// Arrange
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
});
var provider = new Mock<IServiceProvider>();
provider.Setup(provider => provider.GetService(typeof(IOpenIddictAuthorizationManager)))
.Returns(CreateAuthorizationManager(new AggregateException(
@ -376,12 +481,15 @@ public class OpenIddictQuartzJobTests
.Returns(CreateTokenManager(new AggregateException(
new InvalidOperationException(), new ApplicationException())));
provider.Setup(provider => provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)))
.Returns(monitor);
var context = Mock.Of<IJobExecutionContext>(context => context.RefireCount == 0);
var job = CreateJob(provider.Object);
// Act and assert
var exception = await Assert.ThrowsAsync<JobExecutionException>(() => job.Execute(context));
var exception = await Assert.ThrowsAsync<JobExecutionException>(async () => await job.Execute(context));
Assert.True(exception.RefireImmediately);
Assert.IsType<AggregateException>(exception.InnerException);
@ -425,6 +533,13 @@ public class OpenIddictQuartzJobTests
public async Task Execute_DisallowsRefiringWhenMaximumRefireCountIsReached()
{
// Arrange
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(
monitor => monitor.CurrentValue == new OpenIddictQuartzOptions
{
MaximumRefireCount = 5,
TimeProvider = TimeProvider.System
});
var manager = new Mock<IOpenIddictAuthorizationManager>();
manager.Setup(manager => manager.PruneAsync(It.IsAny<DateTimeOffset>(), It.IsAny<CancellationToken>()))
.Throws(new ApplicationException());
@ -432,32 +547,25 @@ public class OpenIddictQuartzJobTests
var provider = Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IOpenIddictAuthorizationManager)) == manager.Object &&
provider.GetService(typeof(IOpenIddictSessionManager)) == Mock.Of<IOpenIddictSessionManager>() &&
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>());
provider.GetService(typeof(IOpenIddictTokenManager)) == Mock.Of<IOpenIddictTokenManager>() &&
provider.GetService(typeof(IOptionsMonitor<OpenIddictQuartzOptions>)) == monitor);
var context = Mock.Of<IJobExecutionContext>(context => context.RefireCount == 5);
var job = CreateJob(provider, options => options.MaximumRefireCount = 5);
var job = CreateJob(provider);
// Act and assert
var exception = await Assert.ThrowsAsync<JobExecutionException>(() => job.Execute(context));
var exception = await Assert.ThrowsAsync<JobExecutionException>(async () => await job.Execute(context));
Assert.False(exception.RefireImmediately);
}
private static OpenIddictQuartzJob CreateJob(IServiceProvider provider, Action<OpenIddictQuartzOptions>? configuration = null)
private static OpenIddictQuartzJob CreateJob(IServiceProvider provider)
{
var scope = Mock.Of<IServiceScope>(scope => scope.ServiceProvider == provider);
var factory = Mock.Of<IServiceScopeFactory>(factory => factory.CreateScope() == scope);
var options = new OpenIddictQuartzOptions
{
TimeProvider = TimeProvider.System
};
configuration?.Invoke(options);
var monitor = Mock.Of<IOptionsMonitor<OpenIddictQuartzOptions>>(monitor => monitor.CurrentValue == options);
return new OpenIddictQuartzJob(monitor,
Mock.Of<IServiceProvider>(provider => provider.GetService(typeof(IServiceScopeFactory)) == factory));
return new OpenIddictQuartzJob(Mock.Of<IServiceProvider>(provider =>
provider.GetService(typeof(IServiceScopeFactory)) == factory));
}
}

Loading…
Cancel
Save