Skip to content

Commit 3efdda1

Browse files
falvarez1linkdotnet
authored andcommitted
fix buggy unit test
1 parent cb5f10f commit 3efdda1

File tree

3 files changed

+47
-16
lines changed

3 files changed

+47
-16
lines changed

src/NCronJob/Registry/JobState.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ public JobState(JobStateType type, string? message = default)
1717
}
1818

1919
private string DebuggerDisplay => $"Type = {Type}, Timestamp = {Timestamp}";
20+
21+
public static implicit operator JobStateType(JobState jobState) => jobState.Type;
2022
}
2123

2224
internal enum JobStateType

tests/NCronJob.Tests/NCronJobRetryTests.cs

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System.Collections.Specialized;
12
using System.Threading.Channels;
23
using Microsoft.Extensions.DependencyInjection;
34
using Microsoft.Extensions.Hosting;
@@ -14,7 +15,7 @@ public async Task JobShouldRetryOnFailure()
1415
{
1516
var fakeTimer = new FakeTimeProvider();
1617
ServiceCollection.AddSingleton<TimeProvider>(fakeTimer);
17-
ServiceCollection.AddSingleton<MaxFailuresWrapper>(new MaxFailuresWrapper(3));
18+
ServiceCollection.AddSingleton<MaxFailuresWrapper>(new MaxFailuresWrapper(2));
1819
ServiceCollection.AddNCronJob(n => n.AddJob<FailingJob>(p => p.WithCronExpression("* * * * *")));
1920
var provider = CreateServiceProvider();
2021

@@ -23,9 +24,9 @@ public async Task JobShouldRetryOnFailure()
2324
fakeTimer.Advance(TimeSpan.FromMinutes(1));
2425

2526
// Validate that the job was retried the correct number of times
26-
// Fail 3 times = 3 retries + 1 success
27+
// Total = 2 retries + 1 success
2728
var attempts = await CommunicationChannel.Reader.ReadAsync(CancellationToken);
28-
attempts.ShouldBe(4);
29+
attempts.ShouldBe(3);
2930
}
3031

3132
[Fact]
@@ -101,18 +102,44 @@ public async Task CancelledJobIsStillAValidExecution()
101102
ServiceCollection.AddNCronJob(n => n.AddJob<CancelRetryingJob2>(p => p.WithCronExpression("* * * * *")));
102103
var provider = CreateServiceProvider();
103104
var jobExecutor = provider.GetRequiredService<JobExecutor>();
104-
var cronRegistryEntries = provider.GetServices<JobDefinition>();
105-
var cancelRetryingJobEntry = cronRegistryEntries.First(entry => entry.Type == typeof(CancelRetryingJob2));
105+
var jobQueueManager = provider.GetRequiredService<JobQueueManager>();
106+
var jobQueue = jobQueueManager.GetOrAddQueue(typeof(CancelRetryingJob2).FullName!);
107+
108+
JobRun? nextJob = null;
109+
var tcs = new TaskCompletionSource<JobStateType>();
110+
111+
jobQueue.CollectionChanged += (sender, args) =>
112+
{
113+
if (args.Action == NotifyCollectionChangedAction.Add && nextJob == null)
114+
{
115+
nextJob = args.NewItems?.OfType<JobRun>().FirstOrDefault();
116+
nextJob!.CurrentState.Type.ShouldBe(JobStateType.NotStarted);
117+
nextJob!.JobExecutionCount.ShouldBe(0);
118+
nextJob!.OnStateChanged += (s, e) =>
119+
{
120+
if (e == JobStateType.Cancelled)
121+
{
122+
tcs.SetResult(e);
123+
}
124+
};
125+
}
126+
};
127+
106128
await provider.GetRequiredService<IHostedService>().StartAsync(CancellationToken);
107129
fakeTimer.Advance(TimeSpan.FromMinutes(1));
108-
130+
131+
while (nextJob!.CurrentState != JobStateType.Retrying)
132+
{
133+
await Task.Delay(1);
134+
}
135+
// wait until we're in retrying state before cancelling
109136
jobExecutor.CancelJobs();
110137

111-
var cancellationHandled = await Task.WhenAny(CancellationSignaled, Task.Delay(100));
112-
cancellationHandled.ShouldBe(CancellationSignaled);
113-
114-
var jobRun = provider.GetRequiredService<IJobHistory>().GetAll().Single(s => s.JobDefinition == cancelRetryingJobEntry);
115-
jobRun.JobExecutionCount.ShouldBe(1);
138+
var cancellationHandled = await Task.WhenAny(tcs.Task, Task.Delay(1000));
139+
cancellationHandled.ShouldBe(tcs.Task);
140+
await Task.Delay(10);
141+
nextJob!.CurrentState.Type.ShouldBe(JobStateType.Cancelled);
142+
nextJob!.JobExecutionCount.ShouldBe(1);
116143
}
117144

118145
[Fact]
@@ -143,7 +170,7 @@ public async Task JobShouldHonorApplicationCancellationDuringRetry()
143170

144171
private sealed record MaxFailuresWrapper(int MaxFailuresBeforeSuccess = 3);
145172

146-
[RetryPolicy(retryCount: 4, PolicyType.FixedInterval)]
173+
[RetryPolicy(retryCount: 3, PolicyType.FixedInterval)]
147174
private sealed class FailingJob(ChannelWriter<object> writer, MaxFailuresWrapper maxFailuresWrapper)
148175
: IJob
149176
{
@@ -229,15 +256,13 @@ public async Task RunAsync(JobExecutionContext context, CancellationToken token)
229256
}
230257
}
231258

232-
[RetryPolicy(retryCount: 4, PolicyType.FixedInterval)]
259+
[RetryPolicy(retryCount: 2, PolicyType.FixedInterval)]
233260
private sealed class CancelRetryingJob2 : IJob
234261
{
235262
public Task RunAsync(JobExecutionContext context, CancellationToken token)
236263
{
237264
token.ThrowIfCancellationRequested();
238-
return !token.IsCancellationRequested
239-
? throw new InvalidOperationException("Job Failed")
240-
: Task.CompletedTask;
265+
throw new InvalidOperationException("Job Failed");
241266
}
242267
}
243268

tests/NCronJob.Tests/TestRetryHandler.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ public async Task ExecuteAsync(Func<CancellationToken, Task> operation, JobExecu
2121
await retryPolicy.ExecuteAsync((ct) =>
2222
{
2323
runContext.Attempts++;
24+
if (runContext.Attempts > 1)
25+
{
26+
runContext.JobRun.NotifyStateChange(JobStateType.Retrying);
27+
}
2428
return operation(ct);
2529
}, cancellationToken).ConfigureAwait(false);
2630
}

0 commit comments

Comments
 (0)