95 lines
4.0 KiB
C#
95 lines
4.0 KiB
C#
using System.Globalization;
|
|
using ItWorx.KnowledgeHub.Domain.Jobs;
|
|
|
|
namespace ItWorx.KnowledgeHub.Domain.Tests;
|
|
|
|
public sealed class BackgroundJobTests
|
|
{
|
|
private static BackgroundJob NewJob(int total = 2) => BackgroundJob.Create(
|
|
Guid.NewGuid(), Guid.NewGuid(), Guid.NewGuid(), BackgroundJobType.CourseTagging,
|
|
"{}", new string('a', 64), new string('b', 64), "correlation", DateTimeOffset.UtcNow, total);
|
|
|
|
[Fact]
|
|
public void A_retried_job_can_resume_and_finish_without_losing_its_original_start_time()
|
|
{
|
|
var job = NewJob();
|
|
var now = DateTimeOffset.Parse("2026-10-02T12:00:00Z", CultureInfo.InvariantCulture);
|
|
job.Start(Guid.NewGuid(), now, now.AddMinutes(1));
|
|
job.ScheduleRetry(now.AddSeconds(1), now.AddSeconds(3), "worker_retry", "Tijdelijke fout.");
|
|
|
|
Assert.Equal(BackgroundJobStatus.Queued, job.Status);
|
|
Assert.Equal(0, job.Failed);
|
|
job.Start(Guid.NewGuid(), now.AddSeconds(3), now.AddMinutes(2));
|
|
job.RecordOutcome(BackgroundJobItemState.Succeeded);
|
|
job.RecordOutcome(BackgroundJobItemState.Succeeded);
|
|
job.Complete(now.AddSeconds(4), "{\"complete\":true}");
|
|
|
|
Assert.Equal(BackgroundJobStatus.Succeeded, job.Status);
|
|
Assert.Equal(now, job.StartedAt);
|
|
Assert.Equal(2, job.Processed);
|
|
Assert.Equal(2, job.Succeeded);
|
|
Assert.Equal(100, job.Progress);
|
|
Assert.Equal("{\"complete\":true}", job.ResultJson);
|
|
}
|
|
|
|
[Fact]
|
|
public void The_worker_stops_after_three_retries_and_records_a_bounded_safe_failure()
|
|
{
|
|
var job = NewJob();
|
|
var now = DateTimeOffset.Parse("2026-10-02T12:00:00Z", CultureInfo.InvariantCulture);
|
|
|
|
for (var attempt = 1; attempt <= 4; attempt++)
|
|
{
|
|
var attemptAt = now.AddSeconds(attempt * 10);
|
|
job.Start(Guid.NewGuid(), attemptAt, attemptAt.AddMinutes(1));
|
|
job.ScheduleRetry(attemptAt, attemptAt.AddSeconds(1), "processing_retry", "Veilige foutmelding.");
|
|
if (attempt < 4)
|
|
Assert.Equal(BackgroundJobStatus.Queued, job.Status);
|
|
}
|
|
|
|
Assert.Equal(BackgroundJobStatus.Failed, job.Status);
|
|
Assert.Equal(1, job.Failed);
|
|
Assert.Equal(1, job.Processed);
|
|
Assert.Equal(4, job.AttemptCount);
|
|
Assert.True(job.ErrorsJson.Length < 4096);
|
|
Assert.Contains("Veilige foutmelding", job.ErrorsJson, StringComparison.Ordinal);
|
|
}
|
|
|
|
[Fact]
|
|
public void Cancellation_is_immediate_for_queued_jobs_and_cooperative_for_running_jobs()
|
|
{
|
|
var queued = NewJob();
|
|
var now = DateTimeOffset.UtcNow;
|
|
queued.RequestCancellation(now);
|
|
Assert.Equal(BackgroundJobStatus.Cancelled, queued.Status);
|
|
Assert.True(queued.IsTerminal);
|
|
|
|
var running = NewJob();
|
|
running.Start(Guid.NewGuid(), now, now.AddMinutes(1));
|
|
running.RequestCancellation(now.AddSeconds(1));
|
|
Assert.Equal(BackgroundJobStatus.Running, running.Status);
|
|
Assert.True(running.CancelRequestedAt.HasValue);
|
|
running.Cancel(now.AddSeconds(2));
|
|
Assert.Equal(BackgroundJobStatus.Cancelled, running.Status);
|
|
Assert.Equal(0, running.Processed);
|
|
}
|
|
|
|
[Fact]
|
|
public void Errors_are_capped_and_result_json_is_bounded()
|
|
{
|
|
var job = NewJob(30);
|
|
for (var index = 0; index < 30; index++)
|
|
job.RecordOutcome(BackgroundJobItemState.Failed, "page_failed", new string('x', 500), Guid.NewGuid());
|
|
job.Complete(DateTimeOffset.UtcNow, new string('z', 5000));
|
|
|
|
Assert.Equal(BackgroundJobStatus.Failed, job.Status);
|
|
Assert.Null(job.ResultJson);
|
|
Assert.Equal(BackgroundJob.MaxStoredErrors, System.Text.Json.JsonSerializer.Deserialize<BackgroundJobError[]>(job.ErrorsJson)!.Length);
|
|
Assert.All(System.Text.Json.JsonSerializer.Deserialize<BackgroundJobError[]>(job.ErrorsJson)!, error =>
|
|
{
|
|
Assert.Equal(BackgroundJob.MaxErrorMessageLength, error.Message.Length);
|
|
Assert.Equal("page_failed", error.Code);
|
|
});
|
|
}
|
|
}
|