feat(ui): AuditResultsGrid + AuditLogQueryService with keyset paging (#23 M7)
Adds the results grid + query facade for the central Audit Log page (#23 M7-T3): * IAuditLogQueryService / AuditLogQueryService — CentralUI facade over IAuditLogRepository.QueryAsync so the grid can be tested with a stubbed query source. Default page size is 100; callers can override per call. * AuditResultsGrid.razor + .razor.cs — Blazor Server component (Bootstrap only, no third-party UI libs). Renders the 10 columns from Component-AuditLog.md §10 (OccurredAtUtc, Site, Channel, Kind, Status, Target, Actor, DurationMs, HttpStatus, ErrorMessage). Keyset-paged via the last visible row's (OccurredAtUtc, EventId) as the cursor; Next-page button disabled when the current page is short (no count query). Row clicks emit OnRowSelected(AuditEvent) for Bundle C's drilldown drawer. Status badges are colour-coded (Delivered=green; Failed/Parked/Discarded =red; other=gray). Error messages truncated to 80 chars with full text on hover. * Column model framework: a ColumnOrder [Parameter] reorders columns by stable string keys; unknown keys are dropped. M7 scope decision (in the class doc): the framework is in place but drag-reorder / resize UX is not implemented — M7.x can add persisted-per-user reordering without rewriting the column model. * AuditLogPage wired: hosts AuditFilterBar + AuditResultsGrid, threads the filter through and stubs OnRowSelected for Bundle C. * AuditLogQueryService registered as scoped in AddCentralUI. * Tests: 4 grid bUnit tests (10 columns rendered, next-page cursor carries last row, row click raises callback, badge classes for Failed vs Delivered), 2 service tests (filter+paging pass-through, default page size of 100). AuditLogPageScaffoldTests updated to provide the new ISiteRepository + IAuditLogQueryService stubs the page now resolves.
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
using NSubstitute;
|
||||
using ScadaLink.CentralUI.Services;
|
||||
using ScadaLink.Commons.Entities.Audit;
|
||||
using ScadaLink.Commons.Interfaces.Repositories;
|
||||
using ScadaLink.Commons.Types.Audit;
|
||||
using ScadaLink.Commons.Types.Enums;
|
||||
|
||||
namespace ScadaLink.CentralUI.Tests.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Service-level tests for <see cref="AuditLogQueryService"/> (#23 M7-T3). The
|
||||
/// service is a thin pass-through over <see cref="IAuditLogRepository.QueryAsync"/>;
|
||||
/// these tests pin the filter forwarding contract and the 100-row default-page-size
|
||||
/// rule the grid relies on.
|
||||
/// </summary>
|
||||
public class AuditLogQueryServiceTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task QueryAsync_ForwardsFilterAndPaging_ToRepository()
|
||||
{
|
||||
var repo = Substitute.For<IAuditLogRepository>();
|
||||
var filter = new AuditLogQueryFilter(Channel: AuditChannel.ApiOutbound);
|
||||
var paging = new AuditLogPaging(PageSize: 25);
|
||||
var expected = new List<AuditEvent>
|
||||
{
|
||||
new() { EventId = Guid.NewGuid(), OccurredAtUtc = DateTime.UtcNow, Channel = AuditChannel.ApiOutbound, Kind = AuditKind.ApiCall, Status = AuditStatus.Delivered }
|
||||
};
|
||||
repo.QueryAsync(filter, paging, Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult<IReadOnlyList<AuditEvent>>(expected));
|
||||
|
||||
var sut = new AuditLogQueryService(repo);
|
||||
|
||||
var result = await sut.QueryAsync(filter, paging);
|
||||
|
||||
Assert.Same(expected, result);
|
||||
await repo.Received(1).QueryAsync(filter, paging, Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task QueryAsync_AppliesDefaultPageSize_WhenNotSpecified()
|
||||
{
|
||||
var repo = Substitute.For<IAuditLogRepository>();
|
||||
AuditLogPaging? observed = null;
|
||||
repo.QueryAsync(Arg.Any<AuditLogQueryFilter>(), Arg.Do<AuditLogPaging>(p => observed = p), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult<IReadOnlyList<AuditEvent>>(Array.Empty<AuditEvent>()));
|
||||
|
||||
var sut = new AuditLogQueryService(repo);
|
||||
|
||||
await sut.QueryAsync(new AuditLogQueryFilter(), paging: null);
|
||||
|
||||
Assert.NotNull(observed);
|
||||
Assert.Equal(sut.DefaultPageSize, observed!.PageSize);
|
||||
Assert.Equal(100, sut.DefaultPageSize);
|
||||
Assert.Null(observed.AfterOccurredAtUtc);
|
||||
Assert.Null(observed.AfterEventId);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user