50 lines
2.3 KiB
C#
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);
|
|
}
|
|
}
|