fix(ui): Mission Control no longer lists finished operations as running
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
c834f17c78
commit
e9c1ff2e70
@@ -158,22 +158,26 @@ public sealed partial class MissionControlViewModel : ViewModelBase, IDisposable
|
||||
{
|
||||
var snapshot = await _worker.GetActiveOperationsAsync();
|
||||
RunningOperations.Clear();
|
||||
foreach (var op in snapshot.OrderBy(o => o.StartedAt))
|
||||
foreach (var op in snapshot.Where(o => !_endedOperationIds.Contains(o.Id)).OrderBy(o => o.StartedAt))
|
||||
RunningOperations.Add(new RunningOperationViewModel(op));
|
||||
NotifyOperationsChanged();
|
||||
}
|
||||
catch (Exception ex) { ErrorReported?.Invoke(Loc.T("missionControl.operationsRefreshFailed", ex.Message)); }
|
||||
}
|
||||
|
||||
/// <summary>Ids already reported ended; guards against a late Started or a stale snapshot re-adding them. Op ids are unique, so it is never pruned (a few bytes per op).</summary>
|
||||
private readonly HashSet<string> _endedOperationIds = new();
|
||||
|
||||
private void AddOperation(RunningOperationDto op)
|
||||
{
|
||||
if (RunningOperations.Any(r => r.Id == op.Id)) return;
|
||||
if (_endedOperationIds.Contains(op.Id) || RunningOperations.Any(r => r.Id == op.Id)) return;
|
||||
RunningOperations.Add(new RunningOperationViewModel(op));
|
||||
NotifyOperationsChanged();
|
||||
}
|
||||
|
||||
private void RemoveOperation(string id)
|
||||
{
|
||||
_endedOperationIds.Add(id);
|
||||
var row = RunningOperations.FirstOrDefault(r => r.Id == id);
|
||||
if (row is null) return;
|
||||
RunningOperations.Remove(row);
|
||||
|
||||
@@ -28,7 +28,13 @@ public class MissionControlRunningOperationsTests : IDisposable
|
||||
private sealed class FakeWorker : StubWorkerClient
|
||||
{
|
||||
public IReadOnlyList<RunningOperationDto> Snapshot { get; set; } = Array.Empty<RunningOperationDto>();
|
||||
public override Task<IReadOnlyList<RunningOperationDto>> GetActiveOperationsAsync() => Task.FromResult(Snapshot);
|
||||
/// <summary>When set, the snapshot call completes only once this source is resolved.</summary>
|
||||
public TaskCompletionSource? Gate { get; set; }
|
||||
public override async Task<IReadOnlyList<RunningOperationDto>> GetActiveOperationsAsync()
|
||||
{
|
||||
if (Gate is not null) await Gate.Task;
|
||||
return Snapshot;
|
||||
}
|
||||
}
|
||||
|
||||
private static RunningOperationDto Op(string id, string kind, int? number, string title, string? listId = null) =>
|
||||
@@ -78,6 +84,35 @@ public class MissionControlRunningOperationsTests : IDisposable
|
||||
Assert.False(vm.HasRunningOperations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EndedDuringPendingSnapshot_DoesNotReappear()
|
||||
{
|
||||
var gate = new TaskCompletionSource();
|
||||
var worker = new FakeWorker { Snapshot = new[] { Op("a", OperationKinds.Merging, 1, "Task") }, Gate = gate };
|
||||
var vm = BuildVm(worker);
|
||||
|
||||
var refresh = vm.RefreshOperationsAsync();
|
||||
worker.RaiseOperationEnded("a");
|
||||
gate.SetResult();
|
||||
await refresh;
|
||||
|
||||
Assert.Empty(vm.RunningOperations);
|
||||
Assert.False(vm.HasRunningOperations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task EndedBeforeStarted_LeavesNoRow()
|
||||
{
|
||||
var worker = new FakeWorker();
|
||||
var vm = BuildVm(worker);
|
||||
await vm.RefreshOperationsAsync();
|
||||
|
||||
worker.RaiseOperationEnded("a");
|
||||
worker.RaiseOperationStarted(Op("a", OperationKinds.Merging, 1, "Task"));
|
||||
|
||||
Assert.Empty(vm.RunningOperations);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ConnectionRestored_ReplacesListWithFreshSnapshot()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user