refactor(tags): remove tag entity and all references
Drops TagEntity, TagRepository, and tag wiring across data layer, worker, and UI. Adds RemoveTags migration to clean up schema. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,13 +10,11 @@ public sealed class ListRepositoryTests : IDisposable
|
||||
private readonly DbFixture _db = new();
|
||||
private readonly ClaudeDoDbContext _ctx;
|
||||
private readonly ListRepository _lists;
|
||||
private readonly TagRepository _tags;
|
||||
|
||||
public ListRepositoryTests()
|
||||
{
|
||||
_ctx = _db.CreateContext();
|
||||
_lists = new ListRepository(_ctx);
|
||||
_tags = new TagRepository(_ctx);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -95,20 +93,4 @@ public sealed class ListRepositoryTests : IDisposable
|
||||
Assert.True(all.Count >= 2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task TagJunction_AddAndRemove()
|
||||
{
|
||||
var listId = Guid.NewGuid().ToString();
|
||||
await _lists.AddAsync(new ListEntity { Id = listId, Name = "Tagged", CreatedAt = DateTime.UtcNow });
|
||||
var tagId = await _tags.GetOrCreateAsync("agent");
|
||||
|
||||
await _lists.AddTagAsync(listId, tagId);
|
||||
var tags = await _lists.GetTagsAsync(listId);
|
||||
Assert.Single(tags);
|
||||
Assert.Equal("agent", tags[0].Name);
|
||||
|
||||
await _lists.RemoveTagAsync(listId, tagId);
|
||||
tags = await _lists.GetTagsAsync(listId);
|
||||
Assert.Empty(tags);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ public sealed class TaskRepositoryOrphanGuardTests : IDisposable
|
||||
await _tasks.AddAsync(parent);
|
||||
|
||||
var ex = await Assert.ThrowsAsync<InvalidOperationException>(
|
||||
() => _tasks.CreateChildAsync(parent.Id, "child", null, null, null));
|
||||
() => _tasks.CreateChildAsync(parent.Id, "child", null, null));
|
||||
Assert.Contains("not in a planning phase", ex.Message);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public sealed class TaskRepositoryOrphanGuardTests : IDisposable
|
||||
var listId = await CreateListAsync();
|
||||
var parent = await SeedPlanningParentAsync(listId);
|
||||
|
||||
var child = await _tasks.CreateChildAsync(parent.Id, "child", null, null, null);
|
||||
var child = await _tasks.CreateChildAsync(parent.Id, "child", null, null);
|
||||
Assert.Equal(parent.Id, child.ParentTaskId);
|
||||
Assert.Equal(TaskStatus.Idle, child.Status);
|
||||
}
|
||||
@@ -101,8 +101,8 @@ public sealed class TaskRepositoryOrphanGuardTests : IDisposable
|
||||
{
|
||||
var listId = await CreateListAsync();
|
||||
var parent = await SeedPlanningParentAsync(listId);
|
||||
await _tasks.CreateChildAsync(parent.Id, "a", null, null, null);
|
||||
await _tasks.CreateChildAsync(parent.Id, "b", null, null, null);
|
||||
await _tasks.CreateChildAsync(parent.Id, "a", null, null);
|
||||
await _tasks.CreateChildAsync(parent.Id, "b", null, null);
|
||||
|
||||
var outcome = await _tasks.DiscardPlanningAsync(parent.Id, dequeueQueuedChildren: false);
|
||||
|
||||
@@ -117,7 +117,7 @@ public sealed class TaskRepositoryOrphanGuardTests : IDisposable
|
||||
{
|
||||
var listId = await CreateListAsync();
|
||||
var parent = await SeedPlanningParentAsync(listId);
|
||||
var child = await _tasks.CreateChildAsync(parent.Id, "c", null, null, null);
|
||||
var child = await _tasks.CreateChildAsync(parent.Id, "c", null, null);
|
||||
await SetChildStatusAsync(child.Id, TaskStatus.Queued);
|
||||
|
||||
var outcome = await _tasks.DiscardPlanningAsync(parent.Id, dequeueQueuedChildren: false);
|
||||
@@ -134,7 +134,7 @@ public sealed class TaskRepositoryOrphanGuardTests : IDisposable
|
||||
{
|
||||
var listId = await CreateListAsync();
|
||||
var parent = await SeedPlanningParentAsync(listId);
|
||||
var child = await _tasks.CreateChildAsync(parent.Id, "c", null, null, null);
|
||||
var child = await _tasks.CreateChildAsync(parent.Id, "c", null, null);
|
||||
await SetChildStatusAsync(child.Id, TaskStatus.Queued);
|
||||
|
||||
var outcome = await _tasks.DiscardPlanningAsync(parent.Id, dequeueQueuedChildren: true);
|
||||
@@ -149,7 +149,7 @@ public sealed class TaskRepositoryOrphanGuardTests : IDisposable
|
||||
{
|
||||
var listId = await CreateListAsync();
|
||||
var parent = await SeedPlanningParentAsync(listId);
|
||||
var child = await _tasks.CreateChildAsync(parent.Id, "c", null, null, null);
|
||||
var child = await _tasks.CreateChildAsync(parent.Id, "c", null, null);
|
||||
await SetChildStatusAsync(child.Id, TaskStatus.Running);
|
||||
|
||||
var outcome = await _tasks.DiscardPlanningAsync(parent.Id, dequeueQueuedChildren: true);
|
||||
@@ -164,8 +164,8 @@ public sealed class TaskRepositoryOrphanGuardTests : IDisposable
|
||||
{
|
||||
var listId = await CreateListAsync();
|
||||
var parent = await SeedPlanningParentAsync(listId);
|
||||
var done = await _tasks.CreateChildAsync(parent.Id, "done", null, null, null);
|
||||
var failed = await _tasks.CreateChildAsync(parent.Id, "failed", null, null, null);
|
||||
var done = await _tasks.CreateChildAsync(parent.Id, "done", null, null);
|
||||
var failed = await _tasks.CreateChildAsync(parent.Id, "failed", null, null);
|
||||
await SetChildStatusAsync(done.Id, TaskStatus.Done);
|
||||
await SetChildStatusAsync(failed.Id, TaskStatus.Failed);
|
||||
|
||||
@@ -220,7 +220,7 @@ public sealed class TaskRepositoryOrphanGuardTests : IDisposable
|
||||
{
|
||||
var listId = await CreateListAsync();
|
||||
var parent = await SeedPlanningParentAsync(listId);
|
||||
var child = await _tasks.CreateChildAsync(parent.Id, "c", null, null, null);
|
||||
var child = await _tasks.CreateChildAsync(parent.Id, "c", null, null);
|
||||
|
||||
var dequeued = await _tasks.DequeueOrphanedChildrenAsync();
|
||||
Assert.Equal(0, dequeued);
|
||||
|
||||
@@ -12,14 +12,12 @@ public sealed class TaskRepositoryPlanningTests : IDisposable
|
||||
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()
|
||||
@@ -97,7 +95,6 @@ public sealed class TaskRepositoryPlanningTests : IDisposable
|
||||
parent.Id,
|
||||
title: "child title",
|
||||
description: "child desc",
|
||||
tagNames: new[] { "agent" },
|
||||
commitType: "feat");
|
||||
|
||||
Assert.Equal(TaskStatus.Idle, child.Status);
|
||||
@@ -110,9 +107,6 @@ public sealed class TaskRepositoryPlanningTests : IDisposable
|
||||
var loaded = await _tasks.GetByIdAsync(child.Id);
|
||||
Assert.NotNull(loaded);
|
||||
Assert.Equal(TaskStatus.Idle, loaded!.Status);
|
||||
|
||||
var tags = await _tasks.GetTagsAsync(child.Id);
|
||||
Assert.Contains(tags, t => t.Name == "agent");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -122,7 +116,7 @@ public sealed class TaskRepositoryPlanningTests : IDisposable
|
||||
_ = listId;
|
||||
|
||||
await Assert.ThrowsAsync<InvalidOperationException>(() =>
|
||||
_tasks.CreateChildAsync("nonexistent-parent-id", "t", null, null, null));
|
||||
_tasks.CreateChildAsync("nonexistent-parent-id", "t", null, null));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -202,8 +196,8 @@ public sealed class TaskRepositoryPlanningTests : IDisposable
|
||||
await _tasks.AddAsync(parent);
|
||||
await _tasks.SetPlanningStartedAsync(parent.Id, "tok");
|
||||
await _tasks.UpdatePlanningSessionIdAsync(parent.Id, "claude-42");
|
||||
var c1 = await _tasks.CreateChildAsync(parent.Id, "c1", null, null, null);
|
||||
var c2 = await _tasks.CreateChildAsync(parent.Id, "c2", null, null, null);
|
||||
var c1 = await _tasks.CreateChildAsync(parent.Id, "c1", null, null);
|
||||
var c2 = await _tasks.CreateChildAsync(parent.Id, "c2", null, null);
|
||||
|
||||
var outcome = await _tasks.DiscardPlanningAsync(parent.Id, dequeueQueuedChildren: false);
|
||||
|
||||
@@ -237,7 +231,7 @@ public sealed class TaskRepositoryPlanningTests : IDisposable
|
||||
var listId = await CreateListAsync();
|
||||
var parent = MakeTask(listId, phase: PlanningPhase.Active);
|
||||
await _tasks.AddAsync(parent);
|
||||
await _tasks.CreateChildAsync(parent.Id, "c", null, null, null);
|
||||
await _tasks.CreateChildAsync(parent.Id, "c", null, null);
|
||||
|
||||
await Assert.ThrowsAsync<Microsoft.Data.Sqlite.SqliteException>(async () =>
|
||||
{
|
||||
|
||||
@@ -12,14 +12,12 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
private readonly ClaudeDoDbContext _ctx;
|
||||
private readonly TaskRepository _tasks;
|
||||
private readonly ListRepository _lists;
|
||||
private readonly TagRepository _tags;
|
||||
|
||||
public TaskRepositoryTests()
|
||||
{
|
||||
_ctx = _db.CreateContext();
|
||||
_tasks = new TaskRepository(_ctx);
|
||||
_lists = new ListRepository(_ctx);
|
||||
_tags = new TagRepository(_ctx);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
@@ -239,83 +237,4 @@ public sealed class TaskRepositoryTests : IDisposable
|
||||
Assert.Equal(0, reloadB!.SortOrder);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetEffectiveTagsAsync_Returns_Union_Of_ListTags_And_TaskTags()
|
||||
{
|
||||
var listId = await CreateListAsync();
|
||||
var agentTagId = await _tags.GetOrCreateAsync("agent");
|
||||
var manualTagId = await _tags.GetOrCreateAsync("manual");
|
||||
var codeTagId = await _tags.GetOrCreateAsync("code");
|
||||
|
||||
await _lists.AddTagAsync(listId, agentTagId);
|
||||
|
||||
var task = MakeTask(listId);
|
||||
await _tasks.AddAsync(task);
|
||||
await _tasks.AddTagAsync(task.Id, manualTagId);
|
||||
await _tasks.AddTagAsync(task.Id, codeTagId);
|
||||
|
||||
var effective = await _tasks.GetEffectiveTagsAsync(task.Id);
|
||||
var names = effective.Select(t => t.Name).OrderBy(n => n).ToList();
|
||||
|
||||
Assert.Equal(3, names.Count);
|
||||
Assert.Contains("agent", names);
|
||||
Assert.Contains("code", names);
|
||||
Assert.Contains("manual", names);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SetTagsAsync_AttachesNewTagsAndCreatesMissingRows()
|
||||
{
|
||||
var listId = await CreateListAsync("L");
|
||||
var task = MakeTask(listId);
|
||||
await _tasks.AddAsync(task);
|
||||
|
||||
await _tasks.SetTagsAsync(task.Id, new[] { "agent", "novel-tag" });
|
||||
|
||||
var tags = await _tasks.GetTagsAsync(task.Id);
|
||||
Assert.Contains(tags, t => t.Name == "agent");
|
||||
Assert.Contains(tags, t => t.Name == "novel-tag");
|
||||
Assert.Equal(2, tags.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SetTagsAsync_ReplacesExistingTagSet()
|
||||
{
|
||||
var listId = await CreateListAsync("L");
|
||||
var task = MakeTask(listId);
|
||||
await _tasks.AddAsync(task);
|
||||
await _tasks.SetTagsAsync(task.Id, new[] { "agent" });
|
||||
|
||||
await _tasks.SetTagsAsync(task.Id, new[] { "manual" });
|
||||
|
||||
var tags = await _tasks.GetTagsAsync(task.Id);
|
||||
Assert.Single(tags);
|
||||
Assert.Equal("manual", tags[0].Name);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SetTagsAsync_DeduplicatesCaseInsensitively()
|
||||
{
|
||||
var listId = await CreateListAsync("L");
|
||||
var task = MakeTask(listId);
|
||||
await _tasks.AddAsync(task);
|
||||
|
||||
await _tasks.SetTagsAsync(task.Id, new[] { "agent", "AGENT", "Agent" });
|
||||
|
||||
var tags = await _tasks.GetTagsAsync(task.Id);
|
||||
Assert.Single(tags);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task SetTagsAsync_EmptyListClearsAllTags()
|
||||
{
|
||||
var listId = await CreateListAsync("L");
|
||||
var task = MakeTask(listId);
|
||||
await _tasks.AddAsync(task);
|
||||
await _tasks.SetTagsAsync(task.Id, new[] { "agent" });
|
||||
|
||||
await _tasks.SetTagsAsync(task.Id, Array.Empty<string>());
|
||||
|
||||
Assert.Empty(await _tasks.GetTagsAsync(task.Id));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user