using ClaudeDo.Data; using ClaudeDo.Data.Models; using ClaudeDo.Data.Repositories; using ClaudeDo.Worker.Tests.Infrastructure; using TaskStatus = ClaudeDo.Data.Models.TaskStatus; namespace ClaudeDo.Worker.Tests.Runner; public sealed class TaskRunnerParentCompletionTests : IDisposable { private readonly DbFixture _db = new(); private readonly ClaudeDoDbContext _ctx; private readonly TaskRepository _tasks; private readonly ListRepository _lists; public TaskRunnerParentCompletionTests() { _ctx = _db.CreateContext(); _tasks = new TaskRepository(_ctx); _lists = new ListRepository(_ctx); } public void Dispose() { _ctx.Dispose(); _db.Dispose(); } [Fact] public async Task ChildMarkedDone_LastOne_ParentFinalized() { var listId = Guid.NewGuid().ToString(); await _lists.AddAsync(new ListEntity { Id = listId, Name = "L", CreatedAt = DateTime.UtcNow }); var parent = new TaskEntity { Id = Guid.NewGuid().ToString(), ListId = listId, Title = "p", Status = TaskStatus.Planned, CreatedAt = DateTime.UtcNow, CommitType = "chore", }; await _tasks.AddAsync(parent); var c1 = new TaskEntity { Id = Guid.NewGuid().ToString(), ListId = listId, Title = "c1", Status = TaskStatus.Done, CreatedAt = DateTime.UtcNow, CommitType = "chore", ParentTaskId = parent.Id, }; var c2 = new TaskEntity { Id = Guid.NewGuid().ToString(), ListId = listId, Title = "c2", Status = TaskStatus.Running, CreatedAt = DateTime.UtcNow, CommitType = "chore", ParentTaskId = parent.Id, }; await _tasks.AddAsync(c1); await _tasks.AddAsync(c2); // Simulate the runner finishing the second child: await _tasks.MarkDoneAsync(c2.Id, DateTime.UtcNow, "done"); if (c2.ParentTaskId is not null) await _tasks.TryCompleteParentAsync(c2.ParentTaskId); var parentLoaded = await _tasks.GetByIdAsync(parent.Id); Assert.Equal(TaskStatus.Done, parentLoaded!.Status); Assert.NotNull(parentLoaded.FinishedAt); } }