feat(comm): add source_node field to AuditEventDto + SiteCallOperationalDto proto

- AuditEventDto field 22, SiteCallOperationalDto field 12. Both follow the
  existing empty-string-means-null convention.
- Mappers carry SourceNode end-to-end; round-trip tests cover both populated
  and null cases.
This commit is contained in:
Joseph Doherty
2026-05-23 16:10:03 -04:00
parent 990eb02fe0
commit dfaa416ebe
9 changed files with 221 additions and 40 deletions

View File

@@ -1,3 +1,4 @@
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ScadaLink.Communication.Grpc;
@@ -28,6 +29,7 @@ public class SiteCallDtoMapperTests
Channel = "ApiOutbound",
Target = "ERP.GetOrder",
SourceSite = "site-melbourne",
SourceNode = "node-a",
Status = "Delivered",
RetryCount = 3,
LastError = "transient 503",
@@ -43,6 +45,7 @@ public class SiteCallDtoMapperTests
Assert.Equal("ApiOutbound", entity.Channel);
Assert.Equal("ERP.GetOrder", entity.Target);
Assert.Equal("site-melbourne", entity.SourceSite);
Assert.Equal("node-a", entity.SourceNode);
Assert.Equal("Delivered", entity.Status);
Assert.Equal(3, entity.RetryCount);
Assert.Equal("transient 503", entity.LastError);
@@ -121,6 +124,40 @@ public class SiteCallDtoMapperTests
Assert.Throws<ArgumentNullException>(() => SiteCallDtoMapper.FromDto(null!));
}
[Fact]
public void SiteCallOperationalDto_round_trip_preserves_SourceNode()
{
// Populated SourceNode travels verbatim across the wire and through
// the DTO→entity mapper.
var dto = NewMinimalDto();
dto.SourceNode = "node-a";
var bytes = dto.ToByteArray();
var onWire = SiteCallOperationalDto.Parser.ParseFrom(bytes);
Assert.Equal("node-a", onWire.SourceNode);
var entity = SiteCallDtoMapper.FromDto(onWire);
Assert.Equal("node-a", entity.SourceNode);
}
[Fact]
public void SiteCallOperationalDto_round_trip_preserves_null_SourceNode()
{
// The DTO uses an empty-string-means-null convention on the wire;
// FromDto rehydrates that back to a true null on the entity.
var dto = NewMinimalDto();
// SourceNode left at proto default (empty string) — semantically null.
var bytes = dto.ToByteArray();
var onWire = SiteCallOperationalDto.Parser.ParseFrom(bytes);
Assert.Equal(string.Empty, onWire.SourceNode);
var entity = SiteCallDtoMapper.FromDto(onWire);
Assert.Null(entity.SourceNode);
}
private static SiteCallOperationalDto NewMinimalDto() => new()
{
TrackedOperationId = Guid.NewGuid().ToString(),