feat(mesh): mesh_transport.v1 proto contract
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.Protos;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.Protos.MeshTransport.V1;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Commons.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Contract-lock for the ClusterClient→gRPC command envelope's oneof. If a fifth command is added
|
||||
/// to <c>mesh_transport.proto</c> without adding a matching entry to
|
||||
/// <see cref="MeshTransportProtoContract.HandledCases"/>, this test goes red — the single guard
|
||||
/// that keeps the wire contract and the handled-case set from silently drifting apart. A drift
|
||||
/// here is exactly the failure the whole transport exists to avoid: a command that crosses the
|
||||
/// mesh boundary, decodes to a case nobody handles, and is dropped without a trace.
|
||||
/// </summary>
|
||||
public class MeshTransportProtoContractTests
|
||||
{
|
||||
[Fact]
|
||||
public void EveryOneofVariant_IsAccountedFor()
|
||||
{
|
||||
var variants = System.Enum.GetValues<CommandEnvelope.CommandOneofCase>()
|
||||
.Where(c => c != CommandEnvelope.CommandOneofCase.None).ToArray();
|
||||
|
||||
variants.ShouldBe(MeshTransportProtoContract.HandledCases, ignoreOrder: true);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_service_base_and_client_types_generate()
|
||||
{
|
||||
// Referencing these types is the whole assertion — GrpcServices="Both" must emit both halves
|
||||
// of both services: central hosts CentralCommandService and dials NodeCommandService, and a
|
||||
// driver node does the mirror image, so neither service can ship client-only or server-only.
|
||||
typeof(NodeCommandService.NodeCommandServiceBase).ShouldNotBeNull();
|
||||
typeof(NodeCommandService.NodeCommandServiceClient).ShouldNotBeNull();
|
||||
typeof(CentralCommandService.CentralCommandServiceBase).ShouldNotBeNull();
|
||||
typeof(CentralCommandService.CentralCommandServiceClient).ShouldNotBeNull();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_apply_outcome_enum_reserves_zero_as_unspecified()
|
||||
{
|
||||
// proto3 decodes an absent enum field as 0. If 0 meant "Applied", a message that lost the
|
||||
// field in transit — or was written by a peer that predates it — would report a deployment
|
||||
// as successfully applied when nothing was said at all. 0 must stay meaningless.
|
||||
((int)ApplyOutcome.Unspecified).ShouldBe(0);
|
||||
((int)ApplyOutcome.Applied).ShouldNotBe(0);
|
||||
((int)ApplyOutcome.Failed).ShouldNotBe(0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user