using ScadaLink.Commons.Entities.Audit; using ScadaLink.Commons.Messages.Integration; using ScadaLink.Commons.Types.Enums; namespace ScadaLink.Commons.Tests.Messages.Integration; /// /// Audit Log (#23) telemetry handoff: envelope + pull request/response DTOs. /// At-least-once from sites; idempotent at central on . /// public class AuditTelemetryMessagesTests { private static AuditEvent MakeEvent(Guid? id = null) => new() { EventId = id ?? Guid.NewGuid(), OccurredAtUtc = DateTime.UtcNow, Channel = AuditChannel.ApiOutbound, Kind = AuditKind.ApiCall, Status = AuditStatus.Delivered, PayloadTruncated = false }; [Fact] public void AuditTelemetryEnvelope_ConstructsWithThreeEvents_AndIsEnumerable() { var envelopeId = Guid.NewGuid(); var events = new List { MakeEvent(), MakeEvent(), MakeEvent() }; var envelope = new AuditTelemetryEnvelope(envelopeId, "site-01", events); Assert.Equal(envelopeId, envelope.EnvelopeId); Assert.Equal("site-01", envelope.SourceSiteId); Assert.Equal(3, envelope.Events.Count); // Enumerable round-trip var collected = new List(); foreach (var e in envelope.Events) { collected.Add(e); } Assert.Equal(3, collected.Count); } [Fact] public void AuditTelemetryEnvelope_IsImmutable_RecordEqualityOnReferenceIdentityOfList() { // The record's value equality compares the IReadOnlyList reference; two envelopes // built with the same list instance + same fields must be equal, but using a // different list instance (even with equal content) must NOT be equal. var events = new List { MakeEvent() } as IReadOnlyList; var envelopeId = Guid.NewGuid(); var a = new AuditTelemetryEnvelope(envelopeId, "site-01", events); var b = new AuditTelemetryEnvelope(envelopeId, "site-01", events); Assert.Equal(a, b); Assert.Equal(a.GetHashCode(), b.GetHashCode()); var withDifferentSite = a with { SourceSiteId = "site-02" }; Assert.NotEqual(a, withDifferentSite); Assert.Equal("site-02", withDifferentSite.SourceSiteId); Assert.Equal("site-01", a.SourceSiteId); } [Fact] public void PullAuditEventsRequest_ConstructsAndIsImmutable() { var since = new DateTime(2026, 5, 20, 0, 0, 0, DateTimeKind.Utc); var request = new PullAuditEventsRequest("site-01", since, 100); Assert.Equal("site-01", request.SourceSiteId); Assert.Equal(since, request.SinceUtc); Assert.Equal(100, request.BatchSize); var bigger = request with { BatchSize = 500 }; Assert.Equal(100, request.BatchSize); Assert.Equal(500, bigger.BatchSize); } [Fact] public void PullAuditEventsResponse_ConstructsWithMoreAvailableTrue_AndIsEnumerable() { var events = new List { MakeEvent(), MakeEvent() }; var response = new PullAuditEventsResponse(events, MoreAvailable: true); Assert.True(response.MoreAvailable); Assert.Equal(2, response.Events.Count); var collected = new List(); foreach (var e in response.Events) { collected.Add(e); } Assert.Equal(2, collected.Count); } [Fact] public void PullAuditEventsResponse_WithExpression_ChangesSingleField() { var response = new PullAuditEventsResponse(new List(), MoreAvailable: false); var updated = response with { MoreAvailable = true }; Assert.False(response.MoreAvailable); Assert.True(updated.MoreAvailable); } }