feat(data): TaskRepository.CreateChildAsync
This commit is contained in:
@@ -217,6 +217,56 @@ public sealed class TaskRepository
|
|||||||
.ToListAsync(ct);
|
.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
|
#endregion
|
||||||
|
|
||||||
#region Queue selection
|
#region Queue selection
|
||||||
|
|||||||
@@ -80,4 +80,43 @@ public sealed class TaskRepositoryPlanningTests : IDisposable
|
|||||||
Assert.Equal("b", children[0].Title);
|
Assert.Equal("b", children[0].Title);
|
||||||
Assert.Equal("a", children[1].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