fix(audit): race-safe channel cache + UTC-kind cursor handling in gRPC pull client (review)
This commit is contained in:
@@ -163,4 +163,53 @@ public class GrpcPullAuditEventsClientTests
|
||||
Assert.Empty(result.Events);
|
||||
Assert.False(result.MoreAvailable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PullAsync_swallows_unexpected_faults_to_empty_response()
|
||||
{
|
||||
// I3(a): the catch-all path. A non-transport fault (e.g. a mapping/
|
||||
// protocol error surfacing as InvalidOperationException) must still be
|
||||
// swallowed to empty — audit reconciliation is best-effort and a throw
|
||||
// would only get re-caught by the actor's per-site guard.
|
||||
var invoker = FakeInvoker.Throwing(new InvalidOperationException("boom"));
|
||||
var sut = new GrpcPullAuditEventsClient(
|
||||
new StaticEnumerator(new SiteEntry("site-a", "http://site-a:8083")),
|
||||
invoker,
|
||||
NullLogger<GrpcPullAuditEventsClient>.Instance);
|
||||
|
||||
var result = await sut.PullAsync("site-a", BaseTime, batchSize: 256, CancellationToken.None);
|
||||
|
||||
Assert.Empty(result.Events);
|
||||
Assert.False(result.MoreAvailable);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PullAsync_with_minvalue_unspecified_cursor_does_not_throw_and_dials()
|
||||
{
|
||||
// I3(b) / guards I2: the reconciliation cursor starts at DateTime.MinValue
|
||||
// with Kind=Unspecified. EnsureUtc must treat it AS UTC (per the system-wide
|
||||
// "all timestamps are UTC" invariant) and NOT call ToUniversalTime() — on a
|
||||
// host with a positive UTC offset that underflows and Timestamp.FromDateTime
|
||||
// throws ArgumentOutOfRangeException, crashing the FIRST pull for every site.
|
||||
var minUnspecified = default(DateTime); // DateTime.MinValue, Kind=Unspecified
|
||||
Assert.Equal(DateTimeKind.Unspecified, minUnspecified.Kind);
|
||||
|
||||
var invoker = FakeInvoker.Returning(new ProtoPullResponse());
|
||||
var sut = new GrpcPullAuditEventsClient(
|
||||
new StaticEnumerator(new SiteEntry("site-a", "http://site-a:8083")),
|
||||
invoker,
|
||||
NullLogger<GrpcPullAuditEventsClient>.Instance);
|
||||
|
||||
// MUST NOT throw — must dial successfully.
|
||||
var result = await sut.PullAsync("site-a", minUnspecified, batchSize: 256, CancellationToken.None);
|
||||
|
||||
Assert.Equal(1, invoker.CallCount);
|
||||
Assert.Equal("http://site-a:8083", invoker.Endpoint);
|
||||
Assert.NotNull(invoker.Request);
|
||||
// The unspecified-MinValue cursor is carried through verbatim as UTC
|
||||
// MinValue (no local-TZ conversion).
|
||||
Assert.Equal(DateTime.MinValue, invoker.Request!.SinceUtc.ToDateTime());
|
||||
Assert.Empty(result.Events);
|
||||
Assert.False(result.MoreAvailable);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user