test(mesh): step 7 — replace the boundary test with MeshGrpcBoundaryTests and sweep the comments

MeshGrpcBoundaryTests replaces the MeshClusterClientBoundaryTests deleted in step 1. Three
self-seeded single-member clusters sharing the ActorSystem name, joined ONLY by gRPC, driving
the REAL CentralCommunicationActor -> MeshGrpcCommandDialer -> Kestrel h2c ->
MeshCommandAuthInterceptor -> NodeCommandGrpcService -> NodeCommunicationActor -> EventStream:

  1. No_cross_mesh_Akka_association_forms_between_central_and_either_site — the program's own
     point, kept as a test rather than a live-gate note: each cluster stays single-member and
     none ever holds another's address as a member.
  2. One_dispatch_through_central_reaches_a_node_in_every_cluster_mesh — the fan, with each
     site holding its OWN per-cluster PSK, so a dial that selected the wrong cluster's key
     would be refused at the far side.
  3. Falsifiability_control_a_dial_carrying_the_wrong_cluster_key_delivers_nothing — without
     it, (2) cannot distinguish "the gRPC boundary carried it" from "something else in the
     process did". The same node then accepts the right key, so the silence is the auth gate
     and not a dead endpoint.

All three run fully offline — no Docker fixture — and pass.

Comment sweep across every remaining clusterclient|receptionist hit in src/ and tests/:
the program label becomes "mesh gRPC command plane", and every comment that described the
deleted transport as live now describes what actually runs (Dps = pair-local default,
Grpc = cross-mesh). mesh_transport.proto's header/service comments are reworded; no message,
field or enum is touched — the contract is locked. MeshCommActorPathTests is reshaped rather
than retired: the two paths still exist and still matter, but what a mismatch costs is now a
misleading Unavailable from the gRPC ingress rather than a silently dropped receptionist send.

The only surviving mentions of the old names are the ones that must survive: the fail-closed
rules that name "ClusterClient" as a retired value refused at boot, their tests, and this
test class's own account of what it replaces.
This commit is contained in:
Joseph Doherty
2026-08-06 15:58:36 -04:00
parent af3b0c3146
commit 4d2dfb5b50
33 changed files with 505 additions and 86 deletions
@@ -92,8 +92,7 @@ public sealed class AdminOperationsActor : ReceiveActor
/// <b>retained</b>, not scheduled for deletion — an earlier note here promised Phase 6 would
/// remove it, which was deliberately reversed (<c>docs/Configuration.md</c> § "The compiled
/// transport-mode defaults were deliberately NOT flipped"): DPS is still the correct and
/// default transport for a single-mesh topology. What is being replaced is the ClusterClient
/// branch, by gRPC.
/// default transport for a single-mesh topology; gRPC is what crosses a mesh boundary.
/// </remarks>
/// <param name="topic">The legacy DistributedPubSub topic.</param>
/// <param name="message">The command.</param>
@@ -10,7 +10,7 @@ namespace ZB.MOM.WW.OtOpcUa.ControlPlane.Communication;
/// <summary>
/// Places one central→node command call on one node's <c>NodeCommandService</c>
/// (ClusterClient→gRPC migration Phase 1A). The seam <c>CentralCommunicationActor</c> fans
/// (mesh gRPC command plane, Phase 1A). The seam <c>CentralCommunicationActor</c> fans
/// across its target set, kept as an interface so the actor's tests never open a socket.
/// </summary>
/// <remarks>
@@ -72,7 +72,7 @@ public sealed class ConfigPublishCoordinator : ReceiveActor, IWithTimers
/// not scheduled for deletion — an earlier note here promised Phase 6 would remove it, which
/// was deliberately reversed (<c>docs/Configuration.md</c> § "The compiled transport-mode
/// defaults were deliberately NOT flipped"): DPS is still the correct and default transport
/// for a single-mesh topology. What is being replaced is the ClusterClient branch, by gRPC.
/// for a single-mesh topology; gRPC is what crosses a mesh boundary.
/// </remarks>
private readonly IActorRef? _meshRouter;
private readonly ILoggingAdapter _log = Context.GetLogger();
@@ -19,7 +19,7 @@
Google.Protobuf + Grpc.Core.Api flow transitively from Commons (the generated client). -->
<PackageReference Include="Grpc.Net.Client"/>
<PackageReference Include="ZB.MOM.WW.Audit"/>
<!-- ClusterClient→gRPC: MeshPskProvider resolves OT-MESH-PSK-{clusterId} from the shared
<!-- Mesh gRPC command plane: MeshPskProvider resolves OT-MESH-PSK-{clusterId} from the shared
secret store when MeshTransport:ClusterPsks carries no override. Abstractions only —
the concrete store is the host's choice (SecretsRegistration), not the control plane's. -->
<PackageReference Include="ZB.MOM.WW.Secrets.Abstractions"/>