feat(ui): add Node column + filter to AuditLog grid
This commit is contained in:
@@ -282,6 +282,70 @@ public class AuditLogQueryServiceTests
|
||||
Assert.NotSame(resolvedRepos[0], resolvedRepos[1]);
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
// Task 15: SourceNode filter forwarding + distinct-nodes service contract.
|
||||
// ─────────────────────────────────────────────────────────────────────────
|
||||
|
||||
[Fact]
|
||||
public async Task QueryAsync_ForwardsSourceNodesFilter_ToRepository()
|
||||
{
|
||||
// The Audit Log page's new Node multi-select pushes its chip set into
|
||||
// AuditLogQueryFilter.SourceNodes; the service must thread it through
|
||||
// unchanged so the repository's IN-list applies.
|
||||
var repo = Substitute.For<IAuditLogRepository>();
|
||||
var filter = new AuditLogQueryFilter(
|
||||
SourceNodes: new[] { "central-a", "site-plant-a-node-a" });
|
||||
repo.QueryAsync(filter, Arg.Any<AuditLogPaging>(), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult<IReadOnlyList<AuditEvent>>(Array.Empty<AuditEvent>()));
|
||||
|
||||
var sut = new AuditLogQueryService(repo, EmptyAggregator());
|
||||
await sut.QueryAsync(filter);
|
||||
|
||||
await repo.Received(1).QueryAsync(
|
||||
Arg.Is<AuditLogQueryFilter>(f =>
|
||||
f.SourceNodes != null
|
||||
&& f.SourceNodes.Count == 2
|
||||
&& f.SourceNodes.Contains("central-a")
|
||||
&& f.SourceNodes.Contains("site-plant-a-node-a")),
|
||||
Arg.Any<AuditLogPaging>(),
|
||||
Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetDistinctSourceNodesAsync_ForwardsToRepository_OnFirstCall()
|
||||
{
|
||||
var repo = Substitute.For<IAuditLogRepository>();
|
||||
var expected = new[] { "central-a", "central-b", "site-plant-a-node-a" };
|
||||
repo.GetDistinctSourceNodesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult<IReadOnlyList<string>>(expected));
|
||||
|
||||
var sut = new AuditLogQueryService(repo, EmptyAggregator());
|
||||
|
||||
var result = await sut.GetDistinctSourceNodesAsync();
|
||||
|
||||
Assert.Equal(expected, result);
|
||||
await repo.Received(1).GetDistinctSourceNodesAsync(Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GetDistinctSourceNodesAsync_CachesSnapshot_AcrossRepeatedCalls()
|
||||
{
|
||||
// Two back-to-back calls within the 60s TTL must hit the repository
|
||||
// exactly once — the filter bar should never produce N DB hits when
|
||||
// the operator opens it twice in quick succession.
|
||||
var repo = Substitute.For<IAuditLogRepository>();
|
||||
repo.GetDistinctSourceNodesAsync(Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult<IReadOnlyList<string>>(new[] { "central-a" }));
|
||||
|
||||
var sut = new AuditLogQueryService(repo, EmptyAggregator());
|
||||
|
||||
var first = await sut.GetDistinctSourceNodesAsync();
|
||||
var second = await sut.GetDistinctSourceNodesAsync();
|
||||
|
||||
Assert.Equal(first, second);
|
||||
await repo.Received(1).GetDistinctSourceNodesAsync(Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
private static SiteHealthState StateWithBacklog(string siteId, int? pending)
|
||||
{
|
||||
SiteAuditBacklogSnapshot? backlog = pending.HasValue
|
||||
|
||||
Reference in New Issue
Block a user