Files
ScadaBridge/tests/ZB.MOM.WW.ScadaBridge.Communication.Tests/SiteCallDtoMapperTests.cs
T
Joseph Doherty 513fa1aa16 docs(tests): fix CommentChecker findings across tests/ — 13,392 → 0 (repo-wide scan clean)
Comment/XML-doc changes only:
- add summaries to ~10k test methods, fixtures, fakes and helpers
  (+ <param> on theories, <returns> on async Task methods,
  <inheritdoc/> on interface implementations the checker can resolve)
- strip ~2.6k project-tracking references (WP*, P*-*, row-NN, Task N,
  #NN, Component-0NN) from comments, keeping the explanatory text;
  reword false positives in comments only
- real docs on fakes implementing BCL/library interfaces
  (IDisposable, ILogger, IOptionsMonitor, IAsyncLifetime)
- reword the last 5 src/ false positives (CWE-1236, failures-1,
  year-0001, M365)

Only non-comment edits: four one-line nested classes split across lines
so members can carry docs. Build 0 errors / 0 warnings.
2026-10-07 15:05:49 -04:00

182 lines
6.6 KiB
C#

using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using ZB.MOM.WW.ScadaBridge.Communication.Grpc;
namespace ZB.MOM.WW.ScadaBridge.Communication.Tests;
/// <summary>
/// Field-coverage + edge tests for the <see cref="SiteCallDtoMapper"/> that
/// decodes <see cref="SiteCallOperationalDto"/> (proto) into the
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit.SiteCall"/> persistence entity.
/// Only the DTO→entity direction exists — nothing in the system maps a
/// <c>SiteCall</c> back onto the wire — so there is no round-trip test.
/// <c>IngestedAtUtc</c> is a site-side placeholder the central ingest actor
/// overwrites, so it is asserted as "recent UTC" rather than a fixed value.
/// </summary>
public class SiteCallDtoMapperTests
{
/// <summary>Verifies the case: from Dto, fully Populated, maps Every Field.</summary>
[Fact]
public void FromDto_FullyPopulated_MapsEveryField()
{
var trackedOperationId = Guid.NewGuid();
var createdAt = new DateTime(2026, 5, 20, 10, 0, 0, DateTimeKind.Utc);
var updatedAt = new DateTime(2026, 5, 20, 10, 5, 0, DateTimeKind.Utc);
var terminalAt = new DateTime(2026, 5, 20, 10, 10, 0, DateTimeKind.Utc);
var dto = new SiteCallOperationalDto
{
TrackedOperationId = trackedOperationId.ToString(),
Channel = "ApiOutbound",
Target = "ERP.GetOrder",
SourceSite = "site-melbourne",
SourceNode = "node-a",
Status = "Delivered",
RetryCount = 3,
LastError = "transient 503",
HttpStatus = 200,
CreatedAtUtc = Timestamp.FromDateTime(createdAt),
UpdatedAtUtc = Timestamp.FromDateTime(updatedAt),
TerminalAtUtc = Timestamp.FromDateTime(terminalAt),
};
var entity = SiteCallDtoMapper.FromDto(dto);
Assert.Equal(trackedOperationId, entity.TrackedOperationId.Value);
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);
Assert.Equal(200, entity.HttpStatus);
Assert.Equal(createdAt, entity.CreatedAtUtc);
Assert.Equal(updatedAt, entity.UpdatedAtUtc);
Assert.Equal(terminalAt, entity.TerminalAtUtc);
}
/// <summary>Verifies the case: from Dto, empty Last Error, becomes Null.</summary>
[Fact]
public void FromDto_EmptyLastError_BecomesNull()
{
var dto = NewMinimalDto();
dto.LastError = string.Empty;
var entity = SiteCallDtoMapper.FromDto(dto);
Assert.Null(entity.LastError);
}
/// <summary>Verifies the case: from Dto, absent Http Status, stays Null.</summary>
[Fact]
public void FromDto_AbsentHttpStatus_StaysNull()
{
// Int32Value wrapper unset on the wire — preserves true null semantics
// for non-API cached writes.
var dto = NewMinimalDto();
Assert.Null(dto.HttpStatus);
var entity = SiteCallDtoMapper.FromDto(dto);
Assert.Null(entity.HttpStatus);
}
/// <summary>Verifies the case: from Dto, absent Terminal At, stays Null.</summary>
[Fact]
public void FromDto_AbsentTerminalAt_StaysNull()
{
// Timestamp wrapper unset while the call is still active.
var dto = NewMinimalDto();
Assert.Null(dto.TerminalAtUtc);
var entity = SiteCallDtoMapper.FromDto(dto);
Assert.Null(entity.TerminalAtUtc);
}
/// <summary>Verifies the case: from Dto, timestamps, rehydrate As Utc Kind.</summary>
[Fact]
public void FromDto_Timestamps_RehydrateAsUtcKind()
{
var dto = NewMinimalDto();
var entity = SiteCallDtoMapper.FromDto(dto);
Assert.Equal(DateTimeKind.Utc, entity.CreatedAtUtc.Kind);
Assert.Equal(DateTimeKind.Utc, entity.UpdatedAtUtc.Kind);
}
/// <summary>Verifies the case: from Dto, ingested At Utc, stamped As Recent Placeholder.</summary>
[Fact]
public void FromDto_IngestedAtUtc_StampedAsRecentPlaceholder()
{
// IngestedAtUtc is a site-side DateTime.UtcNow placeholder; the central
// ingest actor overwrites it inside the dual-write transaction.
var before = DateTime.UtcNow;
var entity = SiteCallDtoMapper.FromDto(NewMinimalDto());
var after = DateTime.UtcNow;
Assert.InRange(entity.IngestedAtUtc, before, after);
Assert.Equal(DateTimeKind.Utc, entity.IngestedAtUtc.Kind);
}
/// <summary>Verifies the case: from Dto, null, throws.</summary>
[Fact]
public void FromDto_Null_Throws()
{
Assert.Throws<ArgumentNullException>(() => SiteCallDtoMapper.FromDto(null!));
}
/// <summary>Verifies the case: site Call Operational Dto, round, trip, preserves, source Node.</summary>
[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);
}
/// <summary>Verifies the case: site Call Operational Dto, round, trip, preserves, null, source Node.</summary>
[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(),
Channel = "DbOutbound",
Target = "warehouse.dbo.WriteOrder",
SourceSite = "site-brisbane",
Status = "Submitted",
RetryCount = 0,
CreatedAtUtc = Timestamp.FromDateTime(DateTime.UtcNow),
UpdatedAtUtc = Timestamp.FromDateTime(DateTime.UtcNow),
};
}