Files
ClaudeDo/tests/ClaudeDo.Ui.Tests/IslandsShellViewModelStartupRecoveryTests.cs
T
mika kuns 6ec81f653b 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.
2026-08-21 13:36:25 +02:00

57 lines
1.8 KiB
C#

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);
}
}