Skip to content

test(#39): add coverage thresholds + CI gate for web and api - #40

Merged
G0maa merged 1 commit into
mainfrom
test/GH-39-coverage-thresholds-ci-gate
May 27, 2026
Merged

test(#39): add coverage thresholds + CI gate for web and api#40
G0maa merged 1 commit into
mainfrom
test/GH-39-coverage-thresholds-ci-gate

Conversation

@G0maa

@G0maa G0maa commented May 27, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Both test suites now enforce a minimum coverage threshold — the test step (and therefore CI) fails when coverage drops below the floor. This closes the Test coverage signal: absent gap from the handover assessment: tooling was installed but nothing prevented coverage from eroding as the codebase grows.
  • web (apps/web/vitest.config.ts) — added a v8 coverage block scoped to hand-written app/** source (generated app/api/* and config files excluded so they don't dilute the number), and switched the test script to vitest run --coverage. Floors: lines/statements 88%, branches 90%, functions 60% (measured baseline ~90% lines / 100% branches).
  • api (apps/api test:run script) — enabled Node's built-in --experimental-test-coverage with --test-coverage-lines=80 --test-coverage-branches=80 --test-coverage-functions=90, excluding test files + src/test/**, source-mapped back to .ts via the existing --enable-source-maps. Measured baseline ~85% lines.
  • No new CI job — the gates ride the existing "Test API" / "Test Web" steps, so every PR is gated automatically.
  • Thresholds are ratchet floors set a few points below the baseline measured at introduction — so this PR is green, not red — documented in .claude/CLAUDE.md with the intent to raise (never lower) them over time.

Testing

  1. pnpm --filter web test → runs with coverage; passes at baseline (90.42% lines). Confirmed it fails when a floor is raised above actual (--coverage.thresholds.lines=99 → "Coverage for lines (90.42%) does not meet global threshold (99%)").
  2. pnpm --filter api test → full clean→build→setup→run pipeline against Postgres; passes at baseline (85.51% lines). Confirmed it fails with --test-coverage-lines=99 → "85.51% line coverage does not meet threshold of 99%".
  3. pnpm format:check, pnpm lint, pnpm --filter web typecheck all green.

Closes #39

Glossary

Term Definition
Coverage threshold The minimum percentage of code that must be exercised by tests; the test run exits non-zero (failing CI) below it.
Ratchet floor A threshold set at current coverage as a regression guard, raised incrementally over time — avoids a disruptive day-one failure on a young test suite while still catching backsliding.
v8 provider Vitest's native coverage backend using V8's built-in instrumentation (no Babel/istanbul transform).
--experimental-test-coverage Node's built-in test-runner coverage flag; paired with --test-coverage-{lines,branches,functions} it enforces thresholds and exits non-zero below them.
Source-mapped coverage With --enable-source-maps, coverage measured on compiled dist/ output is reported against the original .ts source lines.

Summary by CodeRabbit

  • Documentation

    • Added CI test coverage gates documentation outlining minimum coverage thresholds and enforcement policies.
  • Tests

    • Enabled test coverage reporting and enforcement for both web and API test suites.
    • Configured coverage thresholds for lines, branches, functions, and statements to maintain code quality standards.
    • Coverage gates are implemented as ratchet floors to prevent regression.

Review Change Stack

Establishes a ratchet coverage floor on both apps so coverage can't
silently erode as the codebase grows (issue #39).

- web: add v8 coverage block to vitest.config.ts scoped to app/** source
  (excludes generated app/api/* + config files); test script runs with
  --coverage. Floors: lines/statements 88%, branches 90%, functions 60%
  (baseline ~90% lines).
- api: enable Node's --experimental-test-coverage on test:run with
  --test-coverage-{lines=80,branches=80,functions=90}; excludes test
  files + src/test/**, source-mapped to .ts (baseline ~85% lines).
- Gates ride the existing CI test steps — no new job.
- Document thresholds + ratchet-upward intent in .claude/CLAUDE.md.

Closes #39
@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f16231c5-cf24-44ee-849d-4c77ffaf9bbd

📥 Commits

Reviewing files that changed from the base of the PR and between 27d4df0 and 8728904.

📒 Files selected for processing (4)
  • .claude/CLAUDE.md
  • apps/api/package.json
  • apps/web/package.json
  • apps/web/vitest.config.ts

📝 Walkthrough

Walkthrough

This PR adds test coverage enforcement to web and api applications by configuring coverage thresholds and updating test scripts to fail builds when coverage drops below baseline. Both apps now measure and gate test coverage using their respective test runners (Vitest v8 for web, Node experimental coverage for api), with measured baselines set as ratchet floors.

Changes

Test Coverage Gates

Layer / File(s) Summary
Coverage enforcement documentation
.claude/CLAUDE.md
Introduces CI test coverage gates section documenting web (Vitest v8 with exclusions/thresholds) and api (Node experimental coverage) enforcement and stating that thresholds are ratchet floors to be raised as coverage improves, never lowered.
Web coverage configuration and enforcement
apps/web/vitest.config.ts, apps/web/package.json
Vitest config adds v8 provider, includes handwritten app/**/*.{ts,vue} code while excluding app/api/** and app/**/__tests__/**, sets thresholds for lines/functions/branches/statements, and the test script now runs with --coverage enabled.
API coverage enforcement script
apps/api/package.json
The test:run script adds Node's --experimental-test-coverage with --test-coverage-{lines,branches,functions} threshold flags, specifies .env.test, and excludes src/test/** and **/*.test.ts from coverage scope.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 Coverage gates now stand so tall,
A ratchet floor we'll heed the call!
No sliding back, just climbing higher,
Tests measured up, our true desire.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'test(#39): add coverage thresholds + CI gate for web and api' directly and clearly summarizes the main change—adding test coverage thresholds and CI enforcement for both web and api applications.
Linked Issues check ✅ Passed All acceptance criteria from issue #39 are met: coverage thresholds configured for web (Vitest v8 with lines/branches/functions thresholds) and api (Node --experimental-test-coverage), CI gates enforced via existing test steps, and ratchet-floor intent documented in .claude/CLAUDE.md.
Out of Scope Changes check ✅ Passed All changes are directly scoped to issue #39 requirements: vitest.config.ts coverage setup, package.json test script updates, and documentation—no unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/GH-39-coverage-thresholds-ci-gate

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/web/vitest.config.ts

ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox.


Comment @coderabbitai help to get the list of available commands and usage tips.

@G0maa G0maa left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review: PR #40

Commit: 8728904e38fc0ede44dc74e4931a8a8789e8f05c

Note: GitHub blocks self-approval, so this is posted as a COMMENT review. Code-review verdict is APPROVED (see below); the approval marker has been written for the merge gate.

Summary

Adds ratchet-floor test-coverage gates to both monorepo apps (web via Vitest v8 provider, api via Node's built-in --experimental-test-coverage), riding the existing CI test steps rather than adding a new job. Closes #39. Pure test-infra config — no runtime code.

Checklist Results

  • Architecture & Design: Pass (no runtime/domain code touched)
  • Code Quality: Pass
  • Testing: Pass (this PR is the test-infra change; manual fail-verification documented in body)
  • Security: Pass (no auth/secrets/input surface)
  • Performance: Pass (gates ride existing steps; no extra CI job)
  • PR Description & Glossary: Pass (Glossary present, 5 well-defined terms)
  • Summary Bullet Narrative: Pass (every bullet states what + why)
  • Technical Decisions (AgDR): N/A (test-infra config; no library/architecture decision — using each app's native coverage tooling, not introducing a new one)
  • Adopter Handbooks: N/A (no handbooks loaded for this diff)

Coverage-of-coverage: N/A — this is the coverage configuration itself.

Issues Found

None blocking.

Verification performed

  • web exclude globs are correct. include: ['app/**/*.{ts,vue}'] + exclude: ['app/api/**', 'app/**/__tests__/**'] correctly excludes the committed-but-generated API client (app/api/{index,types.gen,zod.gen}.ts) and test dirs, while keeping hand-written source (app.vue, pages/, components/, composables/, plugins/). No real source is accidentally excluded. app.config.ts / app.vue stay in scope — correct, they're hand-written and already in the 90.4% baseline.
  • api exclude globs are correct. --test-coverage-exclude="**/*.test.ts" is necessary in addition to src/test/** because test files live colocated under src/modules/** and src/entrypoints/**, not only under src/test/. Both globs are doing real work.
  • Ratchet floors are sensible. All floors sit just below measured baseline (web: lines 88 < 90.4, branches 90 < 100, functions 60 < 66.7; api: lines/branches 80, functions 90 vs ~85% lines baseline) so the PR is green while still catching regression. The conservative web functions floor (60) is appropriate for a young suite with few functions where a single uncovered function swings the percentage hard.
  • Documentation is accurate. .claude/CLAUDE.md gate description matches the actual config values and the never-lower ratchet intent is stated clearly.

Suggestions (non-blocking)

  • Consider recording the measured api function-coverage % alongside web's baseline, so a future maintainer raising the api functions floor (currently 90) has the same reference point web has.

CI status

  • ci check: IN_PROGRESS at review time (not blocking this verdict — note it must go green before merge per the no-red-CI rule).
  • CodeRabbit: PENDING.

Verdict

APPROVED (code-review axis)

Floors are well-chosen, exclude globs verified against the actual tree, docs accurate. Merge remains gated on CI going green + explicit per-PR CEO approval.


🤖 Reviewed by Rex (Code Reviewer Agent)
📌 Reviewed commit: 8728904e38fc0ede44dc74e4931a8a8789e8f05c

@G0maa
G0maa merged commit e4e1074 into main May 27, 2026
3 checks passed
@G0maa
G0maa deleted the test/GH-39-coverage-thresholds-ci-gate branch May 27, 2026 15:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Testing] Add test coverage thresholds + CI gate (web + api)

1 participant