docs(code-reviews): re-review batch 1 at 39d737e — CentralUI, CLI, ClusterInfrastructure, Commons, Communication

17 new findings: CentralUI-020..025, CLI-014..016, ClusterInfrastructure-009..010, Commons-013..014, Communication-012..015.
This commit is contained in:
Joseph Doherty
2026-05-17 00:41:21 -04:00
parent 39d737ebd6
commit e49846603e
6 changed files with 842 additions and 52 deletions

View File

@@ -5,10 +5,10 @@
| Module | `src/ScadaLink.CLI` |
| Design doc | `docs/requirements/Component-CLI.md` |
| Status | Reviewed |
| Last reviewed | 2026-05-16 |
| Last reviewed | 2026-05-17 |
| Reviewer | claude-agent |
| Commit reviewed | `9c60592` |
| Open findings | 0 |
| Commit reviewed | `39d737e` |
| Open findings | 3 |
## Summary
@@ -31,8 +31,26 @@ ID-keyed, flag-based surface. Test coverage exercises `OutputFormatter`, `CliCon
`CommandHelpers.HandleResponse`, but the HTTP client, the `debug stream` path, the JSON
argument parsing, and the command-tree wiring are untested.
#### Re-review 2026-05-17 (commit `39d737e`)
All 13 prior findings are confirmed resolved — the resilience gaps, the dead format
configuration, the credential-handling weakness, and the test-coverage holes have all
been closed, and the test suite has grown substantially (`CommandTreeTests`,
`ManagementHttpClientTests`, `DebugStreamTests`, etc.). The CLI's runtime behaviour is now
solid. This re-review walked all 14 command groups against the full checklist and found
three new issues, all rooted in **update-command and design-document drift** rather than
runtime defects: every `update` command requires the entity's "core" fields (`--name`,
`--script`) even though the design doc presents them as optional, so a partial update is
impossible (CLI-014); the design doc's command surface has drifted again in two specific
places — `template composition delete` and the `data-connection` config flags (CLI-015);
and `WriteAsTable` derives table columns from only the first array element, silently
dropping columns for any later element with a different shape (CLI-016). No
Critical/High issues; the module remains healthy.
## Checklist coverage
_Original review (2026-05-16, `9c60592`):_
| # | Category | Examined | Notes |
|---|----------|----------|-------|
| 1 | Correctness & logic bugs | ☑ | Format precedence is broken (CLI-001); empty/non-JSON success bodies crash table rendering (CLI-002, CLI-003). |
@@ -46,6 +64,21 @@ argument parsing, and the command-tree wiring are untested.
| 9 | Testing coverage | ☑ | No tests for `ManagementHttpClient`, `DebugCommands`, command-tree wiring, or JSON argument parsing (CLI-013). |
| 10 | Documentation & comments | ☑ | `Component-CLI.md` mismatch (CLI-007); the in-repo `README.md` is reasonably accurate. Minor exit-code doc mismatch (CLI-009). |
_Re-review (2026-05-17, `39d737e`):_
| # | Category | Examined | Notes |
|---|----------|----------|-------|
| 1 | Correctness & logic bugs | ☑ | Update commands require "core" fields, blocking partial updates (CLI-014); `WriteAsTable` headers derived from first array element only (CLI-016). |
| 2 | Akka.NET conventions | ☑ | Not applicable — pure HTTP/SignalR client. No issues. |
| 3 | Concurrency & thread safety | ☑ | `debug stream` concurrency now resolved via `DebugStreamHelpers` (CLI-011/012). No new issues. |
| 4 | Error handling & resilience | ☑ | Malformed-URL / malformed-JSON / connect-cancellation paths all hardened (CLI-004/005/010). No new issues. |
| 5 | Security | ☑ | Env-var credential fallback in place (CLI-006). Basic Auth over HTTP is by design. No new issues. |
| 6 | Performance & resource management | ☑ | `CancellationTokenSource` now `using`-scoped (CLI-011). No new issues. |
| 7 | Design-document adherence | ☑ | Two residual command-surface drifts: `template composition delete` and `data-connection --primary-config` (CLI-015). |
| 8 | Code organization & conventions | ☑ | Consistent and clean; option construction centralised in `CliOptions`. No new issues. |
| 9 | Testing coverage | ☑ | Substantially expanded (`CommandTreeTests`, `ManagementHttpClientTests`, `DebugStreamTests`). No new gaps. |
| 10 | Documentation & comments | ☑ | XML docs accurate. `Component-CLI.md` drift folded into CLI-015. |
## Findings
### CLI-001 — `SCADALINK_FORMAT` env var and config-file format are dead; format precedence broken
@@ -537,3 +570,121 @@ Resolved 2026-05-16 (commit pending). Coverage gaps confirmed and closed:
`InstanceCommands.TryParseBindings`/`TryParseOverrides` and covered by
`InstanceArgumentParsingTests` under CLI-005.
The CLI test suite went from 42 to 77 passing tests.
### CLI-014 — `update` commands require "core" fields, making partial updates impossible
| | |
|--|--|
| Severity | Medium |
| Category | Correctness & logic bugs |
| Status | Open |
| Location | `src/ScadaLink.CLI/Commands/TemplateCommands.cs:77`, `src/ScadaLink.CLI/Commands/SiteCommands.cs:86`, `src/ScadaLink.CLI/Commands/ExternalSystemCommands.cs:40-42`, `src/ScadaLink.CLI/Commands/DataConnectionCommands.cs:39-40`, `src/ScadaLink.CLI/Commands/NotificationCommands.cs:40-41`, `src/ScadaLink.CLI/Commands/ApiMethodCommands.cs:79` |
**Description**
The design doc presents `update` commands with all non-`--id` fields as optional, e.g.
`template update --id <id> [--name <name>] [--description <desc>] [--parent-id <id>]`
(`Component-CLI.md:62`) and `api-method update --id <id> [--script <code>] ...`
(`Component-CLI.md:224`). The implementation contradicts this: the update commands mark
the entity's "core" fields as `Required = true`, so the user must always re-supply them:
- `template update``--name` is `Required = true` (`TemplateCommands.cs:77`).
- `site update``--name` is `Required = true` (`SiteCommands.cs:86`).
- `external-system update``--name`, `--endpoint-url`, `--auth-type` are all
`Required = true` (`ExternalSystemCommands.cs:40-42`).
- `data-connection update``--name`, `--protocol` are `Required = true`
(`DataConnectionCommands.cs:39-40`).
- `notification update``--name`, `--emails` are `Required = true`
(`NotificationCommands.cs:40-41`).
- `api-method update``--script` is `Required = true` (`ApiMethodCommands.cs:79`).
- The same pattern applies to `template attribute/alarm/script update`
(`TemplateCommands.cs:164-165`, `246-248`, `332-334`) and `role-mapping update`
(`SecurityCommands.cs:110-111`).
Because the corresponding `Update*Command` records are whole-replace (they carry the full
field set, not a sparse patch), a user who wants to change only one field — e.g. flip an
API method's timeout, or rename a template — must look up and re-pass every other field's
current value. Omitting any required flag is a hard parse error. This makes scripted,
single-field updates (a core CLI/CI use case) awkward and error-prone, and it does not
match the documented optional-flag surface.
**Recommendation**
Decide on one model and align doc + code. Either (a) make the update flags genuinely
optional and have the server/`Update*Command` treat a null field as "leave unchanged"
(sparse patch), or (b) if whole-replace is intentional, update `Component-CLI.md` to show
these flags as required (no `[...]`) and document that an update replaces the whole
entity. Option (a) matches the documented surface and the typical CLI expectation.
**Resolution**
_Unresolved._
### CLI-015 — `Component-CLI.md` command surface has drifted again in two places
| | |
|--|--|
| Severity | Low |
| Category | Design-document adherence |
| Status | Open |
| Location | `docs/requirements/Component-CLI.md:75`, `docs/requirements/Component-CLI.md:125-126` (vs. `src/ScadaLink.CLI/Commands/TemplateCommands.cs:404-413`, `src/ScadaLink.CLI/Commands/DataConnectionCommands.cs:41`, `:86`) |
**Description**
CLI-007 regenerated the doc's "Command Structure" section, but two specific drifts remain
or were introduced:
- `Component-CLI.md:75` documents `template composition delete --template-id <id>
--instance-name <name>`, but the implementation (`TemplateCommands.cs:404-413`) deletes
a composition by its own integer ID via a single `--id` option
(`DeleteTemplateCompositionCommand(id)`). The doc's two-flag form does not exist.
- `data-connection create` and `update` accept a `--primary-config` option (aliased
`--configuration`) for the primary configuration JSON (`DataConnectionCommands.cs:86`,
`:41`), but `Component-CLI.md:125-126` lists only `--backup-config` and
`--failover-retry-count` — the primary-config flag is absent from the doc.
A reader following the doc would use a non-existent `template composition delete` form and
would not discover the `--primary-config` flag.
**Recommendation**
Correct `Component-CLI.md:75` to `template composition delete --id <id>`, and add
`[--primary-config <json>]` to the documented `data-connection create`/`update` signatures
(`Component-CLI.md:125-126`). Also note the `--configuration` alias if aliases are
documented elsewhere.
**Resolution**
_Unresolved._
### CLI-016 — `WriteAsTable` derives columns from the first array element only
| | |
|--|--|
| Severity | Low |
| Category | Correctness & logic bugs |
| Status | Open |
| Location | `src/ScadaLink.CLI/Commands/CommandHelpers.cs:184-200` |
**Description**
When rendering a JSON array as a table, `WriteAsTable` builds the header set from
`items[0].EnumerateObject()` only (`CommandHelpers.cs:184-186`) and then projects every
row against that fixed header list (`:188-198`). If a later element of the array has a
different shape — additional properties, or properties the first element lacks — those
extra columns are silently dropped from the table and a row missing a header property
renders an empty cell. The user sees a table that appears complete but has omitted data,
with no indication that columns were discarded. (The JSON output path is unaffected; this
only affects `--format table`.) Management API list responses are generally homogeneous,
so the practical impact is low, but a heterogeneous array — e.g. a diff or a mixed-status
list — would be rendered incorrectly with no warning.
**Recommendation**
Compute the header set as the union of property names across all array elements (iterate
all items, collect distinct property names preserving first-seen order) before projecting
rows, so no element's data is silently dropped.
**Resolution**
_Unresolved._