using Grpc.Core; using ZB.MOM.WW.ScadaBridge.Communication.Grpc; namespace ZB.MOM.WW.ScadaBridge.Communication.Tests.Grpc; public class AuditWireBudgetTests { /// Verifies that a row over the soft budget travels alone in its batch without being truncated. [Fact] public void OversizeSoftBudgetRow_TravelsAloneWithoutTruncation() { var batch = new AuditEventBatch(); var bytes = AuditWireBudget.InitialBytes; var row = new AuditEventDto { EventId = "large", ErrorDetail = new string('x', GrpcMessageLimits.AuditBatchBytes + 1) }; Assert.True(AuditWireBudget.TryAppend(batch.Events, row, ref bytes, row.EventId)); Assert.False(AuditWireBudget.TryAppend(batch.Events, new AuditEventDto { EventId = "next" }, ref bytes, "next")); Assert.Same(row, Assert.Single(batch.Events)); Assert.Equal(row.ErrorDetail, batch.Events[0].ErrorDetail); Assert.True(bytes < GrpcMessageLimits.MaxMessageBytes); } /// Verifies that a hard-oversize row does not block the rows before it and fails explicitly when it is first in a batch. [Fact] public void HardOversizeRow_DoesNotBlockGoodPrefix_AndFailsExplicitlyWhenFirst() { var good = new AuditEventDto { EventId = "good", ErrorMessage = "small" }; var huge = new AuditEventDto { EventId = "retained-id", ErrorDetail = new string('x', GrpcMessageLimits.MaxMessageBytes) }; var batch = new AuditEventBatch(); var bytes = AuditWireBudget.InitialBytes; Assert.True(AuditWireBudget.TryAppend(batch.Events, good, ref bytes, good.EventId)); Assert.False(AuditWireBudget.TryAppend(batch.Events, huge, ref bytes, huge.EventId)); Assert.Equal("good", Assert.Single(batch.Events).EventId); var next = new AuditEventBatch(); bytes = AuditWireBudget.InitialBytes; var ex = Assert.Throws(() => AuditWireBudget.TryAppend(next.Events, huge, ref bytes, huge.EventId)); Assert.Equal(StatusCode.ResourceExhausted, ex.StatusCode); Assert.Contains("retained-id", ex.Status.Detail); Assert.Contains("retained locally", ex.Status.Detail); Assert.Empty(next.Events); } }