test-coverage-chunks.md + test-coverage-task-template.md in this PR.
The CEO set a target of 80% line coverage. We're at 1.5%.
Closing that gap on a 9-year-old codebase with ~186k coverable lines,
no mocking framework, and ~200 controllers that new RakletDb() inline
is a multi-quarter initiative — not a sprint. This artifact lays out the cost honestly so
the CEO can pick a target curve before any test gets written.
2,906 of 186,449 lines · 383 MSTest tests · 31 assemblies in report
~25 third-party NuGet assemblies (AWSSDK, Intuit.Ipp, etc.) inflate the headline denominator by 5-10×. Real picture below.
RakletDb usage. Fine for pure-math service helpers — does
not extend to controllers, DB-bound services, async pipelines, or HTTP
boundary logic.
| Target | Lines covered | Net new lines | Approx new tests @ 3-5 lines/test |
|---|---|---|---|
| Today | 2,906 | — | — |
| 10% | 18,645 | +15,739 | 3,000-5,000 |
| 25% | 46,612 | +43,706 | 9,000-15,000 |
| 50% | 93,225 | +90,319 | 18,000-30,000 |
| 80% | 149,159 | +146,253 | 30,000-50,000 |
packages.config has only MSTest + EF. No Moq, NSubstitute, FakeItEasy. Decision: pick one and adopt.
EF Core's UseInMemoryDatabase doesn't exist for EF6. Options: Effort.EF6 (unmaintained), hand-rolled IDbSet<T> fakes (recommended), or real LocalDB (slow).
new RakletDb() inlineTop 5 controllers (AccountController is 1,251 LOC) instantiate services directly. Not testable as-is — need constructor-injection refactor first.
All 383 existing tests are sync. Controllers are async-heavy. Need one-time pattern decision before async controller tests can land.
ControllerTestBaseEvery controller test needs stubbed ControllerContext + mock HttpContextBase + principal + route data. Without a base class, each test re-implements this.
Coverlet measurement landed in #13815. Permission fix in #13833. Per-PR coverage summaries already appearing on Tests workflow runs. Foundation is laid.
Add NuGet excludes to coverlet. Honest headline becomes ~3-5%. Trend output. Re-tag stragglers.
Add Moq. FakeDbSet<T> + RakletDbFake + ControllerTestBase + MockHttpContextBuilder. Async test pattern documented. PaymentsController as canonical reference.
Top 5 by traffic: AccountController (1,251 LOC, 5-8d), EventsController (3-4d), ContactController (2-3d), SubscriptionController (2-3d), PaymentsController (done in phase 1).
Once phase 2 has 2+ weeks of data, CI check fails PRs that drop coverage by >0.5%. Forces phase 4 not to regress.
Next 25-50 highest-traffic controllers across Raklet.Api + Application. Same recipe: refactor for injection, write happy + auth + edge tests.
~149 service files at 2.6%. DB-and-external-API-bound (payment processors, email senders, Cosmos). Same patterns + interface stubs for HTTP clients.
Models methods, Raklet.WebFramework, Webjob.Common, Queues, utility libs, edge controllers. Diminishing returns kick in hard. Marginal value of test #30,000 ≪ test #5,000.
Raklet.Backend has 611 .ts files with 4 Jest test files. Smaller scope, separate stack. Can run in parallel with phase 1+ on a different engineer.
| Phase | Engineer-days | Calendar (1 eng) | Calendar (3 eng) | Coverage at end |
|---|---|---|---|---|
| 0. Honest baseline | 1 | 1d | 1d | ~3-5% |
| 1. Foundation | 3-5 | 1w | 3-5d | ~5-6% |
| 2. Tier 1 (top 5) | 15-25 | 3-5w | 1-2w | ~10-15% |
| 3. Trend gate | 1 | 1d | 1d | (mechanism) |
| 4. Tier 2 (~50 controllers) | 100-250 | 5-12mo | 2-4mo | ~25-40% |
| 5. Service layer | 50-150 | 3-8mo | 5-12w | +15-25% |
| 6. Long tail to 80% | 200-500 | 1-2y | 5-12mo | 80% |
| 7. TS backend (parallel) | 3-5 | 1w | — | separate stack |
Not external audit/sale/cert. Real driver is shipping confidence for AI-written PRs. Coverage % is downstream of this goal.
CEO challenged the "high-traffic controllers" claim — correctly. Names were picked by file size and pattern-matching, not real telemetry. Access incoming for App Insights, GA, DB query patterns; chunks 2-N re-rank when data lands.
PaymentsController (83 LOC, 1 method, thin orchestration over OrganisationMembershipService). Foundation scaffolding + first test pack in one PR, sized 5-7 days.
No prior data on codex/cursor writing tests against Raklet specifically. Therefore wedge PR is intentionally a learning experiment. Its "What we learned" section is the highest-value artifact of the whole initiative.
Old plan: optimize for line coverage %. Revised: chunk by hot-path priority. Coverage % stays as a tracked metric but isn't the optimization target. Source of priority: real traffic data, not file-size guessing.
One controller per chunk. Linear card → branch → PR → AI agent. Codified in test-coverage-task-template.md (this PR). AccountController (1,251 LOC) gets sub-chunked. Chunk 0 (Foundation + PaymentsController wedge) ships first, blocking all others until the canonical pattern is proven.
80% is optimized for greenfield codebases. Brownfield codebases of this vintage typically see high ROI at 40-60% with diminishing returns above that.
Ask: is 80% driven by a specific need (audit, sale, certification) or aspirational? The plan changes meaningfully.
1 engineer full-time = phases 0-3 in ~2 months, then 4 rolling 6-12 months. 3 engineers for 6 months = phases 0-5 done (40-60% coverage). Whole team for a quarter = feasible but costs product velocity.
Ask: what headcount can be carved out, and against what other priorities?
Phase 2's main cost is refactoring controllers from new RakletDb() inline to constructor injection. Yes-refactor makes future tests cheap but costs 5-10d per top-tier controller. No-refactor needs heavier per-test fixtures.
Ask: are we willing to touch production code paths for testability? Risk vs long-term cost.
Three positions: no (all handwritten), yes-with-review (3-5× throughput, review risk), yes-autonomous (highest throughput, "tests that always pass" risk). CEO's stated AI-adoption goals point toward yes — but as a deliberate choice.
Ask: which mode, and what's the sample-review discipline to catch slop?
The hard question. 18-30 engineer-months don't exist unless something else gets deprioritized. Office-hours should pin down what — features, ops, sales engineering, customer support engineering?
Ask: pick a specific roadmap item to push out by N months to fund this initiative.
test-coverage-task-template.md as the brief.dotnet-coverage artifact on Tests workflow run 26253865435.
No tests have been written. This is the plan, not the work.