using ClaudeDo.Data.Git; using ClaudeDo.Data.Models; using ClaudeDo.Data.Repositories; using ClaudeDo.Worker.Config; using ClaudeDo.Worker.Runner; using ClaudeDo.Worker.Tests.Infrastructure; using Microsoft.Extensions.Logging.Abstractions; using TaskStatus = ClaudeDo.Data.Models.TaskStatus; using Xunit; namespace ClaudeDo.Worker.Tests.Runner; public sealed class ChildWorktreeBaseTests : IDisposable { private readonly List _repos = new(); private readonly List _dbs = new(); private readonly List<(string repoDir, string wtPath)> _cleanups = new(); [Fact] public async Task ImprovementChild_basesOff_parentWorktreeHead() { if (!GitRepoFixture.IsGitAvailable()) { Assert.True(true, "git not available -- skipping"); return; } var repo = new GitRepoFixture(); _repos.Add(repo); var db = new DbFixture(); _dbs.Add(db); var listId = Guid.NewGuid().ToString(); var parentId = Guid.NewGuid().ToString(); var childId = Guid.NewGuid().ToString(); var list = new ListEntity { Id = listId, Name = "L", WorkingDir = repo.RepoDir, CreatedAt = DateTime.UtcNow }; var parent = new TaskEntity { Id = parentId, ListId = listId, Title = "Parent", CommitType = "chore", PlanningPhase = PlanningPhase.None, CreatedAt = DateTime.UtcNow }; var child = new TaskEntity { Id = childId, ListId = listId, Title = "Improve", CommitType = "chore", ParentTaskId = parentId, CreatedBy = parentId, CreatedAt = DateTime.UtcNow }; using (var seed = db.CreateContext()) { await new ListRepository(seed).AddAsync(list); await new TaskRepository(seed).AddAsync(parent); await new TaskRepository(seed).AddAsync(child); } var cfg = new WorkerConfig { WorktreeRootStrategy = "sibling" }; var mgr = new WorktreeManager(new GitService(), db.CreateFactory(), cfg, NullLogger.Instance); var parentCtx = await mgr.CreateAsync(parent, list, CancellationToken.None); _cleanups.Add((repo.RepoDir, parentCtx.WorktreePath)); File.WriteAllText(Path.Combine(parentCtx.WorktreePath, "parent.txt"), "parent work"); await mgr.CommitIfChangedAsync(parentCtx, parent, list, CancellationToken.None); string parentHead; using (var read = db.CreateContext()) parentHead = (await new WorktreeRepository(read).GetByTaskIdAsync(parentId))!.HeadCommit!; var childCtx = await mgr.CreateAsync(child, list, CancellationToken.None); _cleanups.Add((repo.RepoDir, childCtx.WorktreePath)); Assert.Equal(parentHead, childCtx.BaseCommit); Assert.NotEqual(repo.BaseCommit, childCtx.BaseCommit); Assert.True(File.Exists(Path.Combine(childCtx.WorktreePath, "parent.txt"))); } public void Dispose() { foreach (var (repoDir, wtPath) in _cleanups) try { GitRepoFixture.RunGit(repoDir, "worktree", "remove", "--force", wtPath); } catch { } foreach (var r in _repos) r.Dispose(); foreach (var d in _dbs) d.Dispose(); } }