code-reviews: resolve Server-059/060 + Tests-041 (fixed in 9ae6bce)

Dashboard Galaxy summary now copies volatile fields fresh and memoizes only the
O(N) breakdown; added the DI registration-order wiring test and the memoization
regression/guard tests. All modules back to 0 pending.
This commit is contained in:
Joseph Doherty
2026-06-25 13:44:48 -04:00
parent c004b91164
commit 8cb8fcdf57
3 changed files with 14 additions and 15 deletions
+5 -5
View File
@@ -7,7 +7,7 @@
| Review date | 2026-06-25 |
| Commit reviewed | `3cd7776` |
| Status | Re-reviewed |
| Open findings | 2 |
| Open findings | 0 |
## Checklist coverage
@@ -1182,7 +1182,7 @@ Additionally, `GatewayAlarmMonitor.ApplyProviderModeChangeAsync` increments the
| Severity | Medium |
| Category | Correctness & logic bugs |
| Location | `src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardSnapshotService.cs:109-128` |
| Status | Open |
| Status | Resolved |
**Description:** `ResolveGalaxySummary` memoizes the projected dashboard Galaxy summary keyed **only** on `entry.Sequence` and returns the cached summary whenever the sequence is unchanged. The comment asserts "an unchanged sequence means the entry (and therefore its summary) is unchanged," but that is false for the adopted `ZB.MOM.WW.GalaxyRepository` 0.2.0 cache: `Sequence` is bumped **only on a heavy refresh that detects a deploy change** (`GalaxyHierarchyCache.RefreshCoreAsync`, `nextSequence = previous.Sequence + 1`). Three other paths replace `_current` via `previous with { … }` **preserving the same `Sequence`** while changing fields the dashboard summary surfaces:
- The no-deploy-change tick (the common steady-state path) updates `LastQueriedAt`/`LastSuccessAt` (and clears `LastError`) every interval at the same sequence.
@@ -1193,7 +1193,7 @@ Because the memo keys on `Sequence` alone, the dashboard serves a stale summary:
**Recommendation:** Do not memoize the cheap, per-tick-volatile fields. Memoize only the O(N)-to-compute parts that genuinely change with `Sequence` — the `TopTemplates` and `ObjectCategories` lists derived from `entry.Objects` — keyed on `Sequence`, and build the `DashboardGalaxySummary` fresh each call from the current entry's `Status`, `LastQueriedAt`, `LastSuccessAt`, `LastDeployTime`, `LastError`, and the precomputed counts (all cheap copies). Add a `DashboardSnapshotServiceTests` case that swaps the cache's `Current` to a new entry with the **same** `Sequence` but a changed `Status`/`LastError`/`LastQueriedAt` and asserts the next `GetSnapshot().Galaxy` reflects the change. Correct the misleading memo comment.
**Resolution:** _(open)_
**Resolution:** Resolved in `9ae6bce` (2026-06-25). Split `DashboardGalaxySummaryProjector` into `ComputeBreakdown` (the O(N) template/category work, the only sequence-bound part) and `BuildSummary` (cheap volatile fields). `ResolveGalaxySummary` now memoizes only the breakdown by `Sequence` and rebuilds the summary from the current entry's `Status`/timestamps/`LastError`/counts on every tick, so a same-sequence outage surfaces `Unavailable`/`LastError`. Memo comment corrected; the redundant `DashboardGalaxyProjector` wrapper was removed. Regression test `DashboardSnapshotServiceTests.GetSnapshot_WhenGalaxyEntryChangesAtSameSequence_ReflectsVolatileStatusAndError` added (plus memoization-hit and invalidation guards).
### Server-060
@@ -1202,10 +1202,10 @@ Because the memo keys on `Sequence` alone, the dashboard serves a stale summary:
| Severity | Low |
| Category | Testing coverage |
| Location | `src/ZB.MOM.WW.MxGateway.Server/GatewayApplication.cs:95-99`, `src/ZB.MOM.WW.MxGateway.Tests/Gateway/GatewayApplicationTests.cs` |
| Status | Open |
| Status | Resolved |
**Description:** The per-key browse-subtree scoping depends entirely on the gateway's `AddSingleton<IGalaxyBrowseScopeProvider, GatewayBrowseScopeProvider>()` being registered **before** `AddZbGalaxyRepository(...)`, so that the library's `TryAddSingleton<IGalaxyBrowseScopeProvider, NullGalaxyBrowseScopeProvider>` (no scoping → full hierarchy) loses. This is the highest-blast-radius wiring in the adoption delta: if it ever regressed (the gateway line moved after `AddZbGalaxyRepository`, or the lib switched its default to a plain `AddSingleton`), **all per-API-key browse scoping would silently disable** with no error — a metadata-scoped key would see the entire Galaxy. The provider itself is well covered in isolation (`GatewayBrowseScopeProviderTests`) and end-to-end via manual construction (`GalaxyRepositoryHostWiringTests`), but **no test builds the gateway DI container and asserts `GetRequiredService<IGalaxyBrowseScopeProvider>()` resolves to `GatewayBrowseScopeProvider` rather than `NullGalaxyBrowseScopeProvider`** — the registration-order invariant is guarded only by the inline comment. (The Server-059 memo path is likewise untested; see that finding.)
**Recommendation:** Add a `GatewayApplicationTests` case that runs `GatewayApplication.CreateBuilder([])`, builds the service provider, and asserts `GetRequiredService<IGalaxyBrowseScopeProvider>()` is a `GatewayBrowseScopeProvider`. This pins the registration order and the lib's `TryAdd`-default contract so a future reorder or a lib-default change fails a fast unit test instead of silently widening data exposure.
**Resolution:** _(open)_
**Resolution:** Resolved in `9ae6bce` (2026-06-25). Added `GatewayApplicationTests.Build_RegistersGatewayBrowseScopeProviderOverLibraryDefault`, which builds the gateway via `GatewayApplication.Build([])` and asserts `GetRequiredService<IGalaxyBrowseScopeProvider>()` resolves to `GatewayBrowseScopeProvider`, pinning the registration-order invariant over the library's `TryAddSingleton` no-op default.