chore(claude-do): merge [C4] Rebase-after-Merge sichtbar machen + WorktreeMaintenanc

ClaudeDo-Task: 0a64e32d-6308-4f2d-a69e-fb8b3cabd01a
This commit is contained in:
mika kuns
2026-08-21 13:51:01 +02:00
8 changed files with 183 additions and 9 deletions
@@ -1618,6 +1618,14 @@ public class TaskMergeServiceTests : IDisposable
Assert.Equal(GitRepoFixture.RunGit(wtPathB, "rev-parse", "HEAD").Trim(), wtB.HeadCommit);
Assert.Contains(proxy.Calls, c => c.Method == "WorktreeUpdated" && c.Args[0] is string s && s == taskB.Id);
// The rebase phase must be visible on the wire while it runs -- otherwise the UI still
// shows the stalled "Merging…" phase for the whole (best-effort) rebase loop.
Assert.Contains(proxy.Calls, c => c.Method == "OperationProgress"
&& c.Args[0] is string opKey && opKey == taskA.Id
&& c.Args[1] is string phase && phase == TaskMergeService.PhaseRebasing
&& c.Args[2] is int current && current == 1
&& c.Args[3] is int total && total == 1);
}
[Fact]
@@ -1,6 +1,7 @@
using ClaudeDo.Data.Git;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.Hub;
using ClaudeDo.Worker.Worktrees;
using ClaudeDo.Worker.Tests.Infrastructure;
using Microsoft.Extensions.Logging.Abstractions;
@@ -477,4 +478,57 @@ public class WorktreeMaintenanceServiceTests : IDisposable
var remaining = await new WorktreeRepository(checkCtx).GetAllAsync();
Assert.Empty(remaining);
}
[Fact]
public async Task ResetAll_Reports_OperationProgress_PerWorktree()
{
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
var repo = NewRepo();
var git = new GitService();
var db = NewDb();
var (list, t1) = MakeEntities(repo.RepoDir, status: ClaudeDo.Data.Models.TaskStatus.Done);
var t2 = MakeTaskForList(list.Id, ClaudeDo.Data.Models.TaskStatus.Idle);
var wt1 = await CreateWorktreeAsync(git, repo.RepoDir, t1.Id);
var wt2 = await CreateWorktreeAsync(git, repo.RepoDir, t2.Id);
using (var ctx = db.CreateContext())
{
await new ListRepository(ctx).AddAsync(list);
var taskRepo = new TaskRepository(ctx);
await taskRepo.AddAsync(t1);
await taskRepo.AddAsync(t2);
var wtRepo = new WorktreeRepository(ctx);
await wtRepo.AddAsync(new WorktreeEntity
{
TaskId = t1.Id, Path = wt1, BranchName = $"test/{t1.Id}",
BaseCommit = repo.BaseCommit, State = WorktreeState.Active, CreatedAt = DateTime.UtcNow,
});
await wtRepo.AddAsync(new WorktreeEntity
{
TaskId = t2.Id, Path = wt2, BranchName = $"test/{t2.Id}",
BaseCommit = repo.BaseCommit, State = WorktreeState.Kept, CreatedAt = DateTime.UtcNow,
});
}
var hub = new CapturingHubContext();
var svc = new WorktreeMaintenanceService(
db.CreateFactory(), git, NullLogger<WorktreeMaintenanceService>.Instance, new HubBroadcaster(hub));
var result = await svc.ResetAllAsync();
Assert.Equal(2, result.Removed);
var progressCalls = hub.Proxy.Calls.Where(c => c.Method == "OperationProgress").ToList();
Assert.Equal(2, progressCalls.Count);
Assert.All(progressCalls, c =>
{
Assert.Equal(WorktreeMaintenanceService.OpKey, c.Args[0]);
Assert.Equal(WorktreeMaintenanceService.PhaseMaintaining, c.Args[1]);
Assert.Equal(2, c.Args[3]);
});
Assert.Equal(1, progressCalls[0].Args[2]);
Assert.Equal(2, progressCalls[1].Args[2]);
}
}