review(Driver.Cli.Common): drop dead FormatStatus branch + timestamp-kind test
Re-review at 7286d320. -009: remove unreachable name-is-null branch in FormatStatus +
invariant test. -010: pin DateTimeKind.Unspecified FormatTimestamp behavior.
This commit is contained in:
@@ -4,8 +4,8 @@
|
|||||||
|---|---|
|
|---|---|
|
||||||
| Module | `src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Cli.Common` |
|
| Module | `src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Cli.Common` |
|
||||||
| Reviewer | Claude Code |
|
| Reviewer | Claude Code |
|
||||||
| Review date | 2026-05-23 |
|
| Review date | 2026-06-19 |
|
||||||
| Commit reviewed | `a9be809` |
|
| Commit reviewed | `7286d320` |
|
||||||
| Status | Reviewed |
|
| Status | Reviewed |
|
||||||
| Open findings | 0 |
|
| Open findings | 0 |
|
||||||
|
|
||||||
@@ -362,3 +362,104 @@ well-known Theory comment block; the redundant Theory is dead weight.
|
|||||||
`InlineData` rows are covered by the well-known Theory's `ShouldBe` (strict
|
`InlineData` rows are covered by the well-known Theory's `ShouldBe` (strict
|
||||||
exact-match assertion), which is the authoritative shortlist test. Landed
|
exact-match assertion), which is the authoritative shortlist test. Landed
|
||||||
alongside the Driver.Cli.Common-007 fix in the same commit.
|
alongside the Driver.Cli.Common-007 fix in the same commit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Re-review 2026-06-19 (commit `7286d320`)
|
||||||
|
|
||||||
|
Delta scope since `a9be809`:
|
||||||
|
|
||||||
|
- `a6ae4e22` — `fix(status-codes)`: corrected `BadDeviceFailure` from
|
||||||
|
`0x80550000` to `0x808B0000` in `FormatStatus` and all six native-protocol
|
||||||
|
mappers (Driver.Cli.Common-007); deleted the redundant `FormatStatus_names_native_driver_emitted_codes`
|
||||||
|
Theory (Driver.Cli.Common-008). Both already Resolved.
|
||||||
|
- `2b811477` — `chore(build)`: stripped inline `Version` attributes from
|
||||||
|
`PackageReference` elements and centralised them in `Directory.Packages.props`.
|
||||||
|
No logic change.
|
||||||
|
- `64e3fbe0` — `docs`: XML documentation backfilled on all previously-undocumented
|
||||||
|
public members (`<summary>`, `<param>`, `<returns>`, `<inheritdoc/>` where
|
||||||
|
applicable). No logic change; `CS1591` suppression in the csproj retained (the
|
||||||
|
`NoWarn` suppression now acts as a belt-and-suspenders guard rather than primary
|
||||||
|
silence).
|
||||||
|
|
||||||
|
Review covered all 10 categories. Two new findings recorded (both Low).
|
||||||
|
|
||||||
|
| # | Category | Result |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | Correctness & logic bugs | Driver.Cli.Common-009 |
|
||||||
|
| 2 | OtOpcUa conventions | No issues found |
|
||||||
|
| 3 | Concurrency & thread safety | No issues found |
|
||||||
|
| 4 | Error handling & resilience | No issues found |
|
||||||
|
| 5 | Security | No issues found |
|
||||||
|
| 6 | Performance & resource management | No issues found |
|
||||||
|
| 7 | Design-document adherence | No issues found |
|
||||||
|
| 8 | Code organization & conventions | No issues found |
|
||||||
|
| 9 | Testing coverage | Driver.Cli.Common-010 |
|
||||||
|
| 10 | Documentation & comments | No issues found |
|
||||||
|
|
||||||
|
Both findings were fixed in-session (TDD: tests first, then src change). Suite
|
||||||
|
went from 43 → 49 tests; build clean.
|
||||||
|
|
||||||
|
### Driver.Cli.Common-009
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Severity | Low |
|
||||||
|
| Category | Correctness & logic bugs |
|
||||||
|
| Location | `src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Cli.Common/SnapshotFormatter.cs:159-161` |
|
||||||
|
| Status | Resolved |
|
||||||
|
|
||||||
|
**Description:** After the named-shortlist match and the severity-class fallback
|
||||||
|
that were introduced by Driver.Cli.Common-002, `name` can never be `null` at the
|
||||||
|
`return` statement — the severity fallback switch unconditionally assigns one of
|
||||||
|
`"Good"`, `"Uncertain"`, or `"Bad"`. The surviving `name is null` ternary therefore
|
||||||
|
has a dead branch: the bare-hex path (`$"0x{statusCode:X8}"` with no parenthesised
|
||||||
|
label) is unreachable. This is misleading to readers: it implies there exists a
|
||||||
|
code path where neither the named shortlist nor the severity fallback produce a
|
||||||
|
label, which is false. No correctness impact in practice, but it contradicts the
|
||||||
|
invariant the -002 fix established (operators always see a quality label) and could
|
||||||
|
confuse a future maintainer who extends the shortlist and thinks they need to handle
|
||||||
|
the `null` case explicitly.
|
||||||
|
|
||||||
|
**Recommendation:** Remove the dead ternary; replace with a direct
|
||||||
|
`return $"0x{statusCode:X8} ({name})";` and add a clarifying comment that `name`
|
||||||
|
is always non-null at this point (assigned by shortlist or fallback). Pin a
|
||||||
|
regression `[Theory]` test asserting every output includes a `"("` so future edits
|
||||||
|
cannot accidentally reintroduce the bare-hex path.
|
||||||
|
|
||||||
|
**Resolution:** Resolved 2026-06-19 — removed the dead `name is null` ternary in
|
||||||
|
`FormatStatus`; replaced with `return $"0x{statusCode:X8} ({name})";` plus a
|
||||||
|
comment explaining the invariant. Regression test
|
||||||
|
`FormatStatus_always_includes_parenthesised_label` (5-row `[Theory]`) added to
|
||||||
|
`SnapshotFormatterTests.cs` to pin the guarantee.
|
||||||
|
|
||||||
|
### Driver.Cli.Common-010
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Severity | Low |
|
||||||
|
| Category | Testing coverage |
|
||||||
|
| Location | `src/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Cli.Common/SnapshotFormatter.cs:167-172` / `tests/Drivers/Cli/ZB.MOM.WW.OtOpcUa.Driver.Cli.Common.Tests/SnapshotFormatterTests.cs` |
|
||||||
|
| Status | Resolved |
|
||||||
|
|
||||||
|
**Description:** `FormatTimestamp` converts `DateTime` values whose `Kind` is not
|
||||||
|
`DateTimeKind.Utc` via `ToUniversalTime()`. For `DateTimeKind.Local` this is
|
||||||
|
correct and tested (`FormatTimestamp_normalises_local_kind_to_utc`). However,
|
||||||
|
`DateTimeKind.Unspecified` is a valid `Kind` produced by, e.g., EF-materialized
|
||||||
|
timestamps or unconfigured driver clocks; `ToUniversalTime()` treats `Unspecified`
|
||||||
|
identically to `Local` (documented .NET behavior). The test suite never exercises
|
||||||
|
this case. The behavior is correct per OPC UA conventions (unspecified → assume
|
||||||
|
local → convert to UTC), but the gap was noted in Driver.Cli.Common-005's description
|
||||||
|
and was never closed during the -005 resolution commit. Without the test, a future
|
||||||
|
refactor of the `Kind` branch could silently drop `Unspecified` conversion and no
|
||||||
|
test would catch it.
|
||||||
|
|
||||||
|
**Recommendation:** Add a `[Fact]` asserting that a `DateTimeKind.Unspecified`
|
||||||
|
timestamp also produces a `"Z"`-suffixed output string, documenting the
|
||||||
|
"treat Unspecified as Local" contract.
|
||||||
|
|
||||||
|
**Resolution:** Resolved 2026-06-19 — added
|
||||||
|
`FormatTimestamp_treats_unspecified_kind_as_local_and_converts_to_utc` to
|
||||||
|
`SnapshotFormatterTests.cs`, asserting that a `DateTimeKind.Unspecified` value
|
||||||
|
produces a `"Z"`-suffixed ISO-8601 string. No source change needed (behavior was
|
||||||
|
already correct).
|
||||||
|
|||||||
@@ -156,9 +156,9 @@ public static class SnapshotFormatter
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return name is null
|
// name is always non-null here: the named shortlist assigns it directly, and
|
||||||
? $"0x{statusCode:X8}"
|
// the severity-class fallback above assigns one of "Good" / "Uncertain" / "Bad".
|
||||||
: $"0x{statusCode:X8} ({name})";
|
return $"0x{statusCode:X8} ({name})";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Formats a UTC timestamp as an ISO 8601 string, or "-" if null.</summary>
|
/// <summary>Formats a UTC timestamp as an ISO 8601 string, or "-" if null.</summary>
|
||||||
|
|||||||
@@ -217,6 +217,39 @@ public sealed class SnapshotFormatterTests
|
|||||||
table.ShouldContain("SOURCE TIME");
|
table.ShouldContain("SOURCE TIME");
|
||||||
table.ShouldContain("---");
|
table.ShouldContain("---");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Driver.Cli.Common-009: dead-code in FormatStatus ---
|
||||||
|
|
||||||
|
/// <summary>Verifies that FormatStatus always includes a parenthesised label — bare-hex-only output is unreachable.</summary>
|
||||||
|
/// <param name="statusCode">An arbitrary OPC UA status code.</param>
|
||||||
|
[Theory]
|
||||||
|
// The severity-class fallback fires for every code not in the named shortlist, so
|
||||||
|
// the output always carries a "(<label>)" suffix. Driver.Cli.Common-009 noted that
|
||||||
|
// the "name is null" branch after the fallback is dead code; this Theory pins the
|
||||||
|
// guarantee so the dead branch can be safely removed.
|
||||||
|
[InlineData(0x00000000u)] // Good
|
||||||
|
[InlineData(0x80000000u)] // Bad
|
||||||
|
[InlineData(0x40000000u)] // Uncertain
|
||||||
|
[InlineData(0x80990000u)] // Unknown-bad → fallback "Bad"
|
||||||
|
[InlineData(0xDEADBEEFu)] // Garbage → fallback "Bad"
|
||||||
|
public void FormatStatus_always_includes_parenthesised_label(uint statusCode)
|
||||||
|
{
|
||||||
|
// Verify the output contains "(" — meaning the name/severity label is always appended.
|
||||||
|
SnapshotFormatter.FormatStatus(statusCode).ShouldContain("(");
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Driver.Cli.Common-010: FormatTimestamp with Unspecified kind ---
|
||||||
|
|
||||||
|
/// <summary>Verifies that FormatTimestamp treats Unspecified kind as Local and converts to UTC.</summary>
|
||||||
|
[Fact]
|
||||||
|
public void FormatTimestamp_treats_unspecified_kind_as_local_and_converts_to_utc()
|
||||||
|
{
|
||||||
|
// DateTimeKind.Unspecified is silently treated as Local by ToUniversalTime().
|
||||||
|
// The result must still carry the "Z" suffix so consumers treat it as UTC.
|
||||||
|
var unspecified = new DateTime(2026, 4, 21, 12, 0, 0, DateTimeKind.Unspecified);
|
||||||
|
var formatted = SnapshotFormatter.FormatTimestamp(unspecified);
|
||||||
|
formatted.ShouldEndWith("Z");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Trait("Category", "Unit")]
|
[Trait("Category", "Unit")]
|
||||||
|
|||||||
Reference in New Issue
Block a user