Skip to main content

Worker test hooks

Polling loops often lead to slow tests built around arbitrary delays. GUSTO exposes two static barriers on JobQueueWorker<TRecord> so a test can control one batch cycle.

[Fact]
public async Task Worker_executes_an_enqueued_job()
{
var started = new TaskCompletionSource(
TaskCreationOptions.RunContinuationsAsynchronously);
var completed = new TaskCompletionSource(
TaskCreationOptions.RunContinuationsAsynchronously);

JobQueueWorker<JobRecord>.BatchStartBarrier = started;
JobQueueWorker<JobRecord>.BatchCompletedBarrier = completed;

try
{
await queue.EnqueueAsync<ExampleJobs>(jobs => jobs.DoWorkAsync("test"));

await hostedService.StartAsync(CancellationToken.None);
started.SetResult();
await completed.Task.WaitAsync(TimeSpan.FromSeconds(5));

Assert.True(provider.WasCompleted);
}
finally
{
await hostedService.StopAsync(CancellationToken.None);
JobQueueWorker<JobRecord>.BatchStartBarrier = null;
JobQueueWorker<JobRecord>.BatchCompletedBarrier = null;
}
}

BatchStartBarrier pauses before polling. BatchCompletedBarrier is signaled after the complete batch cycle, including an empty poll. Each barrier is cleared by the worker after use.

Isolate barrier tests​

The barriers are static per record type. Tests using the same TRecord can interfere if they run in parallel. Put them in a non-parallel test collection or use a distinct record type per fixture.

Always put an upper bound around waiting and clear barriers in finally. A failed assertion should not leave a shared test process blocked.

Test the provider separately​

Worker integration tests do not prove distributed claiming or retry transitions. Exercise provider behavior at its real database boundary, including:

  • two concurrent claims do not return the same record;
  • an expired claim becomes recoverable;
  • completed and expired records are excluded;
  • retry limits become terminal;
  • cancellation is idempotent.