nc_program_manager/.paul/phases/13-robustness-fixes/13-01-SUMMARY.md
dtrentin 32f68c5ed5 feat(14-test-hardening): env-var hardware test scaffolding + LicenseFile edge cases
Phase 14 complete — v0.4 Robustness milestone closed:
- HardwareTestHelper.GetIpOrSkip: Assert.Ignore when HEIDENHAIN/SIEMENS/MITSUBISHI_IP not set (ISS-007)
- Hardware_ConnectAsync_RealMachine [Category("Hardware")] added to all 3 integration test files
- LicenseFileTests: 4 tests covering empty, whitespace, valid base64, nonexistent path
- Validate_EmptyFingerprint_ReturnsFalse: confirms validator safe against empty input (ISS-008 scaffold)
- NcProgramManager.Tests.csproj: <Compile> entries for 2 new files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 23:24:58 +02:00

70 lines
2.6 KiB
Markdown

---
phase: 13-robustness-fixes
plan: 01
type: summary
status: complete
completed: 2026-05-18
commit: 4cd7715
---
# Summary: Plan 13-01 — Robustness Fixes
## What Was Built
Three targeted defensive fixes. No new files. No interface changes.
### Task 1 — MachineFingerprint null guard (ISS-001)
**File:** `Licensing/MachineFingerprint.cs`
Both WMI property reads now use null-safe operators:
- `mo.Properties["processorID"]?.Value?.ToString() ?? string.Empty`
- `obj["SerialNumber"]?.ToString() ?? string.Empty`
Customers on hardware where WMI returns null for processorID or SerialNumber no longer get an unhandled NullReferenceException at license check startup.
### Task 2 — SHA256 provider disposed (ISS-005)
**File:** `Licensing/LicenseValidator.cs`
`SHA256CryptoServiceProvider` wrapped in `using` block inside `Validate()`. Disposed on every call — success or failure. RSACryptoServiceProvider pattern unchanged. Public key constant untouched.
### Task 3 — LSV2 connect timeout aborts socket (ISS-006)
**File:** `Cnc/Heidenhain/Lsv2Client.cs`
`Connect()` now calls `_tcp.Close()` when `ConnectAsync().Wait(timeout)` returns false. Closes the underlying socket, causing the pending task to complete with SocketException rather than holding a thread-pool slot indefinitely. No async/await introduced (Connect() stays synchronous by design).
## Acceptance Criteria Results
| AC | Description | Result |
|----|-------------|--------|
| AC-1 | WMI null returns non-null string, no throw | PASS |
| AC-2 | SHA256 provider disposed after every Validate() | PASS |
| AC-3 | Timeout branch closes socket before returning false | PASS |
## Decisions Made
- `?? string.Empty` used (not `?? "unknown"`) — fingerprint string concatenated; empty is correct fallback
- `_tcp.Close()` in bare `try { } catch { }` — same pattern as existing Disconnect() in same file
- No CancellationTokenSource pattern (ISS-006 fix note suggested it) — simpler Close() approach sufficient for .NET 4.7.2 synchronous design
## Deferred
None — all three tasks completed as planned.
## Files Modified
- `Licensing/MachineFingerprint.cs` — null guards on WMI reads
- `Licensing/LicenseValidator.cs` — SHA256 using block
- `Cnc/Heidenhain/Lsv2Client.cs` — socket close on timeout
## Issues Closed
- ISS-001 — MachineFingerprint null dereference on unusual hardware ✓
- ISS-005 — SHA256 crypto provider not disposed ✓
- ISS-006 — LSV2 connect timeout doesn't abort hung task ✓
## Commit
`4cd7715` — fix(robustness): null guard WMI reads, dispose SHA256, abort LSV2 timeout (ISS-001, ISS-005, ISS-006)