fix(worker-tests): close TOCTOU race in slot-failure broadcast poll
QueueServiceSlotFailureTests's throwing-slot test broke its poll loop the instant the DB read observed Status==Failed, but TaskStateService.FailAsync commits the status flip before calling the broadcaster's TaskUpdated, so the assertion could race ahead of the broadcast landing in hub.Proxy.Calls (~1-in-5 failures in isolation). Wait for both signals before breaking.
This commit is contained in:
@@ -132,13 +132,20 @@ public sealed class QueueServiceSlotFailureTests : IDisposable
|
||||
await service.StartAsync(cts.Token);
|
||||
waker.Wake();
|
||||
|
||||
// FailAsync (TaskStateService.cs:236-249) commits the DB status flip via
|
||||
// ExecuteUpdateAsync *before* it calls the broadcaster's TaskUpdated — so a poll that
|
||||
// breaks the instant it observes Status==Failed can race ahead of the broadcast still
|
||||
// landing in hub.Proxy.Calls. Wait for both signals together so the assertions below
|
||||
// never sample a genuinely-not-yet-broadcast window as a failure.
|
||||
TaskEntity? reloaded = null;
|
||||
var deadline = DateTime.UtcNow.AddSeconds(10);
|
||||
while (DateTime.UtcNow < deadline)
|
||||
{
|
||||
using var verify = _db.CreateContext();
|
||||
reloaded = await new TaskRepository(verify).GetByIdAsync(taskId);
|
||||
if (reloaded!.Status == TaskStatus.Failed) break;
|
||||
var broadcastSeen = hub.Proxy.Calls.Any(
|
||||
c => c.Method == "TaskUpdated" && (string)c.Args[0]! == taskId);
|
||||
if (reloaded!.Status == TaskStatus.Failed && broadcastSeen) break;
|
||||
await Task.Delay(25);
|
||||
}
|
||||
cts.Cancel();
|
||||
|
||||
Reference in New Issue
Block a user