Skip to content

Commit

Permalink
Fix RunUntil and add more tests for RunUntil and MaxRuns
Browse files Browse the repository at this point in the history
Two new integration tests have been added to verify task execution until max runs are reached and until run time expires. The verification and storage of RunUntil time in recurring tasks have been updated to use UTC. This ensures consistency and accuracy, especially in scenarios where server times may differ.
  • Loading branch information
GiampaoloGabba committed Apr 9, 2024
1 parent 568029b commit 48825d4
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,11 @@ public IBuildableSchedulerBuilder AtTimes(params TimeOnly[] times)

public IBuildableSchedulerBuilder RunUntil(DateTimeOffset runUntil)
{
if (runUntil.ToUniversalTime() < DateTimeOffset.UtcNow)
var runUntilUtc = runUntil.ToUniversalTime();
if (runUntilUtc < DateTimeOffset.UtcNow)
throw new InvalidOperationException("RunUntil cannot be in the past");

task.RunUntil = runUntil;
task.RunUntil = runUntilUtc;
return new BuildableSchedulerBuilder(task);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ public IMinuteSchedulerBuilder AtMinute(int minute)

public IBuildableSchedulerBuilder RunUntil(DateTimeOffset runUntil)
{
if (runUntil.ToUniversalTime() < DateTimeOffset.UtcNow)
var runUntilUtc = runUntil.ToUniversalTime();
if (runUntilUtc < DateTimeOffset.UtcNow)
throw new InvalidOperationException("RunUntil cannot be in the past");

task.RunUntil = runUntil;
task.RunUntil = runUntilUtc;
return new BuildableSchedulerBuilder(task);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ public IBuildableSchedulerBuilder AtSecond(int second)

public IBuildableSchedulerBuilder RunUntil(DateTimeOffset runUntil)
{
if (runUntil.ToUniversalTime() < DateTimeOffset.UtcNow)
var runUntilUtc = runUntil.ToUniversalTime();
if (runUntilUtc < DateTimeOffset.UtcNow)
throw new InvalidOperationException("RunUntil cannot be in the past");

task.RunUntil = runUntil;
task.RunUntil = runUntilUtc;
return new BuildableSchedulerBuilder(task);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ public IDailyTimeSchedulerBuilder OnFirst(DayOfWeek day)

public IBuildableSchedulerBuilder RunUntil(DateTimeOffset runUntil)
{
if (runUntil.ToUniversalTime() < DateTimeOffset.UtcNow)
var runUntilUtc = runUntil.ToUniversalTime();
if (runUntilUtc < DateTimeOffset.UtcNow)
throw new InvalidOperationException("RunUntil cannot be in the past");

task.RunUntil = runUntil;
task.RunUntil = runUntilUtc;
return new BuildableSchedulerBuilder(task);
}

Expand Down
11 changes: 6 additions & 5 deletions src/EverTask/Scheduler/Recurring/Builder/RecurringTaskBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,17 @@ public class ThenableSchedulerBuilder(RecurringTask recurringTask) : IThenableSc
public IIntervalSchedulerBuilder Then() => new IntervalSchedulerBuilder(recurringTask);
}

public class BuildableSchedulerBuilder(RecurringTask recurringTask) : IBuildableSchedulerBuilder
public class BuildableSchedulerBuilder(RecurringTask task) : IBuildableSchedulerBuilder
{
public IBuildableSchedulerBuilder RunUntil(DateTimeOffset runUntil)
{
if (runUntil.ToUniversalTime() < DateTimeOffset.UtcNow)
var runUntilUtc = runUntil.ToUniversalTime();
if (runUntilUtc < DateTimeOffset.UtcNow)
throw new InvalidOperationException("RunUntil cannot be in the past");

recurringTask.RunUntil = runUntil;
return new BuildableSchedulerBuilder(recurringTask);
task.RunUntil = runUntilUtc;
return new BuildableSchedulerBuilder(task);
}

public void MaxRuns(int maxRuns) => recurringTask.MaxRuns = maxRuns;
public void MaxRuns(int maxRuns) => task.MaxRuns = maxRuns;
}
4 changes: 2 additions & 2 deletions src/EverTask/Scheduler/Recurring/RecurringTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public RecurringTask() { }

current = current.ToUniversalTime();

if (RunUntil!=null && RunUntil >= DateTimeOffset.UtcNow) return null;
if (RunUntil < current) return null;

var next = GetNextOccurrence(current);

Expand Down Expand Up @@ -62,7 +62,7 @@ public RecurringTask() { }
if (!string.IsNullOrEmpty(CronInterval?.CronExpression))
{
var nextCron = CronInterval.GetNextOccurrence(current);
if (nextCron == null || nextCron > RunUntil)
if (nextCron == null || RunUntil < nextCron)
return null;

return nextCron;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,67 @@ public async Task Should_execute_task_with_standard_custom_policy()
TestTaskWithCustomRetryPolicy.Counter.ShouldBe(5);
}

[Fact]
public async Task Should_execute_task_with_max_run_until_max_run_reached()
{
await _host.StartAsync();

var task = new TestTaskDelayed1();
TestTaskDelayed1.Counter = 0;
await _dispatcher.Dispatch(task, builder => builder.RunNow().Then().EverySecond().MaxRuns(3));

await Task.Delay(3500);

var pt = await _storage.RetrievePending();
pt.Length.ShouldBe(0);

var tasks = await _storage.GetAll();

tasks.Length.ShouldBe(1);
tasks[0].Status.ShouldBe(QueuedTaskStatus.Completed);
tasks[0].MaxRuns = tasks[0].RunsAudits.Count(x=>x.Status == QueuedTaskStatus.Completed);
tasks[0].LastExecutionUtc.ShouldNotBeNull();
tasks[0].Exception.ShouldBeNull();

var cts = new CancellationTokenSource();
cts.CancelAfter(2000);

await _host.StopAsync(cts.Token);

TestTaskDelayed1.Counter.ShouldBe(3);
}

[Fact]
public async Task Should_execute_task_with_run_at_until_expires()
{
await _host.StartAsync();

var task = new TestTaskDelayed1();
TestTaskDelayed1.Counter = 0;
await _dispatcher.Dispatch(task, builder => builder.RunNow().Then().EverySecond().RunUntil(DateTimeOffset.Now.AddSeconds(4)));

await Task.Delay(4000);

var pt = await _storage.RetrievePending();
pt.Length.ShouldBe(0);

var tasks = await _storage.GetAll();

tasks.Length.ShouldBe(1);
tasks[0].Status.ShouldBe(QueuedTaskStatus.Completed);
tasks[0].RunsAudits.Count(x=>x.Status == QueuedTaskStatus.Completed).ShouldBe(3);
tasks[0].LastExecutionUtc.ShouldNotBeNull();
tasks[0].Exception.ShouldBeNull();

var cts = new CancellationTokenSource();
cts.CancelAfter(2000);

await _host.StopAsync(cts.Token);

TestTaskDelayed1.Counter.ShouldBe(3);
}


[Fact]
public async Task Should_not_execute_task_with_custom_timeout_excedeed()
{
Expand Down

0 comments on commit 48825d4

Please sign in to comment.