test: drive coverage to the code that could not prove itself - #68
Merged
Conversation
Backend authored-code line coverage 77.6% -> 92.4%; frontend 87.9% -> 95%. Backend tests 218 -> 265, frontend 22 -> 27. Not padding — every addition exercises a path that previously ran only in production: The OpenAI live path (14% covered) constructed its ChatClient internally, so everything between "request sent" and "containment gate" was unprovable offline. The one line that leaves the process is now an injectable delegate, and the full pipeline — prompt assembly, timeout envelope, deserialization, gate, every fallback — runs under test. The replay corpus now drives the whole provider: all ten recorded replies, including the injection cases, must reach the same user-visible outcome the gate verdict implies. The GitHub layer (reader 4%, token provider 13%, resolver 65%) is faked at the HttpClient layer so real URL construction, auth headers, allowlist gates, base64 decoding, annotated-tag walks and the tag-loop ceiling all execute. The token cache is pinned: a fresh token makes no second exchange, a nearly-expired one must. The /api/github endpoints could not be integration-tested at all: Program read GitHubOptions from configuration before Build(), so the endpoints captured a snapshot no test host could influence. Options now resolve from DI per request — one source of truth — and ten endpoint tests cover the status ladder, allowlist refusals, and analysis of retrieved content. Frontend: the unlock flow (including the wrong-key regression this app actually had), lock/clear, and the export download path. Measured with the built-in collector, generated code (*.g.cs, *.generated.cs) excluded as not-authored. Honest remainder: bootstrap main.tsx, type-only models.ts, Kestrel/CORS wiring in Program, and defensive branches whose triggers are not constructible in-process. Async inventory verified against the pre-refactor baseline: 20 methods then, 22 now, zero removed — and CS1998/CS4014 stay unsuppressed under TreatWarningsAsErrors, so a silently de-asynced method fails the build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Coverage was never measured in this repository. First measurement: backend 77.6%, frontend 87.9%. After this PR: backend 92.4%, frontend 95% (authored code; generated
*.g.cs/*.generated.cs, bootstrapmain.tsx, and type-onlymodels.tsexcluded as not-authored).Tests: backend 218 → 265, frontend 22 → 27. None of it is padding — each addition reaches a path that previously ran only in production:
The OpenAI live path was unprovable offline (14%)
The provider built its
ChatClientinternally, so everything between "request sent" and "containment gate" could not run under test. The one line that leaves the process is now an injectable delegate (CompleteChat); the public constructor wraps the real client, an internal one substitutes transport. The replay corpus now drives the entire provider — all ten recorded replies, injection cases included, must produce the user-visible outcome their gate verdict implies. Plus: prompt content, context truncation, timeout envelope, malformed JSON, transport failure, missing key.The GitHub layer was 4–65% covered
Reader, token provider and action-reference resolver are faked at the
HttpClientlayer, so the real request pipeline executes: URL construction, bearer headers, allowlist refusals before any request, base64 decoding, annotated-tag dereferencing, branch fallback, and the tag-loop ceiling. The token cache is pinned in both directions: fresh token → no second exchange; nearly-expired → must exchange./api/githubendpoints were structurally untestableProgramreadGitHubOptionsfrom configuration beforeBuild()— andWebApplicationFactoryconfiguration lands after that read, so the endpoints captured a snapshot no test host could influence. Options now resolve from DI per request (one source of truth; the captured copy could disagree with the container's if registration ever changed). Ten integration tests now cover the status ladder, 403 allowlist refusals, 404s, and analysis of retrieved content with and without AI.Frontend
The unlock flow — including the wrong-key-stored-as-though-it-worked regression this app actually had — lock/clear, and the export download path (server-supplied filename, object-URL lifecycle, failure without download).
Async methods: verified, not assumed
Inventory against the pre-refactor baseline: 20 async methods then, 22 now, zero removed (the two added are the public-scan pair).
CS1998/CS4014remain unsuppressed underTreatWarningsAsErrors, so a silently de-asynced or unawaited method fails the build — the guarantee holds going forward.Verification
265 + 27 = 292 tests, all green, 0 warnings. Full suite run after every structural change.