fix(communication): ReconcileSite threads the gRPC call's cancellation into central reconcile work (review 2026-10-21 03/P-1b)
This commit is contained in:
+43
@@ -81,4 +81,47 @@ public class CentralCommunicationActorReconcileTests : TestKit
|
||||
Assert.Equal("dep-B", gap.DeploymentId);
|
||||
Assert.False(string.IsNullOrWhiteSpace(gap.FetchToken));
|
||||
}
|
||||
|
||||
/// <summary>Verifies that cancelling the originating gRPC call stops central reconcile work (review 2026-10-21 03/P-1b).</summary>
|
||||
[Fact]
|
||||
public void CancelledCall_StopsCentralReconcileWork()
|
||||
{
|
||||
var deploymentRepo = Substitute.For<IDeploymentManagerRepository>();
|
||||
var siteRepo = Substitute.For<ISiteRepository>();
|
||||
siteRepo.GetAllSitesAsync(Arg.Any<CancellationToken>()).Returns(new List<Site>());
|
||||
|
||||
var lookupStarted = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
CancellationToken observed = default;
|
||||
siteRepo.GetSiteByIdentifierAsync("site1", Arg.Any<CancellationToken>()).Returns(async ci =>
|
||||
{
|
||||
observed = ci.Arg<CancellationToken>();
|
||||
lookupStarted.TrySetResult();
|
||||
await Task.Delay(Timeout.Infinite, observed); // a slow central DB
|
||||
return (Site?)new Site("Site One", "site1") { Id = 7 };
|
||||
});
|
||||
|
||||
var services = new ServiceCollection();
|
||||
services.AddScoped(_ => deploymentRepo);
|
||||
services.AddScoped(_ => siteRepo);
|
||||
services.AddSingleton(Options.Create(new CommunicationOptions()));
|
||||
services.AddSingleton<Microsoft.Extensions.Logging.ILogger<ReconcileService>>(
|
||||
NullLogger<ReconcileService>.Instance);
|
||||
services.AddScoped<ReconcileService>();
|
||||
var sp = services.BuildServiceProvider();
|
||||
|
||||
var actor = Sys.ActorOf(Props.Create(() => new CentralCommunicationActor(
|
||||
sp, Substitute.For<ISiteCommandTransport>())));
|
||||
|
||||
using var call = new CancellationTokenSource();
|
||||
actor.Tell(new ReconcileSiteCall(
|
||||
new ReconcileSiteRequest("site1", "node-b", new Dictionary<string, string>()), call.Token));
|
||||
AwaitCondition(() => lookupStarted.Task.IsCompleted, TimeSpan.FromSeconds(3));
|
||||
|
||||
// The site gives up (deadline / disconnect).
|
||||
call.Cancel();
|
||||
|
||||
AwaitCondition(() => observed.IsCancellationRequested, TimeSpan.FromSeconds(3));
|
||||
ExpectMsg<Status.Failure>(TimeSpan.FromSeconds(3));
|
||||
deploymentRepo.DidNotReceiveWithAnyArgs().GetExpectedDeploymentsForSiteAsync(default, default);
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -139,6 +139,29 @@ public class CentralControlGrpcServiceTests : TestKit
|
||||
Assert.Empty(ack.AcceptedEventIds);
|
||||
}
|
||||
|
||||
/// <summary>Verifies that ReconcileSite hands the actor the gRPC call's cancellation token (review 2026-10-21 03/P-1b).</summary>
|
||||
/// <returns>A task that represents the asynchronous operation.</returns>
|
||||
[Fact]
|
||||
public async Task ReconcileSite_CarriesTheCallCancellationToken_ToTheActor()
|
||||
{
|
||||
var probe = CreateTestProbe();
|
||||
var service = CreateService();
|
||||
service.SetReady(probe.Ref);
|
||||
using var callCts = new CancellationTokenSource();
|
||||
|
||||
var reply = service.ReconcileSite(
|
||||
new ReconcileSiteRequestDto { SiteIdentifier = "site1", NodeId = "node-a" }, NewContext(callCts.Token));
|
||||
|
||||
var call = probe.ExpectMsg<ZB.MOM.WW.ScadaBridge.Communication.Actors.ReconcileSiteCall>(TimeSpan.FromSeconds(3));
|
||||
Assert.Equal("site1", call.Request.SiteIdentifier);
|
||||
Assert.Equal(callCts.Token, call.CallCancelled);
|
||||
|
||||
probe.Reply(new ZB.MOM.WW.ScadaBridge.Commons.Messages.Deployment.ReconcileSiteResponse(
|
||||
new List<ZB.MOM.WW.ScadaBridge.Commons.Messages.Deployment.ReconcileGapItem>(),
|
||||
new List<string>(), "http://central"));
|
||||
await reply;
|
||||
}
|
||||
|
||||
/// <summary>Verifies that the audit ingest timeout is the single shared constant.</summary>
|
||||
[Fact]
|
||||
public void TheAuditIngestTimeout_IsTheOneSharedConstant()
|
||||
|
||||
Reference in New Issue
Block a user