using ClaudeDo.Data; using ClaudeDo.Data.Models; using ClaudeDo.Data.Repositories; using ClaudeDo.Worker.External; using ClaudeDo.Worker.Tests.Infrastructure; using TaskStatus = ClaudeDo.Data.Models.TaskStatus; namespace ClaudeDo.Worker.Tests.External; public sealed class TaskIdResolverTests : IDisposable { private readonly DbFixture _db = new(); private readonly ClaudeDoDbContext _ctx; private readonly TaskRepository _tasks; private readonly ListRepository _lists; public TaskIdResolverTests() { _ctx = _db.CreateContext(); _tasks = new TaskRepository(_ctx); _lists = new ListRepository(_ctx); } public void Dispose() { _ctx.Dispose(); _db.Dispose(); } private async Task SeedTaskAsync() { var listId = Guid.NewGuid().ToString(); await _lists.AddAsync(new ListEntity { Id = listId, Name = "L", CreatedAt = DateTime.UtcNow }); var task = new TaskEntity { Id = Guid.NewGuid().ToString(), ListId = listId, Title = "t", Status = TaskStatus.Idle, CreatedAt = DateTime.UtcNow, CommitType = "chore", }; await _tasks.AddAsync(task); return task; } [Fact] public async Task ResolveAsync_HashNumber_ResolvesToGuid() { var task = await SeedTaskAsync(); var resolved = await TaskIdResolver.ResolveAsync(_tasks, $"#{task.Number}", CancellationToken.None); Assert.Equal(task.Id, resolved); } [Fact] public async Task ResolveAsync_BareNumber_ResolvesToGuid() { var task = await SeedTaskAsync(); var resolved = await TaskIdResolver.ResolveAsync(_tasks, task.Number.ToString(), CancellationToken.None); Assert.Equal(task.Id, resolved); } [Fact] public async Task ResolveAsync_Guid_PassesThroughUnchanged() { var task = await SeedTaskAsync(); var resolved = await TaskIdResolver.ResolveAsync(_tasks, task.Id, CancellationToken.None); Assert.Equal(task.Id, resolved); } [Fact] public async Task ResolveAsync_UnknownNumber_ThrowsWithNumberInMessage() { var ex = await Assert.ThrowsAsync(() => TaskIdResolver.ResolveAsync(_tasks, "#999999", CancellationToken.None)); Assert.Contains("999999", ex.Message); } [Fact] public async Task ResolveOptionalAsync_EmptyString_PassesThroughUnchanged() { var resolved = await TaskIdResolver.ResolveOptionalAsync(_tasks, "", CancellationToken.None); Assert.Equal("", resolved); } [Fact] public async Task ResolveOptionalAsync_Null_ReturnsNull() { var resolved = await TaskIdResolver.ResolveOptionalAsync(_tasks, null, CancellationToken.None); Assert.Null(resolved); } [Fact] public async Task ResolveManyAsync_MixedArray_ResolvesBoth() { var a = await SeedTaskAsync(); var b = await SeedTaskAsync(); var resolved = await TaskIdResolver.ResolveManyAsync(_tasks, [$"#{a.Number}", b.Id], CancellationToken.None); Assert.Equal(new[] { a.Id, b.Id }, resolved); } }