124 lines
4.7 KiB
C#
124 lines
4.7 KiB
C#
using Google.Protobuf;
|
|
using Google.Protobuf.WellKnownTypes;
|
|
using ScadaLink.Communication.Grpc;
|
|
|
|
namespace ScadaLink.Communication.Tests.Protos;
|
|
|
|
/// <summary>
|
|
/// Wire-format round-trip tests for the Audit Log (#23) telemetry proto messages
|
|
/// (<see cref="AuditEventDto"/>, <see cref="AuditEventBatch"/>, <see cref="IngestAck"/>).
|
|
/// Locks the additive contract the site → central audit pipeline depends on.
|
|
/// </summary>
|
|
public class AuditEventProtoTests
|
|
{
|
|
[Fact]
|
|
public void AuditEventDto_RoundTrip_PreservesAllFields()
|
|
{
|
|
var occurredAt = Timestamp.FromDateTimeOffset(
|
|
new DateTimeOffset(2026, 5, 20, 10, 15, 30, 123, TimeSpan.Zero));
|
|
|
|
var original = new AuditEventDto
|
|
{
|
|
EventId = Guid.NewGuid().ToString(),
|
|
OccurredAtUtc = occurredAt,
|
|
Channel = "ApiOutbound",
|
|
Kind = "ApiCall",
|
|
CorrelationId = Guid.NewGuid().ToString(),
|
|
SourceSiteId = "site-1",
|
|
SourceInstanceId = "Pump01",
|
|
SourceScript = "OnDemand",
|
|
Actor = "design-key",
|
|
Target = "weather-api",
|
|
Status = "Delivered",
|
|
HttpStatus = 200,
|
|
DurationMs = 42,
|
|
ErrorMessage = "no error",
|
|
ErrorDetail = "stack",
|
|
RequestSummary = "GET /weather?city=brisbane",
|
|
ResponseSummary = "{ \"temp\": 22.5 }",
|
|
PayloadTruncated = true,
|
|
Extra = "{ \"retryCount\": 0 }"
|
|
};
|
|
|
|
var bytes = original.ToByteArray();
|
|
var deserialized = AuditEventDto.Parser.ParseFrom(bytes);
|
|
|
|
Assert.Equal(original.EventId, deserialized.EventId);
|
|
Assert.Equal(original.OccurredAtUtc, deserialized.OccurredAtUtc);
|
|
Assert.Equal(original.Channel, deserialized.Channel);
|
|
Assert.Equal(original.Kind, deserialized.Kind);
|
|
Assert.Equal(original.CorrelationId, deserialized.CorrelationId);
|
|
Assert.Equal(original.SourceSiteId, deserialized.SourceSiteId);
|
|
Assert.Equal(original.SourceInstanceId, deserialized.SourceInstanceId);
|
|
Assert.Equal(original.SourceScript, deserialized.SourceScript);
|
|
Assert.Equal(original.Actor, deserialized.Actor);
|
|
Assert.Equal(original.Target, deserialized.Target);
|
|
Assert.Equal(original.Status, deserialized.Status);
|
|
Assert.Equal(original.HttpStatus, deserialized.HttpStatus);
|
|
Assert.Equal(original.DurationMs, deserialized.DurationMs);
|
|
Assert.Equal(original.ErrorMessage, deserialized.ErrorMessage);
|
|
Assert.Equal(original.ErrorDetail, deserialized.ErrorDetail);
|
|
Assert.Equal(original.RequestSummary, deserialized.RequestSummary);
|
|
Assert.Equal(original.ResponseSummary, deserialized.ResponseSummary);
|
|
Assert.Equal(original.PayloadTruncated, deserialized.PayloadTruncated);
|
|
Assert.Equal(original.Extra, deserialized.Extra);
|
|
}
|
|
|
|
[Fact]
|
|
public void AuditEventDto_NullableInt_AbsentByDefault_NotIncludedInWire()
|
|
{
|
|
// Int32Value fields (http_status, duration_ms) are wrapper-typed in proto;
|
|
// when unset, the wrapper is absent, not serialized, and deserializes back to null.
|
|
var original = new AuditEventDto
|
|
{
|
|
EventId = Guid.NewGuid().ToString(),
|
|
OccurredAtUtc = Timestamp.FromDateTimeOffset(DateTimeOffset.UtcNow),
|
|
Channel = "Notification",
|
|
Kind = "NotifySend",
|
|
Status = "Submitted"
|
|
};
|
|
|
|
Assert.Null(original.HttpStatus);
|
|
Assert.Null(original.DurationMs);
|
|
|
|
var bytes = original.ToByteArray();
|
|
var deserialized = AuditEventDto.Parser.ParseFrom(bytes);
|
|
|
|
Assert.Null(deserialized.HttpStatus);
|
|
Assert.Null(deserialized.DurationMs);
|
|
}
|
|
|
|
[Fact]
|
|
public void AuditEventBatch_Empty_RoundTrip_Yields_EmptyEvents()
|
|
{
|
|
var original = new AuditEventBatch();
|
|
Assert.Empty(original.Events);
|
|
|
|
var bytes = original.ToByteArray();
|
|
var deserialized = AuditEventBatch.Parser.ParseFrom(bytes);
|
|
|
|
Assert.Empty(deserialized.Events);
|
|
}
|
|
|
|
[Fact]
|
|
public void IngestAck_PreservesAcceptedEventIds()
|
|
{
|
|
var id1 = Guid.NewGuid().ToString();
|
|
var id2 = Guid.NewGuid().ToString();
|
|
var id3 = Guid.NewGuid().ToString();
|
|
|
|
var original = new IngestAck();
|
|
original.AcceptedEventIds.Add(id1);
|
|
original.AcceptedEventIds.Add(id2);
|
|
original.AcceptedEventIds.Add(id3);
|
|
|
|
var bytes = original.ToByteArray();
|
|
var deserialized = IngestAck.Parser.ParseFrom(bytes);
|
|
|
|
Assert.Equal(3, deserialized.AcceptedEventIds.Count);
|
|
Assert.Equal(id1, deserialized.AcceptedEventIds[0]);
|
|
Assert.Equal(id2, deserialized.AcceptedEventIds[1]);
|
|
Assert.Equal(id3, deserialized.AcceptedEventIds[2]);
|
|
}
|
|
}
|