3456756cd1
Review fix round 1 for lane F: - The ScanState probe watcher is seeded at InitializeAsync from a platform-only DiscoverHierarchy query (IGalaxyPlatformSource / GatewayGalaxyPlatformSource over the owned repository client) and re-seeded on every deploy-watcher rediscovery. The runtime never calls DiscoverAsync on a live driver, so per-platform host status was unreachable in production. A failed fetch logs a Warning and never fails init; re-seeds coalesce so a deploy during an in-flight pass still gets a pass that starts after it. - EnsureEventPumpStarted refuses while the runtime is stopping, BEFORE reusing the live pump (that pump belongs to the dying subscriber), and the production rebuild disposes any stale pump before wiring the new subscriber. - TeardownRuntimeAsync closes the MX session under the caller's token linked with a 5 s SessionCloseDeadline, so a blackholed gateway cannot hold DriverInstanceActor.PostStop for 30 s. - A runtime generation counter, bumped by every teardown, is re-checked before each Rebind in ResubscribeAllAsync, so a replay-gap re-snapshot that straddles a teardown cannot resurrect handles into the cleared registry. Refs #735 Claude-Session: https://claude.ai/code/session_01Ub9hGmYLQvn9XETzw7CMsT
61 lines
2.5 KiB
C#
61 lines
2.5 KiB
C#
using Shouldly;
|
|
using Xunit;
|
|
using ZB.MOM.WW.MxGateway.Contracts.Proto.Galaxy;
|
|
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Browse;
|
|
|
|
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests.Browse;
|
|
|
|
/// <summary>The platform-only query the driver seeds its ScanState probe watcher from.</summary>
|
|
public sealed class GatewayGalaxyPlatformSourceTests
|
|
{
|
|
private static GalaxyObject Obj(string tag, params string[] chain)
|
|
{
|
|
var o = new GalaxyObject { TagName = tag };
|
|
o.TemplateChain.Add(chain);
|
|
return o;
|
|
}
|
|
|
|
/// <summary>Every request carries the platform template filter, no attributes, and the page size; replies are re-filtered.</summary>
|
|
[Fact]
|
|
public async Task Requests_filter_on_platform_templates_without_attributes_and_replies_are_refiltered()
|
|
{
|
|
var requests = new List<DiscoverHierarchyRequest>();
|
|
Task<DiscoverHierarchyReply> Discover(DiscoverHierarchyRequest req, CancellationToken ct)
|
|
{
|
|
requests.Add(req);
|
|
var reply = new DiscoverHierarchyReply();
|
|
if (req.PageToken == "")
|
|
{
|
|
reply.Objects.Add([Obj("Plat01", "$WinPlatform"), Obj("Tank01", "$UserDefined")]);
|
|
reply.NextPageToken = "p2";
|
|
}
|
|
else
|
|
{
|
|
reply.Objects.Add([Obj("Eng01", "$AppEngine", "$WinPlatform"), Obj("Plat01", "$WinPlatform"), Obj("", "$WinPlatform")]);
|
|
}
|
|
return Task.FromResult(reply);
|
|
}
|
|
|
|
var names = await GatewayGalaxyPlatformSource.QueryAsync(Discover, pageSize: 50, CancellationToken.None);
|
|
|
|
names.ShouldBe(["Plat01", "Eng01"]);
|
|
requests.Count.ShouldBe(2);
|
|
requests.ShouldAllBe(r => r.PageSize == 50 && r.IncludeAttributes == false);
|
|
requests.ShouldAllBe(r => r.TemplateChainContains.Contains("$WinPlatform") && r.TemplateChainContains.Contains("$AppEngine"));
|
|
}
|
|
|
|
/// <summary>A gateway that strips the template chain is trusted to have filtered server-side.</summary>
|
|
[Fact]
|
|
public async Task Objects_without_a_template_chain_are_kept()
|
|
{
|
|
Task<DiscoverHierarchyReply> Discover(DiscoverHierarchyRequest req, CancellationToken ct)
|
|
{
|
|
var reply = new DiscoverHierarchyReply();
|
|
reply.Objects.Add(new GalaxyObject { TagName = "Plat01" });
|
|
return Task.FromResult(reply);
|
|
}
|
|
|
|
(await GatewayGalaxyPlatformSource.QueryAsync(Discover, pageSize: 0, CancellationToken.None)).ShouldBe(["Plat01"]);
|
|
}
|
|
}
|