From b466246c1b1e617b9baf3794a3c3f7cfe8c7d88c Mon Sep 17 00:00:00 2001 From: mika kuns Date: Thu, 23 Apr 2026 17:52:51 +0200 Subject: [PATCH] feat(data): TaskRepository.GetChildrenAsync --- .../Repositories/TaskRepository.cs | 13 +++ .../TaskRepositoryPlanningTests.cs | 83 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 tests/ClaudeDo.Worker.Tests/Repositories/TaskRepositoryPlanningTests.cs diff --git a/src/ClaudeDo.Data/Repositories/TaskRepository.cs b/src/ClaudeDo.Data/Repositories/TaskRepository.cs index b15ef15..4fe24b4 100644 --- a/src/ClaudeDo.Data/Repositories/TaskRepository.cs +++ b/src/ClaudeDo.Data/Repositories/TaskRepository.cs @@ -206,6 +206,19 @@ public sealed class TaskRepository #endregion + #region Planning + + public async Task> GetChildrenAsync(string parentId, CancellationToken ct = default) + { + return await _context.Tasks + .AsNoTracking() + .Where(t => t.ParentTaskId == parentId) + .OrderBy(t => t.SortOrder).ThenBy(t => t.CreatedAt) + .ToListAsync(ct); + } + + #endregion + #region Queue selection public async Task GetNextQueuedAgentTaskAsync(DateTime now, CancellationToken ct = default) diff --git a/tests/ClaudeDo.Worker.Tests/Repositories/TaskRepositoryPlanningTests.cs b/tests/ClaudeDo.Worker.Tests/Repositories/TaskRepositoryPlanningTests.cs new file mode 100644 index 0000000..7773957 --- /dev/null +++ b/tests/ClaudeDo.Worker.Tests/Repositories/TaskRepositoryPlanningTests.cs @@ -0,0 +1,83 @@ +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.Repositories; + +public sealed class TaskRepositoryPlanningTests : IDisposable +{ + private readonly DbFixture _db = new(); + private readonly ClaudeDoDbContext _ctx; + private readonly TaskRepository _tasks; + private readonly ListRepository _lists; + private readonly TagRepository _tags; + + public TaskRepositoryPlanningTests() + { + _ctx = _db.CreateContext(); + _tasks = new TaskRepository(_ctx); + _lists = new ListRepository(_ctx); + _tags = new TagRepository(_ctx); + } + + public void Dispose() + { + _ctx.Dispose(); + _db.Dispose(); + } + + private async Task CreateListAsync(string? id = null) + { + var listId = id ?? Guid.NewGuid().ToString(); + await _lists.AddAsync(new ListEntity + { + Id = listId, + Name = "Test List", + CreatedAt = DateTime.UtcNow, + }); + return listId; + } + + private TaskEntity MakeTask(string listId, TaskStatus status = TaskStatus.Manual, string? parentId = null) => new() + { + Id = Guid.NewGuid().ToString(), + ListId = listId, + Title = "t", + Status = status, + CreatedAt = DateTime.UtcNow, + CommitType = "feat", + ParentTaskId = parentId, + }; + + [Fact] + public async Task GetChildrenAsync_ReturnsOnlyDirectChildren_Sorted() + { + var listId = await CreateListAsync(); + var parent = MakeTask(listId, TaskStatus.Planning); + parent.Title = "parent"; + await _tasks.AddAsync(parent); + + var childA = MakeTask(listId, TaskStatus.Draft, parentId: parent.Id); + childA.Title = "a"; + await _tasks.AddAsync(childA); + childA.SortOrder = 1; + await _tasks.UpdateAsync(childA); + + var childB = MakeTask(listId, TaskStatus.Draft, parentId: parent.Id); + childB.Title = "b"; + await _tasks.AddAsync(childB); + childB.SortOrder = 0; + await _tasks.UpdateAsync(childB); + + var unrelated = MakeTask(listId, TaskStatus.Manual); + await _tasks.AddAsync(unrelated); + + var children = await _tasks.GetChildrenAsync(parent.Id); + + Assert.Equal(2, children.Count); + Assert.Equal("b", children[0].Title); + Assert.Equal("a", children[1].Title); + } +}