chore(sonar): wire JaCoCo coverage and clear actionable smells #26

Merged
whitlocktech merged 1 commits from chore/sonar-coverage-and-cleanup into main 2026-07-22 19:11:03 +00:00
Member

What & why

The SonarQube quality gate for Runic-Gateway-Android-app was failing on a single condition: new-code coverage = 0%. This was a reporting gap, not a testing gap — the 25-file JVM unit suite already exists, but sonarqube.yml runs a source-only scan that never received a JaCoCo report, so Sonar saw zero coverage. The project is otherwise clean (0 bugs / 0 vulnerabilities / 0 hotspots, Maintainability A).

Changes

Coverage wiring (the gate fix):

  • app/build.gradle.kts — apply the jacoco plugin, enableUnitTestCoverage on the debug build type, and a jacocoTestReport task (excludes generated/Hilt/Compose-singleton classes).
  • sonar-project.properties — consume the JaCoCo XML via sonar.coverage.jacoco.xmlReportPaths, and add sonar.coverage.exclusions for pure-@Composable UI (JVM unit tests can't execute composable bodies without Robolectric, so counting those lines would unfairly sink new-code coverage).
  • .gitea/workflows/sonarqube.yml — the scan now runs a real Gradle build first (JDK 17 + Android SDK, mirroring pr-checks.yml): ./gradlew testDebugUnitTest jacocoTestReport before the scan step.

Code smells fixed (the 3 that genuinely warranted it):

  • Remove an unused remember import — AdminContentScreen.kt.
  • Remove an unused page parameter (and its call-site argument) — AdminSupportScreen.RespondDialog.
  • Decompose LoginViewModel.submit() into validateForSubmit() + applyLoginResult() helpers — cognitive complexity 20 → under the 15 threshold.

Not touched (marked Won't Fix in SonarQube with rationale): the remaining 12 smells — 5 snake_case DTO fields that intentionally mirror the backend JSON wire contract, and 7 Compose/nav cognitive-complexity + hoisted-callback param-count smells that are idiomatic for Jetpack Compose.

Verification

Ran locally: ./gradlew testDebugUnitTest jacocoTestReportBUILD SUCCESSFUL, all unit tests pass, and the report is written to the path Sonar reads. Whether new-code coverage clears the 50% threshold will be confirmed by the post-merge scan (sonarqube.yml runs on push to main); if it falls short, targeted unit tests for untested new logic (ViewModels/mappers) are the follow-up.

Docs update in a companion PR: RunicGateway/docsdocs/android-sonar-coverage (adds PLAN.md §12.1).


[x] AI-assisted: this PR was authored with Claude Code (Claude Opus 4.8). AI-authored commits carry a Co-Authored-By trailer.

🤖 Generated with Claude Code

## What & why The SonarQube quality gate for `Runic-Gateway-Android-app` was failing on a single condition: **new-code coverage = 0%**. This was a *reporting* gap, not a testing gap — the 25-file JVM unit suite already exists, but `sonarqube.yml` runs a source-only scan that never received a JaCoCo report, so Sonar saw zero coverage. The project is otherwise clean (0 bugs / 0 vulnerabilities / 0 hotspots, Maintainability **A**). ## Changes **Coverage wiring (the gate fix):** - `app/build.gradle.kts` — apply the `jacoco` plugin, `enableUnitTestCoverage` on the debug build type, and a `jacocoTestReport` task (excludes generated/Hilt/Compose-singleton classes). - `sonar-project.properties` — consume the JaCoCo XML via `sonar.coverage.jacoco.xmlReportPaths`, and add `sonar.coverage.exclusions` for pure-`@Composable` UI (JVM unit tests can't execute composable bodies without Robolectric, so counting those lines would unfairly sink new-code coverage). - `.gitea/workflows/sonarqube.yml` — the scan now runs a real Gradle build first (JDK 17 + Android SDK, mirroring `pr-checks.yml`): `./gradlew testDebugUnitTest jacocoTestReport` before the scan step. **Code smells fixed (the 3 that genuinely warranted it):** - Remove an unused `remember` import — `AdminContentScreen.kt`. - Remove an unused `page` parameter (and its call-site argument) — `AdminSupportScreen.RespondDialog`. - Decompose `LoginViewModel.submit()` into `validateForSubmit()` + `applyLoginResult()` helpers — cognitive complexity 20 → under the 15 threshold. **Not touched (marked *Won't Fix* in SonarQube with rationale):** the remaining 12 smells — 5 snake_case DTO fields that intentionally mirror the backend JSON wire contract, and 7 Compose/nav cognitive-complexity + hoisted-callback param-count smells that are idiomatic for Jetpack Compose. ## Verification Ran locally: `./gradlew testDebugUnitTest jacocoTestReport` → **BUILD SUCCESSFUL**, all unit tests pass, and the report is written to the path Sonar reads. Whether new-code coverage clears the 50% threshold will be confirmed by the post-merge scan (`sonarqube.yml` runs on push to `main`); if it falls short, targeted unit tests for untested new *logic* (ViewModels/mappers) are the follow-up. Docs update in a companion PR: `RunicGateway/docs` → `docs/android-sonar-coverage` (adds PLAN.md §12.1). --- > [x] **AI-assisted:** this PR was authored with Claude Code (Claude Opus 4.8). AI-authored commits carry a `Co-Authored-By` trailer. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
wtclaude added 1 commit 2026-07-22 18:59:45 +00:00
chore(sonar): wire JaCoCo coverage and clear actionable smells
All checks were successful
PR Checks / android-build (pull_request) Successful in 10m44s
43215b49a0
Fix the SonarQube coverage gate (0% on new code) — a reporting gap, not a
testing gap: the JVM unit suite already exists but the source-only scan
never received a coverage report.

- app/build.gradle.kts: apply jacoco, enable debug unit-test coverage, add a
  jacocoTestReport task (excludes generated/Hilt/Compose-singleton classes)
- sonar-project.properties: consume the JaCoCo XML; exclude pure-@Composable
  UI from coverage (JVM unit tests can't execute composable bodies)
- .gitea/workflows/sonarqube.yml: run JDK 17 + Android SDK +
  `testDebugUnitTest jacocoTestReport` before the scan

Also clear the three actionable code smells: remove an unused import
(AdminContentScreen), remove an unused parameter (AdminSupportScreen.
RespondDialog), and decompose LoginViewModel.submit() (cognitive complexity
20 -> under 15). The remaining 12 smells (snake_case DTO fields that mirror
the JSON wire contract; Compose/nav complexity) are marked Won't Fix in
SonarQube with rationale.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NgyHnrNa8WwG3doxvxjuCr
whitlocktech approved these changes 2026-07-22 19:01:25 +00:00
whitlocktech scheduled this pull request to auto merge when all checks succeed 2026-07-22 19:01:30 +00:00
whitlocktech merged commit efe14d3828 into main 2026-07-22 19:11:03 +00:00
whitlocktech deleted branch chore/sonar-coverage-and-cleanup 2026-07-22 19:11:04 +00:00
Sign in to join this conversation.
No description provided.