chore(claude-do): merge [C4] Rebase-after-Merge sichtbar machen + WorktreeMaintenanc
ClaudeDo-Task: 0a64e32d-6308-4f2d-a69e-fb8b3cabd01a
This commit is contained in:
@@ -255,6 +255,40 @@ public class DetailsIslandReviewActionsTests : IDisposable
|
||||
Assert.Equal(0, MergeProgressSubscriberCount(worker));
|
||||
}
|
||||
|
||||
private static int OperationProgressSubscriberCount(StubWorkerClient worker)
|
||||
{
|
||||
var field = typeof(StubWorkerClient).GetField("OperationProgressEvent",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
var del = (Delegate?)field!.GetValue(worker);
|
||||
return del?.GetInvocationList().Length ?? 0;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ApproveReview_ShowsRebasePhase_WithBranchCount_InsteadOfStalledMerging()
|
||||
{
|
||||
var worker = new BlockingApproveWorkerClient();
|
||||
var vm = BuildVm(worker);
|
||||
vm.Bind(new TaskRowViewModel { Id = "task-approve-rebase", Status = TaskStatus.WaitingForReview });
|
||||
vm.Monitor.ApplyState(TaskStatus.WaitingForReview);
|
||||
|
||||
var approve = vm.ApproveReviewCommand.ExecuteAsync(null);
|
||||
var merging = vm.ApproveOp.Label;
|
||||
|
||||
// The rebase phase needs the branch total, which only rides OperationProgress
|
||||
// (MergeProgressEvent forwards elapsed-seconds only) — ignored for other tasks.
|
||||
worker.RaiseOperationProgress("some-other-task", "rebasing", 1, 3);
|
||||
Assert.Equal(merging, vm.ApproveOp.Label);
|
||||
|
||||
worker.RaiseOperationProgress("task-approve-rebase", "rebasing", 2, 3);
|
||||
Assert.NotEqual(merging, vm.ApproveOp.Label);
|
||||
Assert.Contains("(2/3)", vm.ApproveOp.Label);
|
||||
|
||||
worker.Gate.SetResult(new MergeResultDto("merged", new List<string>(), null));
|
||||
await approve;
|
||||
|
||||
Assert.Equal(0, OperationProgressSubscriberCount(worker));
|
||||
}
|
||||
|
||||
private sealed class BlockingSubmitWorkerClient : StubWorkerClient
|
||||
{
|
||||
public override bool IsConnected => true;
|
||||
|
||||
@@ -119,4 +119,26 @@ public class MergeModalViewModelTests
|
||||
|
||||
Assert.Null(vm.ProgressMessage);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Submit_shows_the_rebase_phase_with_branch_count_instead_of_stalled_merging()
|
||||
{
|
||||
var (vm, worker) = Build();
|
||||
await vm.InitializeAsync("task-1", "do the thing");
|
||||
worker.BlockMerge = true;
|
||||
|
||||
var submit = vm.SubmitCommand.ExecuteAsync(null);
|
||||
var merging = vm.ProgressMessage;
|
||||
|
||||
// Needs the branch total, which only rides OperationProgress (MergeProgressEvent
|
||||
// forwards elapsed-seconds only) -- and must not be clobbered by that other forwarder.
|
||||
worker.RaiseOperationProgress("task-1", "rebasing", 1, 2);
|
||||
Assert.NotEqual(merging, vm.ProgressMessage);
|
||||
Assert.Contains("(1/2)", vm.ProgressMessage);
|
||||
|
||||
worker.MergeGate.SetResult(new MergeResultDto("merged", new List<string>(), null));
|
||||
await submit;
|
||||
|
||||
Assert.Null(vm.ProgressMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user