code-reviews: 2026-06-25 re-review of Galaxy-adoption delta at 3cd7776
Re-review Server + Tests modules over 88915c3..3cd7776 (the ZB.MOM.WW.GalaxyRepository 0.2.0 adoption + array-write fixes). Security-critical browse-scope wiring verified sound. 3 new findings: - Server-059 (Medium): dashboard Galaxy summary memoized only on cache Sequence -> status/timestamps freeze during a Galaxy SQL outage. - Server-060 (Low): no DI test asserts IGalaxyBrowseScopeProvider resolves to GatewayBrowseScopeProvider (registration-order invariant). - Tests-041 (Medium): memoization invalidation path untested.
This commit is contained in:
@@ -4,13 +4,38 @@
|
||||
|---|---|
|
||||
| Module | `src/ZB.MOM.WW.MxGateway.Tests` |
|
||||
| Reviewer | Claude Code |
|
||||
| Review date | 2026-06-18 |
|
||||
| Commit reviewed | `88915c3` |
|
||||
| Review date | 2026-06-25 |
|
||||
| Commit reviewed | `3cd7776` |
|
||||
| Status | Re-reviewed |
|
||||
| Open findings | 0 |
|
||||
| Open findings | 1 |
|
||||
|
||||
## Checklist coverage
|
||||
|
||||
### 2026-06-25 re-review (commit `3cd7776`)
|
||||
|
||||
Re-review of the `88915c3..3cd7776` diff. Primary change is the Galaxy library adoption
|
||||
reconciliation: eight `Galaxy/*Tests.cs` files and `Grpc/GalaxyRepositoryGrpcServiceTests.cs`
|
||||
deleted (code moved to `ZB.MOM.WW.GalaxyRepository` library and its own test suite).
|
||||
Replacements: `Grpc/GalaxyRepositoryHostWiringTests.cs` (new), `Security/Authorization/GatewayBrowseScopeProviderTests.cs` (new),
|
||||
`Dashboard/DashboardGalaxySummaryProjectorTests.cs` (new), `DashboardSnapshotServiceTests.cs`
|
||||
(updated for projector-driven Galaxy summary). Secondary change: `GatewayArrayWriteWiringTests.cs`
|
||||
(+438 lines: six missing write-expansion wiring tests from Tests-040 resolution, plus
|
||||
`AddItem2`, `AddBufferedItem`, `AddItemBulk` normalization tests) and `ConstraintEnforcerTests.cs`
|
||||
(+126 lines: three new authz-path tests for array-address denial).
|
||||
|
||||
| # | Category | Result |
|
||||
|---|---|---|
|
||||
| 1 | Correctness & logic bugs | No issues found. `GalaxyRepositoryHostWiringTests.BrowseChildren_BrowseSubtreesConstraintThroughHostWiring_FiltersChildren` correctly asserts 2 children unconstrained (phase 1) and 0 children under a non-matching scope (phase 2) — non-tautological. `DashboardGalaxySummaryProjectorTests` and `ConstraintEnforcerTests` new additions all assert concrete post-conditions; no swallowed exceptions or trivially-green assertions found. |
|
||||
| 2 | mxaccessgw conventions | No issues found. New files use file-scoped namespaces, `sealed` classes, target-typed `new()`, and PascalCase `Method_Condition_Result` names. `DashboardSnapshotServiceTests` uses non-sealed `FakeApiKeyAdminStore` / `CountingApiKeyAdminStore` as intentional base classes for the inheritance chain (`SequencedApiKeyAdminStore`); acceptable in context. |
|
||||
| 3 | Concurrency & thread safety | No issues found. `GatewayRepositoryHostWiringTests` and `GatewayBrowseScopeProviderTests` are synchronous or use immediate stubs; no new `Task.Delay` or wall-clock timing. `DashboardSnapshotServiceTests` new watch-snapshot tests use `CancellationTokenSource(TimeSpan.FromSeconds(2))` bounds, consistent with the existing suite. |
|
||||
| 4 | Error handling & resilience | No issues found. |
|
||||
| 5 | Security | No issues found. `GalaxyRepositoryHostWiringTests` proves the security-critical authz filter chain end-to-end: `GatewayRequestIdentityAccessor.Push` → `GatewayBrowseScopeProvider.ResolveBrowseSubtrees` → lib `GalaxyRepositoryGrpcService` → filtered `BrowseChildren`. `GatewayBrowseScopeProviderTests` covers happy path (non-empty `BrowseSubtrees`) and no-identity fallback (empty list = no scoping, as documented — safe behind the global auth interceptor). The deleted `GalaxyRepositoryGrpcServiceTests.BrowseChildren_BrowseSubtreesConstraint_FiltersChildren` (which passed `GatewayRequestIdentityAccessor` directly rather than through the `GatewayBrowseScopeProvider` wrapper) is now superseded by a stricter test that threads through the actual production seam. |
|
||||
| 6 | Performance & resource management | No issues found. |
|
||||
| 7 | Design-document adherence | No issues found. Galaxy test deletions match the `A2-galaxyrepository-adoption-handoff.md` adoption description. All eight deleted `Galaxy/*Tests.cs` files covered lib-owned types now in `scadaproj/ZB.MOM.WW.GalaxyRepository`; their test equivalents are confirmed present in the library's test suite (`GalaxyHierarchyCacheTests`, `GalaxyHierarchyRefreshServiceTests`, `GalaxyBrowseProjectorTests`, `GalaxyDeployNotifierTests`, `GalaxyHierarchyProjectorTests`, `GalaxyAlarmAttributeMappingTests`, `GalaxyHierarchySnapshotStoreTests`). `GalaxyProtoMapperTests` and `GalaxyHierarchyIndexTests` have no direct lib-side equivalent — the types are lib-owned; the index is exercised indirectly through `GalaxyBrowseProjectorTests` and `GalaxyHierarchyProjectorTests`; this is a lib-side gap, not a host-module finding. |
|
||||
| 8 | Code organization & conventions | No issues found. |
|
||||
| 9 | Testing coverage | Issue found: Tests-041 (`DashboardSnapshotServiceTests` does not assert the memoization-by-sequence invalidation path in `DashboardSnapshotService.ResolveGalaxySummary` — a new sequence bumps the cached summary; all tests use a fixed-sequence `StubGalaxyHierarchyCache` so a bug that never invalidates the memoized summary (always returning the first computed result) would pass). |
|
||||
| 10 | Documentation & comments | No issues found. New test files carry accurate class-level `<summary>` docs; `GalaxyFilterInputSafetyTests.cs` class summary correctly updated to reference `ZB.MOM.WW.GalaxyRepository` lib types and notes that the `RegexCacheCapacity` counter is now lib-internal. |
|
||||
|
||||
### 2026-06-15 re-review (commit `410acc9`)
|
||||
|
||||
Re-review of the `42b0037..410acc9` diff (≈57 files), scoped to the alarm-provider
|
||||
@@ -756,3 +781,53 @@ The cancellation tests for `WorkerClient` in `WorkerClientTests` *do* exercise t
|
||||
**Recommendation:** Add one wiring test per uncovered variant (or a single `[Theory]` over the six command kinds), constructing the matching command type with a `SparseArrayValue` and asserting `worker.LastCommand!.Command.<Variant>.Value.KindCase == MxValue.KindOneofCase.ArrayValue` after `session.InvokeAsync`. The `SparseArrayExpanderTests` already pin the expander logic exhaustively; the wiring tests need only check that the choke point invokes expansion for each variant, not the expansion semantics themselves. The four secured variants (`WriteSecured`, `Write2`, `WriteSecured2`, `WriteSecured2Bulk`) can reuse the same `CapturingWorkerClient` stub.
|
||||
|
||||
**Resolution:** 2026-06-18 — root cause confirmed: the six arms (`WriteSecured`, `Write2`, `WriteSecured2`, `Write2Bulk`, `WriteSecuredBulk`, `WriteSecured2Bulk`) each had a `case` in `NormalizeOutboundCommand` calling `ExpandValue` but no wiring test. Server-057's additions (`AddItemBulk`, `AddBufferedItem`) covered address-normalization tests only, not the missing write-expansion variants. Added six tests to `GatewayArrayWriteWiringTests.cs` — one per uncovered arm — each constructing the matching command with a 4-element `SparseArrayValue` (Integer, single element set), driving it through `GatewaySession.InvokeAsync`, and asserting `worker.LastCommand.Command.<Variant>.Value.KindCase == ArrayValue` and the expected element positions. Tests: `WriteSecured_SparseArrayValue_ExpandedBeforeReachingWorker`, `Write2_SparseArrayValue_ExpandedBeforeReachingWorker`, `WriteSecured2_SparseArrayValue_ExpandedBeforeReachingWorker`, `Write2Bulk_SparseArrayEntryValue_ExpandedBeforeReachingWorker`, `WriteSecuredBulk_SparseArrayEntryValue_ExpandedBeforeReachingWorker`, `WriteSecured2Bulk_SparseArrayEntryValue_ExpandedBeforeReachingWorker`. All 13 `GatewayArrayWriteWiring` tests pass (7 pre-existing + 6 new).
|
||||
|
||||
### Tests-041
|
||||
|
||||
| Field | Value |
|
||||
|---|---|
|
||||
| Severity | Medium |
|
||||
| Category | Testing coverage |
|
||||
| Location | `src/ZB.MOM.WW.MxGateway.Tests/Gateway/Dashboard/DashboardSnapshotServiceTests.cs`, `src/ZB.MOM.WW.MxGateway.Server/Dashboard/DashboardSnapshotService.cs:109-128` |
|
||||
| Status | Open |
|
||||
|
||||
**Description:** `DashboardSnapshotService.ResolveGalaxySummary` introduced a lock-free
|
||||
memoization cache keyed on `GalaxyHierarchyCacheEntry.Sequence` (lines 109-128):
|
||||
on a matching sequence the cached `DashboardGalaxySummary` is returned without
|
||||
recomputing; on a new sequence it recomputes and stores the replacement. This
|
||||
restores the pre-adoption O(1)-per-tick behavior (the library bumps `Sequence`
|
||||
only on a heavy refresh).
|
||||
|
||||
`DashboardSnapshotServiceTests.GetSnapshot_ProjectsGalaxySummaryFromHierarchyCache`
|
||||
verifies that a first `GetSnapshot()` returns the correct derived summary, but the
|
||||
`StubGalaxyHierarchyCache` is immutable — it always returns the same entry with the
|
||||
same `Sequence`. No test:
|
||||
|
||||
1. Calls `GetSnapshot()` twice with an unchanged entry and verifies the second call
|
||||
returns the same summary (memoization hit); a test could observe this via a
|
||||
counting stub that increments on each `Project` call.
|
||||
2. Swaps the cache stub to a new entry with an incremented `Sequence`, then calls
|
||||
`GetSnapshot()` and asserts the snapshot reflects the **new** entry's templates
|
||||
and categories (invalidation). A bug that inverts the sequence check — e.g.
|
||||
`cached is not null && cached.Sequence != sequence` — would always return the
|
||||
first-computed summary, causing the dashboard Galaxy section to freeze after the
|
||||
initial load regardless of subsequent cache refreshes. This regression passes all
|
||||
existing tests because no test ever presents two entries with different sequences.
|
||||
|
||||
**Recommendation:** Add two tests:
|
||||
|
||||
1. `GetSnapshot_WhenGalaxyCacheSequenceUnchanged_ReusesProjectedSummary`: create a
|
||||
`CountingProjectorCache` or a `SequenceChangingGalaxyHierarchyCache` that allows
|
||||
controlled sequence changes. Call `GetSnapshot()` twice with the same entry
|
||||
(same `Sequence`); assert the second snapshot's `Galaxy` object is reference-equal
|
||||
to (or at minimum observably the same as) the first, and that the projector was
|
||||
invoked only once (e.g. via a counting stub wrapping `DashboardGalaxyProjector`).
|
||||
|
||||
2. `GetSnapshot_WhenGalaxyCacheSequenceChanges_RecomputesGalaxySummary`: present
|
||||
entry A (Sequence = 1, one template), call `GetSnapshot()`, then swap the cache
|
||||
to entry B (Sequence = 2, different templates), call `GetSnapshot()` again, and
|
||||
assert the second snapshot reflects entry B's template set, not entry A's. A
|
||||
`MutableGalaxyHierarchyCache` with a settable `Current` property suffices; the
|
||||
existing `StubGalaxyHierarchyCache` pattern is a single-line extension.
|
||||
|
||||
**Resolution:**
|
||||
|
||||
Reference in New Issue
Block a user