test(communication): M2.11 review nits — bridge-actor not-found test + dead-letter comment + toast wording (#24)

- Add DebugStreamBridgeActorTests: On_InstanceNotFound_Snapshot_Forwards_To_OnEvent_Does_Not_Open_Stream_And_Terminates — asserts _onEvent receives the not-found snapshot, SubscribeCalls remains empty, and the actor terminates cleanly via Watch/ExpectTerminated.
- Add comment in DebugStreamBridgeActor near Context.Stop(Self) explaining that the subsequent StopDebugStream Tell from DebugStreamService.StopStream produces a benign expected dead-letter.
- Reword not-found toast in DebugView.razor to "Instance not found on the selected site — check the deployment target." (accurate when the instance may be deployed to a different site).
This commit is contained in:
Joseph Doherty
2026-06-16 06:15:26 -04:00
parent dbf44b9e10
commit d160c7f694
3 changed files with 43 additions and 2 deletions
@@ -451,8 +451,7 @@
{
DebugStreamService.StopStream(session.SessionId);
_toast.ShowError(
$"Instance is not deployed on that site. " +
$"Deploy it first or choose the correct site.");
"Instance not found on the selected site — check the deployment target.");
_connecting = false;
return;
}
@@ -98,6 +98,10 @@ public class DebugStreamBridgeActor : ReceiveActor, IWithTimers
_instanceUniqueName);
_stopped = true;
_onEvent(snapshot); // resolves the snapshot TCS with InstanceNotFound=true
// Note: after Context.Stop(Self) below the actor is dead. DebugStreamService
// inspects InitialSnapshot.InstanceNotFound and calls StopStream, which sends
// a StopDebugStream message. That Tell arrives after the actor has already
// stopped, producing a benign Akka dead-letter — expected and harmless.
Context.Stop(Self);
return;
}
@@ -60,6 +60,44 @@ public class DebugStreamBridgeActorTests : TestKit
return new TestContext(actor, commProbe, mockClient, events, terminated);
}
[Fact]
public void On_InstanceNotFound_Snapshot_Forwards_To_OnEvent_Does_Not_Open_Stream_And_Terminates()
{
// M2.11: when the site reports InstanceNotFound=true the bridge actor must
// (a) forward the not-found snapshot to _onEvent so DebugStreamService's TCS
// resolves and the caller can inspect the flag,
// (b) NOT open a gRPC stream (SubscribeCalls must remain empty), and
// (c) stop itself cleanly.
var ctx = CreateBridgeActor();
ctx.CommProbe.ExpectMsg<SiteEnvelope>(); // initial subscribe envelope
var notFoundSnapshot = new DebugViewSnapshot(
InstanceName,
new List<AttributeValueChanged>(),
new List<AlarmStateChanged>(),
DateTimeOffset.UtcNow,
InstanceNotFound: true);
Watch(ctx.BridgeActor);
ctx.BridgeActor.Tell(notFoundSnapshot);
// (a) _onEvent must receive the not-found snapshot
AwaitCondition(() => { lock (ctx.ReceivedEvents) { return ctx.ReceivedEvents.Count == 1; } },
TimeSpan.FromSeconds(3));
lock (ctx.ReceivedEvents)
{
var received = Assert.IsType<DebugViewSnapshot>(ctx.ReceivedEvents[0]);
Assert.True(received.InstanceNotFound);
}
// (b) no gRPC stream opened
ExpectTerminated(ctx.BridgeActor, TimeSpan.FromSeconds(3));
Assert.Empty(ctx.MockGrpcClient.SubscribeCalls);
// (c) actor terminates cleanly
// ExpectTerminated above already verified termination
}
[Fact]
public void PreStart_Sends_SubscribeDebugViewRequest_Via_ClusterClient()
{