Files
annotations/_docs/04_refactoring/01-testability-refactoring/verification.md
T
Oleksandr Bezdieniezhnykh d7d1c0ed6a [AZ-PENDING-1] [AZ-PENDING-2] Step 4 close-out: verification + docs
Phase 6 smoke (Docker, _docs/04_refactoring/01-testability-refactoring/
smoke-compose.yml):
  - Annotations app boots clean under ASPNETCORE_ENVIRONMENT=E2ETest.
  - /health 200 OK; /annotations with bearer returns 401 with the
    JWT library's own malformed-token rejection.
  - 0 IDX20108 occurrences in logs (C01 verified).
  - 0 IPAddress.Parse FormatException occurrences; FailsafeProducer
    reaches the broker via Docker DNS (C02 verified).
  - Full smoke report in verification.md.

Phase 7 docs:
  - architecture.md: retire Open Risks §6 (testability blocker
    resolved). Update the constraints block to describe the
    ASPNETCORE_ENVIRONMENT-gated RequireHttps behavior.
  - components/06_platform/description.md: one-liner on JwtExtensions
    JWKS gating.
  - components/02_annotations-realtime-sync/description.md: one-liner
    on FailsafeProducer host resolution accepting literal IP or DNS.
  - tests/test-data.md: refresh the JWKS URL configuration section to
    point at the resolved implementation instead of the open risk.

Task housekeeping:
  - _docs/02_tasks/todo/01_*.md -> done/
  - _docs/02_tasks/todo/02_*.md -> done/
  - _docs/_autodev_state.md: advance to Step 5 (Refactor Backlog Triage).

Tracker IDs remain placeholders pending Atlassian MCP availability —
real IDs to be assigned per
_docs/_process_leftovers/2026-05-14_testability-tracker.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-14 20:38:14 +03:00

4.4 KiB

Phase 6 — Verification Report

Date: 2026-05-14 Run: 01-testability-refactoring, cycle 1 Verdict: PASS

1. Static build

Step Command Result
.NET build (host) dotnet build src/Azaion.Annotations.csproj -c Debug --no-restore 0 errors, 39 pre-existing CS8632 warnings (all ?-on-non-nullable-context — none introduced by this batch).
.NET build (containerised) dotnet run --project src/Azaion.Annotations.csproj inside mcr.microsoft.com/dotnet/sdk:10.0 App compiled successfully and reached Application started. (see smoke logs).
Lint ReadLints on src/Auth/JwtExtensions.cs and src/Services/FailsafeProducer.cs 0 new lint issues.

2. Smoke run

2.1 Stack

Smoke compose: _docs/04_refactoring/01-testability-refactoring/smoke-compose.yml.

Topology — postgres + rabbitmq (with streams plugin) + python:3.12-alpine serving a stub JWKS over HTTP + annotations app running directly via mcr.microsoft.com/dotnet/sdk:10.0 with the repo bind-mounted at /repo.

Why not the production Dockerfile: src/Dockerfile has a build-context bug (uses WORKDIR /src + COPY . . then dotnet publish with no project arg, which fails because the .csproj lives one level deeper). That bug is OUT OF SCOPE for the testability refactor and will be fixed in autodev Step 6 when the full e2e harness comes online.

2.2 Probes

Probe Expected Observed
Annotations container reaches healthy ≤ 90 s 15 s
Hosting environment E2ETest info: Microsoft.Hosting.Lifetime[0] Hosting environment: E2ETest
GET /health (anonymous) 200 OK 200 OK, multiple requests during runtime
GET /annotations with Authorization: Bearer dummy.invalid.token (protected) 401 (token rejected by validator) 401 Unauthorized, WWW-Authenticate: Bearer error="invalid_token"

2.3 Failure signatures — the two we fixed

Signature Looking for Found
IDX20108 ("The address specified ... is not valid as per HTTPS scheme") 0 occurrences — proves C01 is active 0 occurrences
IPAddress.Parse FormatException ("An invalid IP address was specified") 0 occurrences — proves C02 is active 0 occurrences

2.4 Failure signatures — unrelated to this batch

Signature Severity Why it's present
FailsafeProducer ... CreateProducerException: StreamDoesNotExist Expected The smoke stack does not declare the azaion.detections stream; the seed step that creates streams lives in e2e/seed/run.sh and only runs as part of the full e2e harness. The producer reaches the broker (which proves C02), then fails because the stream is missing. Would fail identically with a literal-IP RABBITMQ_HOST.
IDX10400: Unable to decode '...' as Base64url encoded string Expected The smoke probe deliberately sends dummy.invalid.token, which is not valid base64url. This is the JWT library's own rejection of a malformed token — NOT the IPAddress.Parse FormatException nor the IDX20108 we fixed. It is the desired 401 path.

3. Functional behavior unchanged for the non-test paths

Concern Method Result
HTTPS-only enforcement preserved under non-test envs Code review of JwtExtensions.cs:30-37: requireHttpsForJwks is false only when ASPNETCORE_ENVIRONMENT == "E2ETest" (case-insensitive). ✓ Passed
Literal-IP RABBITMQ_HOST still works Code review of FailsafeProducer.cs:ResolveHostAddress: IPAddress.TryParse(host, out var literal) short-circuits before DNS. ✓ Passed
Token validation pipeline unchanged TokenValidationParameters block, algorithm pinning (ES256), signature/issuer/audience/lifetime checks all identical to pre-change code. ✓ Passed

4. Verdict

PASS. Both surgical changes (C01, C02) behave exactly as specified by their task acceptance criteria. No regression observed in the unchanged paths. Production safety preserved (HTTPS-required when not in E2ETest; literal-IP path unchanged).

Tear-down: docker compose -f smoke-compose.yml down -v completed cleanly; no orphaned volumes or networks remain. The smoke-compose.yml file is intentionally retained as a verification artifact under _docs/04_refactoring/01-testability-refactoring/ — it is NOT part of the test harness or the production stack.