Skip to content

Commit

Permalink
Fixes for command scheduling
Browse files Browse the repository at this point in the history
  • Loading branch information
aritchie committed Jan 24, 2025
1 parent 20d4a3b commit b85d86e
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/Shiny.Mediator/RegistrationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public static ShinyConfigurator AddCommandScheduling<TScheduler>(this ShinyConfi
{
configurator.Services.AddSingleton<ICommandScheduler, TScheduler>();
configurator.AddOpenCommandMiddleware(typeof(ScheduledCommandMiddleware<>));
configurator.Services.TryAddSingleton(TimeProvider.System);
return configurator;
}

Expand Down
15 changes: 12 additions & 3 deletions src/Shiny.Mediator/Services/Impl/InMemoryCommandScheduler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,25 @@ namespace Shiny.Mediator.Services.Impl;
public class InMemoryCommandScheduler : ICommandScheduler
{
readonly List<CommandContext> commands = new();
readonly TimeProvider timeProvider;
readonly ILogger logger;
readonly IMediator mediator;
readonly Timer timer = new();


public InMemoryCommandScheduler(IMediator mediator, ILogger<ICommandScheduler> logger)
public InMemoryCommandScheduler(
IMediator mediator,
ILogger<ICommandScheduler> logger,
TimeProvider timeProvider
)
{
this.mediator = mediator;
this.logger = logger;

// TODO: timeprovider
this.timeProvider = timeProvider;

// var tt = this.timeProvider.CreateTimer(null, null, TimeSpan.Zero, TimeSpan.FromSeconds(30));
this.timer.Interval = TimeSpan.FromSeconds(15).TotalMilliseconds;
this.timer.Elapsed += this.OnTimerElapsed;
}
Expand Down Expand Up @@ -52,7 +62,7 @@ protected virtual async void OnTimerElapsed(object? sender, ElapsedEventArgs e)
foreach (var item in items)
{
var command = (IScheduledCommand)item.Command;
if (command.DueAt >= DateTimeOffset.UtcNow)
if (command.DueAt < this.timeProvider.GetUtcNow())
{
var headers = item
.Values
Expand All @@ -64,7 +74,6 @@ protected virtual async void OnTimerElapsed(object? sender, ElapsedEventArgs e)
await this.mediator
.Send(command, CancellationToken.None, headers)
.ConfigureAwait(false);

}
catch (Exception ex)
{
Expand Down
1 change: 1 addition & 0 deletions src/Shiny.Mediator/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ public static class Utils
}
return result;
}


public static TAttribute? GetHandlerHandleMethodAttribute<TCommand, TAttribute>(this ICommandHandler handler) where TAttribute : Attribute where TCommand : ICommand
=> handler
Expand Down
6 changes: 6 additions & 0 deletions tests/Shiny.Mediator.Tests/AttributeTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace Shiny.Mediator.Tests;

public class AttributeTests
{

}
51 changes: 51 additions & 0 deletions tests/Shiny.Mediator.Tests/CommandSchedulingTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Time.Testing;
using Xunit.Abstractions;

namespace Shiny.Mediator.Tests;

public class CommandSchedulingTests(ITestOutputHelper output)
{
[Fact]
public async Task EndToEndTest()
{
MyScheduleCommandHandler.Received = false;

var services = new ServiceCollection();
var time = new FakeTimeProvider();

services.AddSingleton<TimeProvider>(time);
services.AddShinyMediator(x =>
{
x.AddInMemoryCommandScheduling();
}, false);
services.AddLogging(x => x.AddXUnit(output));
services.AddSingletonAsImplementedInterfaces<MyScheduleCommandHandler>();

var sp = services.BuildServiceProvider();
var mediator = sp.GetRequiredService<IMediator>();
await mediator.Send(new MySchedule
{
DueAt = DateTimeOffset.UtcNow.AddMinutes(1)
});
MyScheduleCommandHandler.Received.ShouldBeFalse();
time.Advance(TimeSpan.FromMinutes(1.5));

await Task.Delay(1000); // let the thing execute now
MyScheduleCommandHandler.Received.ShouldBeTrue();
}
}

public class MySchedule : IScheduledCommand
{
public DateTimeOffset DueAt { get; set; }
}

public class MyScheduleCommandHandler : ICommandHandler<MySchedule>
{
public static bool Received { get; set; }
public async Task Handle(MySchedule command, CommandContext<MySchedule> context, CancellationToken cancellationToken)
{
Received = true;
}
}
1 change: 1 addition & 0 deletions tests/Shiny.Mediator.Tests/Shiny.Mediator.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
<PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.11.0" PrivateAssets="all"/>
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0"/>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.0"/>
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" Version="9.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
<PackageReference Include="MartinCostello.Logging.XUnit" Version="0.5.1"/>
<PackageReference Include="Npgsql" Version="9.0.2"/>
Expand Down

0 comments on commit b85d86e

Please sign in to comment.