feat(worker): surface startup recovery via OperationProgress channel
The six Lifecycle/*Recovery hosted services now broadcast one
OperationProgress("startup-recovery", <phase>, current, total) message each
after they finish, instead of leaving the UI on a bare "connecting" text
during worker startup. IslandsShellViewModel subscribes and swaps in
"Recovering... (i/n)" (existing ops.worker.startupRecovery key, no locale
changes) while Worker.IsReconnecting is true, and clears it once actually
connected so a later transient reconnect doesn't replay stale text.
OperationProgress broadcasts to Clients.All with no replay-on-connect, so a
UI that hasn't finished its SignalR handshake yet can miss some or all of
these messages and simply keep showing "connecting" as before -- accepted
rather than adding a cached-state + reconnect-replay path (mirroring
RefreshExternalMergeConflictsAsync) for what is a fast, best-effort,
local-only startup sweep with no UI-visible failure mode beyond that.
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
using ClaudeDo.Ui.ViewModels;
|
||||
using Xunit;
|
||||
|
||||
namespace ClaudeDo.Ui.Tests;
|
||||
|
||||
// Covers C2: the six Lifecycle/*Recovery startup sweeps broadcast OperationProgress under the
|
||||
// stable "startup-recovery" opKey; the shell replaces the generic "connecting" text with it
|
||||
// while the worker is reconnecting.
|
||||
public class IslandsShellViewModelStartupRecoveryTests
|
||||
{
|
||||
private sealed class ReconnectingWorkerClient : StubWorkerClient
|
||||
{
|
||||
public override bool IsReconnecting => true;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartupRecoveryProgress_ReplacesConnectingText()
|
||||
{
|
||||
var vm = new IslandsShellViewModel();
|
||||
var worker = new ReconnectingWorkerClient();
|
||||
vm.Worker = worker;
|
||||
worker.OperationProgressEvent += vm.OnOperationProgress;
|
||||
|
||||
worker.RaiseOperationProgress("startup-recovery", "attachments", 3, 6);
|
||||
|
||||
Assert.Contains("3/6", vm.ConnectionText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StartupRecoveryProgress_WithoutTotal_ShowsLabelOnly()
|
||||
{
|
||||
var vm = new IslandsShellViewModel();
|
||||
var worker = new ReconnectingWorkerClient();
|
||||
vm.Worker = worker;
|
||||
worker.OperationProgressEvent += vm.OnOperationProgress;
|
||||
|
||||
worker.RaiseOperationProgress("startup-recovery", "stale-tasks", 0, 0);
|
||||
|
||||
Assert.DoesNotContain("/", vm.ConnectionText);
|
||||
Assert.NotEqual(string.Empty, vm.ConnectionText);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ForeignOpKey_DoesNotChangeConnectionText()
|
||||
{
|
||||
var vm = new IslandsShellViewModel();
|
||||
var worker = new ReconnectingWorkerClient();
|
||||
vm.Worker = worker;
|
||||
worker.OperationProgressEvent += vm.OnOperationProgress;
|
||||
var before = vm.ConnectionText;
|
||||
|
||||
worker.RaiseOperationProgress("merge:task-123", "merging", 0, 0);
|
||||
|
||||
Assert.Equal(before, vm.ConnectionText);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user