feat(central-ui): Site Calls page accepts ?correlationId= and opens the operation's detail

Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29
This commit is contained in:
Joseph Doherty
2026-09-06 11:04:39 -04:00
parent 8abc0eca13
commit 33f9c97030
4 changed files with 209 additions and 3 deletions
@@ -151,6 +151,51 @@ public sealed class QueryStringDrillInTests
Assert.Empty(cut.FindAll("[data-test='site-calls-filter-operation']"));
}
// -----------------------------------------------------------------
// SiteCallsReport — ?correlationId= (Flow Debugger / Audit Log deep link, D2)
// -----------------------------------------------------------------
[Fact]
public void SiteCallsReport_CorrelationIdQuery_SeedsTheOperationFilter()
{
// A well-formed GUID rides on the first query as the exact-match
// TrackedOperationIdFilter and renders the read-only Operation pill.
var op = Guid.Parse("44444444-4444-4444-4444-444444444444");
using var ctx = new SiteCallsReportFixture();
var nav = (BunitNavigationManager)ctx.Services.GetRequiredService<NavigationManager>();
nav.NavigateTo($"/site-calls/report?correlationId={op}");
var cut = ctx.Render<SiteCallsReportPage>();
cut.WaitForAssertion(() =>
{
Assert.Single(ctx.QueryRequests);
Assert.Equal(op, ctx.QueryRequests[0].TrackedOperationIdFilter);
var pill = cut.Find("[data-test='site-calls-filter-operation']");
Assert.Contains(op.ToString("N")[..12], pill.TextContent);
});
}
[Fact]
public void SiteCallsReport_CorrelationIdQuery_Malformed_IsIgnored()
{
// Lax parsing, like ?status= — a value that is not a GUID leaves the
// filter null and renders no pill, so a hand-crafted bad URL loads the
// page unfiltered instead of breaking it.
using var ctx = new SiteCallsReportFixture();
var nav = (BunitNavigationManager)ctx.Services.GetRequiredService<NavigationManager>();
nav.NavigateTo("/site-calls/report?correlationId=nope");
var cut = ctx.Render<SiteCallsReportPage>();
cut.WaitForAssertion(() =>
{
Assert.Single(ctx.QueryRequests);
Assert.Null(ctx.QueryRequests[0].TrackedOperationIdFilter);
});
Assert.Empty(cut.FindAll("[data-test='site-calls-filter-operation']"));
}
// -----------------------------------------------------------------
// NotificationReport — ?correlationId= (Flow Debugger follow-ups, D2)
// -----------------------------------------------------------------
@@ -65,6 +65,16 @@ public class SiteCallsReportPageTests : BunitContext
private readonly List<SiteCallQueryRequest> _queryRequests = new();
private readonly List<RetrySiteCallRequest> _retryRequests = new();
private readonly List<DiscardSiteCallRequest> _discardRequests = new();
private readonly List<SiteCallDetailRequest> _detailRequests = new();
// Scripted detail reply — the ?correlationId= auto-open path fetches the full
// row for the modal body. Overridable per test.
private SiteCallDetailResponse _detailReply = new(
"q", true, null,
new SiteCallDetail(ParkedId, "plant-a", "ApiOutbound", "ERP.GetOrder", "Parked",
RetryCount: 3, LastError: "HTTP 503 from ERP", HttpStatus: 503,
CreatedAtUtc: DateTime.UtcNow.AddMinutes(-30), UpdatedAtUtc: DateTime.UtcNow.AddMinutes(-5),
TerminalAtUtc: null, IngestedAtUtc: DateTime.UtcNow.AddMinutes(-5)));
// Scripted relay responses — overridable per test.
private RetrySiteCallResponse _retryReply =
@@ -506,6 +516,105 @@ public class SiteCallsReportPageTests : BunitContext
});
}
// ─────────────────────────────────────────────────────────────────────────
// Flow Debugger / Audit Log deep link: ?correlationId=<TrackedOperationId>
// seeds a read-only "Operation" pill filter, rides on the query as
// TrackedOperationIdFilter, and auto-opens the detail modal when exactly
// one matching row comes back (follow-ups plan D2 / Task 4).
// ─────────────────────────────────────────────────────────────────────────
[Fact]
public void SiteCallsReport_CorrelationIdQuery_SeedsTheOperationFilterAndAutoOpensDetail()
{
// The scripted query returns exactly the one row the deep link names.
_queryReply = _queryReply with
{
SiteCalls = _queryReply.SiteCalls.Where(c => c.TrackedOperationId == ParkedId).ToList(),
};
var nav = (BunitNavigationManager)Services.GetRequiredService<NavigationManager>();
nav.NavigateTo($"/site-calls/report?correlationId={ParkedId}");
var cut = Render<SiteCallsReportPage>();
cut.WaitForAssertion(() =>
{
// The first query already carries the operation filter.
Assert.Single(_queryRequests);
Assert.Equal(ParkedId, _queryRequests[0].TrackedOperationIdFilter);
// The filter card shows the read-only pill with the short id.
var pill = cut.Find("[data-test='site-calls-filter-operation']");
Assert.Contains("Operation", pill.TextContent);
Assert.Contains(ParkedId.ToString("N")[..12], pill.TextContent);
// Exactly one matching row → the detail modal opened on it.
var title = cut.Find(".modal .modal-title");
Assert.Contains("Site Call Detail", title.TextContent);
Assert.Contains(ParkedId.ToString("N")[..12], title.TextContent);
Assert.Single(_detailRequests);
Assert.Equal(ParkedId, _detailRequests[0].TrackedOperationId);
});
}
[Fact]
public void SiteCallsReport_CorrelationIdQuery_NoMatchingRow_DoesNotOpenDetail()
{
// The default scripted reply carries two rows, neither of which is the
// requested id — the filter still rides on the query, but nothing
// auto-opens.
var other = Guid.Parse("33333333-3333-3333-3333-333333333333");
var nav = (BunitNavigationManager)Services.GetRequiredService<NavigationManager>();
nav.NavigateTo($"/site-calls/report?correlationId={other}");
var cut = Render<SiteCallsReportPage>();
cut.WaitForAssertion(() =>
{
Assert.Single(_queryRequests);
Assert.Equal(other, _queryRequests[0].TrackedOperationIdFilter);
Assert.NotNull(cut.Find("[data-test='site-calls-filter-operation']"));
});
Assert.Empty(cut.FindAll(".modal"));
Assert.Empty(_detailRequests);
}
[Fact]
public void ClearFilters_DropsTheOperationFilter()
{
var nav = (BunitNavigationManager)Services.GetRequiredService<NavigationManager>();
nav.NavigateTo($"/site-calls/report?correlationId={ParkedId}");
var cut = Render<SiteCallsReportPage>();
cut.WaitForAssertion(() => Assert.NotNull(cut.Find("[data-test='site-calls-filter-operation']")));
// The pill counts as an active filter, so Clear is enabled.
var clear = cut.FindAll("button").Single(b => b.TextContent.Trim() == "Clear");
Assert.False(clear.HasAttribute("disabled"));
clear.Click();
cut.WaitForAssertion(() => Assert.Empty(cut.FindAll("[data-test='site-calls-filter-operation']")));
}
[Fact]
public void OperationPill_ClearButton_DropsTheFilterAndRequeries()
{
var nav = (BunitNavigationManager)Services.GetRequiredService<NavigationManager>();
nav.NavigateTo($"/site-calls/report?correlationId={ParkedId}");
var cut = Render<SiteCallsReportPage>();
cut.WaitForAssertion(() => Assert.Single(_queryRequests));
cut.Find("[data-test='site-calls-filter-operation-clear']").Click();
cut.WaitForAssertion(() =>
{
Assert.Equal(2, _queryRequests.Count);
Assert.Null(_queryRequests[1].TrackedOperationIdFilter);
Assert.Empty(cut.FindAll("[data-test='site-calls-filter-operation']"));
});
}
[Fact]
public void SiteScoping_ScopedDeploymentUser_HidesOutOfScopeRows()
{
@@ -673,6 +782,11 @@ public class SiteCallsReportPageTests : BunitContext
test._discardRequests.Add(r);
Sender.Tell(test._discardReply);
});
Receive<SiteCallDetailRequest>(r =>
{
test._detailRequests.Add(r);
Sender.Tell(test._detailReply);
});
}
}