feat: planning sessions foundation (Plan A) #4
@@ -217,6 +217,56 @@ public sealed class TaskRepository
|
||||
.ToListAsync(ct);
|
||||
}
|
||||
|
||||
public async Task<TaskEntity> CreateChildAsync(
|
||||
string parentId,
|
||||
string title,
|
||||
string? description,
|
||||
IReadOnlyList<string>? tagNames,
|
||||
string? commitType,
|
||||
CancellationToken ct = default)
|
||||
{
|
||||
var parent = await _context.Tasks.FirstOrDefaultAsync(t => t.Id == parentId, ct);
|
||||
if (parent is null)
|
||||
throw new InvalidOperationException($"Parent task {parentId} not found.");
|
||||
|
||||
var maxSort = await _context.Tasks
|
||||
.Where(t => t.ListId == parent.ListId)
|
||||
.Select(t => (int?)t.SortOrder)
|
||||
.MaxAsync(ct);
|
||||
|
||||
var child = new TaskEntity
|
||||
{
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
ListId = parent.ListId,
|
||||
Title = title,
|
||||
Description = description,
|
||||
Status = TaskStatus.Draft,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
CommitType = string.IsNullOrEmpty(commitType) ? parent.CommitType : commitType,
|
||||
ParentTaskId = parentId,
|
||||
SortOrder = (maxSort ?? -1) + 1,
|
||||
};
|
||||
_context.Tasks.Add(child);
|
||||
|
||||
if (tagNames is not null && tagNames.Count > 0)
|
||||
{
|
||||
foreach (var tagName in tagNames.Distinct(StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
var tag = await _context.Tags.FirstOrDefaultAsync(t => t.Name == tagName, ct);
|
||||
if (tag is null)
|
||||
{
|
||||
tag = new TagEntity { Name = tagName };
|
||||
_context.Tags.Add(tag);
|
||||
await _context.SaveChangesAsync(ct);
|
||||
}
|
||||
child.Tags.Add(tag);
|
||||
}
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync(ct);
|
||||
return child;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Queue selection
|
||||
|
||||
@@ -80,4 +80,43 @@ public sealed class TaskRepositoryPlanningTests : IDisposable
|
||||
Assert.Equal("b", children[0].Title);
|
||||
Assert.Equal("a", children[1].Title);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateChildAsync_CreatesDraftUnderParent()
|
||||
{
|
||||
var listId = await CreateListAsync();
|
||||
var parent = MakeTask(listId, TaskStatus.Planning);
|
||||
await _tasks.AddAsync(parent);
|
||||
|
||||
var child = await _tasks.CreateChildAsync(
|
||||
parent.Id,
|
||||
title: "child title",
|
||||
description: "child desc",
|
||||
tagNames: new[] { "agent" },
|
||||
commitType: "feat");
|
||||
|
||||
Assert.Equal(TaskStatus.Draft, child.Status);
|
||||
Assert.Equal(parent.Id, child.ParentTaskId);
|
||||
Assert.Equal(listId, child.ListId);
|
||||
Assert.Equal("child title", child.Title);
|
||||
Assert.Equal("child desc", child.Description);
|
||||
Assert.Equal("feat", child.CommitType);
|
||||
|
||||
var loaded = await _tasks.GetByIdAsync(child.Id);
|
||||
Assert.NotNull(loaded);
|
||||
Assert.Equal(TaskStatus.Draft, loaded!.Status);
|
||||
|
||||
var tags = await _tasks.GetTagsAsync(child.Id);
|
||||
Assert.Contains(tags, t => t.Name == "agent");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateChildAsync_ThrowsIfParentNotFound()
|
||||
{
|
||||
var listId = await CreateListAsync();
|
||||
_ = listId; // just to create the DB
|
||||
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
_tasks.CreateChildAsync("nonexistent-parent-id", "t", null, null, null));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user