perf(ui): topology page — staleness off the live loop + parallelized one-shot
This commit is contained in:
@@ -387,6 +387,93 @@ public class TopologyPageTests : BunitContext
|
||||
Assert.Contains("Changed", markup);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seeds one site with one deployed (Enabled) instance whose deployment
|
||||
/// comparison resolves: a deployed snapshot plus a current flatten. Used by
|
||||
/// the staleness-lifecycle tests below, which assert on how OFTEN the
|
||||
/// comparison runs (via the snapshot repository call count), not on the
|
||||
/// resulting Stale/Current value.
|
||||
/// </summary>
|
||||
private void SeedOneDeployedInstanceWithComparison()
|
||||
{
|
||||
SeedRepos(
|
||||
sites: new[] { new Site("Plant-A", "plant-a") { Id = 1 } },
|
||||
instances: new[]
|
||||
{
|
||||
new Instance("Pump-001") { Id = 100, SiteId = 1, State = InstanceState.Enabled }
|
||||
});
|
||||
|
||||
var deployedConfig = new FlattenedConfiguration { InstanceUniqueName = "Pump-001" };
|
||||
_deployRepo.GetDeployedSnapshotByInstanceIdAsync(100, Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult<DeployedConfigSnapshot?>(
|
||||
new DeployedConfigSnapshot("dep-1", "hash-old",
|
||||
JsonSerializer.Serialize(deployedConfig))));
|
||||
|
||||
var currentConfig = new FlattenedConfiguration { InstanceUniqueName = "Pump-001" };
|
||||
_pipeline.FlattenAndValidateAsync(100, Arg.Any<CancellationToken>())
|
||||
.Returns(Task.FromResult(Result<FlatteningPipelineResult>.Success(
|
||||
new FlatteningPipelineResult(currentConfig, "hash-new", ValidationResult.Success()))));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task LiveUpdate_DoesNotRecomputeStaleness()
|
||||
{
|
||||
// Performance regression guard: staleness (the expensive per-instance
|
||||
// re-flatten via DeploymentService.GetDeploymentComparisonAsync) must NOT
|
||||
// run on the 15s live-update poll. The live-update path refreshes only the
|
||||
// cheap deployed state (LoadDeployedStateAsync), which the timer invokes
|
||||
// directly. The comparison reaches into the snapshot repository, so its
|
||||
// call count is the proxy for "did staleness recompute".
|
||||
SeedOneDeployedInstanceWithComparison();
|
||||
|
||||
var cut = Render<TopologyPage>();
|
||||
|
||||
// Initial load computed staleness exactly once.
|
||||
await _deployRepo.Received(1)
|
||||
.GetDeployedSnapshotByInstanceIdAsync(100, Arg.Any<CancellationToken>());
|
||||
|
||||
// Drive the EXACT code path the live-update timer runs.
|
||||
await cut.InvokeAsync(() => cut.Instance.LoadDeployedStateAsync());
|
||||
|
||||
// Staleness was NOT recomputed by the live-update refresh — still once.
|
||||
await _deployRepo.Received(1)
|
||||
.GetDeployedSnapshotByInstanceIdAsync(100, Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void DeployedState_Renders_AfterSplit()
|
||||
{
|
||||
// After splitting the load into fast-state vs expensive-staleness, the
|
||||
// cheap deployed state (hierarchy + State badge) must still render.
|
||||
SeedOneDeployedInstanceWithComparison();
|
||||
|
||||
var cut = Render<TopologyPage>();
|
||||
FindToggleForLabel(cut, "Plant-A")!.Click();
|
||||
|
||||
Assert.Contains("Pump-001", cut.Markup);
|
||||
Assert.Contains("Enabled", cut.Markup);
|
||||
// The deployed instance shows a staleness badge (Stale or Current).
|
||||
Assert.Matches("Stale|Current", cut.Markup);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ManualRecheck_RecomputesStaleness()
|
||||
{
|
||||
// The "Re-check staleness" button lets operators refresh staleness on
|
||||
// demand (since it's off the live loop). Clicking it runs the comparison
|
||||
// again — a second snapshot-repository call.
|
||||
SeedOneDeployedInstanceWithComparison();
|
||||
|
||||
var cut = Render<TopologyPage>();
|
||||
_deployRepo.Received(1)
|
||||
.GetDeployedSnapshotByInstanceIdAsync(100, Arg.Any<CancellationToken>());
|
||||
|
||||
cut.Find("button[aria-label='Re-check staleness']").Click();
|
||||
|
||||
_deployRepo.Received(2)
|
||||
.GetDeployedSnapshotByInstanceIdAsync(100, Arg.Any<CancellationToken>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void LegacyInstancesRoute_IsDeclaredOnTopologyPage()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user