fix(communication): streaming consumers re-resolve site gRPC addresses on every reopen; spec matches (review 2026-10-21 03/R-2, 03/D-2)

This commit is contained in:
Joseph Doherty
2026-10-22 06:21:33 -04:00
parent 3ed71167b3
commit 90306f0ccb
8 changed files with 438 additions and 34 deletions
@@ -420,6 +420,33 @@ public class DebugStreamBridgeActorTests : TestKit
Assert.Equal("corr-1", factory.ClientFor(GrpcNodeB).SubscribeCalls[0].CorrelationId);
}
/// <summary>Verifies that a reconnect after the site's address was edited dials the new address (review 2026-10-21 03/R-2).</summary>
[Fact]
public void Reconnect_AfterSiteAddressEdit_DialsTheNewAddress()
{
const string NewNodeB = "http://localhost:5400";
var commProbe = CreateTestProbe();
var factory = new EndpointTrackingGrpcClientFactory();
SiteStreamEndpointResolver resolver =
_ => Task.FromResult<SiteStreamEndpoints?>(new SiteStreamEndpoints(GrpcNodeA, NewNodeB));
var props = Props.Create(typeof(DebugStreamBridgeActor),
SiteId, InstanceName, "corr-1", commProbe.Ref,
(Action<object>)(_ => { }), (Action)(() => { }),
factory, GrpcNodeA, GrpcNodeB, resolver);
Sys.ActorOf(props);
commProbe.ExpectMsg<SiteEnvelope>();
AwaitCondition(() => factory.ClientFor(GrpcNodeA).SubscribeCalls.Count == 1, TimeSpan.FromSeconds(3));
factory.ClientFor(GrpcNodeA).SubscribeCalls[0].OnError(new Exception("NodeA down"));
// The failover reconnect goes to the EDITED NodeB, never the start-time one.
AwaitCondition(() => factory.ClientFor(NewNodeB).SubscribeCalls.Count == 1, TimeSpan.FromSeconds(5));
Assert.Empty(factory.ClientFor(GrpcNodeB).SubscribeCalls);
// The failed stream was released on the endpoint it was opened against.
Assert.Contains("corr-1", factory.ClientFor(GrpcNodeA).UnsubscribedCorrelationIds);
}
// ── graceful (status OK) stream completion is a reconnect trigger ──
/// <summary>Verifies that a graceful stream completion reopens the stream on the same node.</summary>