using ClaudeDo.Worker.Hub; using ClaudeDo.Worker.Lifecycle; using ClaudeDo.Worker.Tests.Services; namespace ClaudeDo.Worker.Tests.Lifecycle; public class RunningOperationRegistryTests { [Fact] public void Begin_AddsEntry_DisposeRemovesIt_AndIsIdempotent() { var ops = new RunningOperationRegistry(); var scope = ops.Begin(OperationKinds.Merging, "title", taskId: "t1", taskNumber: 7); var op = Assert.Single(ops.Snapshot()); Assert.Equal(OperationKinds.Merging, op.Kind); Assert.Equal("t1", op.TaskId); Assert.Equal(7, op.TaskNumber); scope.Dispose(); scope.Dispose(); Assert.Empty(ops.Snapshot()); } [Fact] public void Entry_IsRemovedWhenTheOperationThrows() { var ops = new RunningOperationRegistry(); Action act = () => { using var _ = ops.Begin(OperationKinds.Rebasing, "title", taskId: "t1"); Assert.Single(ops.Snapshot()); throw new InvalidOperationException("boom"); }; Assert.Throws(act); Assert.Empty(ops.Snapshot()); } [Fact] public void BeginAndDispose_BroadcastStartedAndEnded() { var hub = new MergeRecordingHubContext(); var ops = new RunningOperationRegistry(new HubBroadcaster(hub)); var scope = ops.Begin(OperationKinds.CreatingWorktree, "title", taskId: "t1"); var id = ops.Snapshot().Single().Id; scope.Dispose(); Assert.Collection(hub.Proxy.Calls, c => { Assert.Equal("OperationStarted", c.Method); Assert.Equal(id, ((RunningOperationDto)c.Args[0]!).Id); }, c => { Assert.Equal("OperationEnded", c.Method); Assert.Equal(id, c.Args[0]); }); } }