Merge task branch for: fix(worker): planning-chain cascade stalls at an Idle middle link
This commit is contained in:
@@ -178,6 +178,37 @@ public sealed class PlanningChainCoordinatorTests : IDisposable
|
||||
Assert.Equal(TaskStatus.Cancelled, kids[3].Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OnChildFailed_MidChain_IdleSuccessor_DoesNotWedgeTail()
|
||||
{
|
||||
// Chain: c0 → c1 → c2 → c3. c1 is parked back to Idle (e.g. a manual reset)
|
||||
// while still wired into the chain (BlockedByTaskId = c0). c0 then fails:
|
||||
// CancelAsync(c1) is a no-op because Idle isn't a cancellable state, so the
|
||||
// cascade must keep walking past c1 instead of leaving c2/c3 wedged forever.
|
||||
await SeedPlanningFamilyAsync("P", 4);
|
||||
await _sut.SetupChainAsync("P", enqueue: true, default);
|
||||
|
||||
await using (var ctx = _factory.CreateDbContext())
|
||||
{
|
||||
var c0 = await ctx.Tasks.FirstAsync(t => t.Id == "P-c0");
|
||||
c0.Status = TaskStatus.Failed;
|
||||
var c1 = await ctx.Tasks.FirstAsync(t => t.Id == "P-c1");
|
||||
c1.Status = TaskStatus.Idle;
|
||||
await ctx.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var advanced = await _sut.OnChildFinishedAsync("P-c0", TaskStatus.Failed, default);
|
||||
|
||||
Assert.Null(advanced);
|
||||
var kids = await GetChildrenAsync("P");
|
||||
Assert.Equal(TaskStatus.Failed, kids[0].Status);
|
||||
// c1 was already parked Idle; the coordinator must not resurrect it.
|
||||
Assert.Equal(TaskStatus.Idle, kids[1].Status);
|
||||
// c2/c3 must not stay wedged as Queued+blocked forever.
|
||||
Assert.Equal(TaskStatus.Cancelled, kids[2].Status);
|
||||
Assert.Equal(TaskStatus.Cancelled, kids[3].Status);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task OnChildDone_LastChild_ReturnsNull()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user