Files
lmxopcua/tests/Core/ZB.MOM.WW.OtOpcUa.Commons.Tests/MeshTransportProtoContractTests.cs
T
2026-08-06 02:53:50 -04:00

50 lines
2.3 KiB
C#

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);
}
}