Skip to content

v4.0.2 — HealthChecks lockstep + JSON hardening + test guards

Choose a tag to compare

@07artem132 07artem132 released this 24 May 17:45
· 78 commits to main since this release

Patch-реліз без breaking changes. Якщо ти використовуєш SignalCli.NET.HealthChecks — онови його разом з main package: до 4.0.2 версії розійшлися й змішування пакетів давало runtime crash. Решта — 8 нових захисних тестів і одна реальна знахідка про JSON-валідацію, яка мовчки не fire'ила у продакшені.

🐛 Виправлено

  • Якщо консьюм'иш обидва пакети (SignalCli.NET + SignalCli.NET.HealthChecks) — онови їх разом. До 4.0.2 main lib був 4.0.1, а health-checks-adapter — 3.0.0. Змішування пакетів кидало MissingMethodException на першому health-check probe (бо adapter читає internal'и main lib через [InternalsVisibleTo]). Тепер обидва завжди ходять в lockstep через єдиний MSBuild property у Directory.Build.props. Bug-class виключено: reflection-тест VersionLockstepTests ловить розбіжність до merge'у. (NF-003, RG07)

  • JSON duplicate-key захист (rule #18) тепер реально діє на production-шляху. З 4.0.0 ми оголошували що malformed signal-cli response з повтореним ключем fail'ить deserialize. У реальності — fail'ило тільки на reflection-шляху, а production деsеріалізує через source-gen fast-path який власний runtime-flag не консумує. Тепер захист увімкнено на обох рівнях (runtime flag + source-gen attribute). Без впливу на нормальні signal-cli responses — Jackson на upstream-стороні фізично не може емітити дублікати; fix чисто defensive проти MITM/corruption. Знайшли під час audit'у коли наш же RG-тест спочатку failed. (json-hardening-source-gen-attribute)

  • CI pipeline нарешті пушить SignalCli.NET.HealthChecks на NuGet feed разом з main package. До 4.0.2 publish-nuget.yml workflow мав pack-steps для 5 пакетів (SignalCli.NET, SignalCli.Runtime, SignalCli.Runtime.Native, SignalCli.Runtime.Jre.{win-x64,osx-arm64}) — але SignalCli.NET.HealthChecks був відсутній. Net effect: csproj-сторона мала версії в lockstep (NF-003 main fix), але consumer-сторона все одно отримувала stale 3.0.0 HealthChecks-пакета з registry бо нічого свіжіше не пушилось. Цей gap робив весь NF-003 lockstep косметичним на consumer-стороні. Тепер крок присутній — після першого gh release create v4.0.2 HealthChecks теж потрапить на feed. (nf003-completion follow-up)

🛡️ Захист від регресій

8 нових unit + 1 E2E-тести закривають declared-але-untested invariants з CLAUDE.md "Future development guardrails":

  • Паралельні JSON-RPC виклики тепер pinned проти реального signal-cli — не лише проти mock'а на unit-рівні. Новий E2E запускає bundled JRE, кидає 10 паралельних version-викликів через справжній signal-cli virtual-thread dispatcher, асертить що всі 10 повертають свою власну відповідь. Якщо хтось у майбутньому замінить ConcurrentDictionary<id, TCS> на Queue<TCS> "бо порядок збережений" — unit-тести з in-order Subject<T>-моком цього б не побачили; цей E2E одразу впаде. (e2e-coverage-expansion)

  • Observability counters тепер pinned на всіх 3 тригерах перезапуску. Раніше privacy-guards перевіряли лише відсутність PII у Meter-tags, а фактичне інкрементування signalcli.process.restarts{trigger=…} було pinned тільки на trigger=force. Тепер trigger=crash (через event-raise на mock IProcess) і trigger=health (через FakeTimeProvider.Advance повз HealthCheckIntervalSeconds) теж покриті. (G4 subcases — T04, T05)

  • Event-API симетрія enforced reflection-тестом. CLAUDE.md правило "кожен IObservable<T> event-kind має парний IAsyncEnumerable<T> метод" раніше enforcъ'вся code-review'ом. Тепер EventApiSymmetryTests reflectively сканує ISignalEventService і fail'ить build якщо хтось додав 11-й event без парного методу. (NF-002, RG06)

  • JSON duplicate-key захист — 3-шаровий guard. Окрім самого fix'у з "Виправлено" вище, додано 3 facts у JsonSerializationTests: SignalJsonOptions_…IsFalse, JsonDocumentOptions_…ThrowsOnDuplicateKey, SignalJsonContext_…ThrowsOnDuplicateKey. Видалення будь-якого з 3 шарів (runtime flag, .NET 10 API contract, source-gen attribute) surface'иться як failed test. (G12, RG05)

  • JSON-RPC error wins over result. Захисний test пінує що при malformed-response (одночасно result І error) JsonRpcClient кидає exception, а не повертає result. CHANGELOG [4.0.1] стверджував покриття; grep показав що тесту не існувало. Тепер дійсно існує. (NF-001, G9)

🛠 Інше

  • Test-проект тепер ловить warnings як errors (як main lib давно). Раніше тихо акумулювалися 3 xUnit1031 violations у SyncDisposeDuringCleanupTests.cs (deadlock-prone .GetAwaiter().GetResult()). Виправлено: метод-сигнатури async Task + await; service.Dispose() навмисно залишається SYNC бо саме його тести й вимірюють. (NF-004)

  • Microsoft.Extensions.*.Testing bumped 9.0.0 → 10.0.0 — закриває last mismatched-major у dependency graph. Public-surface unchanged per MS Learn. (NF-005)

  • Версія обох NuGet-пакетів тепер централізована в Directory.Build.props → <SignalCliPackageVersion>. Bump = single-file edit. Hardcoded <Version> у будь-якому csproj заборонено.

  • CLAUDE.md розширено двома секціями для майбутніх контрибуторів + новим правилом:

    • "Audit baseline" — мінімальна планка яку PR не може опустити (test counts, regression guards, архітектурні invariants).
    • "How we discovered these issues" — 5 failure-mode-сценаріїв з PR-time checklists, щоб не повторювати ті самі паттерни.
    • "Version-CHANGELOG lockstep" + "CHANGELOG voice template" — bump версії МУСИТЬ йти з CHANGELOG-секцією в одному коміті; новий template для consumer-first voice (bold leading claim, технічна обгортка, internal-IDs у дужках).
    • Виправлено константа MaxInlineEncodedAttachmentBytes: 15 000 000 → 12 000 000 (drift з 4.0.0 — реальне значення в коді змінилось у signal-cli-protocol-alignment §5, doc відстав). (NF-006)
  • CHANGELOG voice tightened — починаючи з [4.0.2] (цей entry), пишемо у consumer-first voice. Старіші версії [4.0.1]/[4.0.0] перероблено в тому ж коміті задля консистенції.

📊 Тестова статистика

  • Unit: 279 → 287 (+8 захисних тестів).
  • Integration: 7 → 8 (+1 E2E на паралельний RPC).
  • Build на src/ + Tests/: 0 warnings, 0 errors.

Pending follow-up

Нічого — усі audit v2.1 знахідки закриті.