CI was reporting coverage = 0.0% on SonarQube despite a 1180-test suite that already exercises the production code. Cause: the Gitea Actions workflow ran dotnet build and sonarscanner begin/end but never dotnet test, and the scanner was never told where to find coverage reports.
scan-sonar.ps1 (the local equivalent) has had the right flags + test step the whole time — this aligns CI with it.
Changes
.gitea/workflows/sonarqube.yml:
sonarscanner begin now passes:
/d:sonar.cs.opencover.reportsPaths="tests/**/TestResults/**/coverage.opencover.xml" — picks up the per-project OpenCover reports coverlet emits
/d:sonar.coverage.exclusions="tests/**,**/*.Tests/**" — keeps test code out of the coverage denominator
Step 8 — the dormant if: false curl-debug block (from an earlier auth-troubleshooting session) is replaced with the actual test run: dotnet test ... --no-build --settings coverlet.runsettings --collect "XPlat Code Coverage".
Step 9 (sonarscanner end) unchanged — now has real coverage data to upload.
Why this matters
Until this lands we don't know what the real coverage is. The first scan after merge will surface the actual percentage, after which we can pick which files genuinely deserve test investment instead of writing tests against the existing 0%-shaped void.
Test plan
CI run completes successfully (test step adds ~1-2 min to the workflow)
After merge, query /api/measures/component?component=sql-utilities&metricKeys=coverage,line_coverage,branch_coverage and confirm coverage is no longer 0.0
## Summary
CI was reporting coverage = **0.0%** on SonarQube despite a 1180-test suite that already exercises the production code. Cause: the Gitea Actions workflow ran `dotnet build` and `sonarscanner begin/end` but **never `dotnet test`**, and the scanner was never told where to find coverage reports.
`scan-sonar.ps1` (the local equivalent) has had the right flags + test step the whole time — this aligns CI with it.
## Changes
`.gitea/workflows/sonarqube.yml`:
1. **`sonarscanner begin`** now passes:
- `/d:sonar.cs.opencover.reportsPaths="tests/**/TestResults/**/coverage.opencover.xml"` — picks up the per-project OpenCover reports coverlet emits
- `/d:sonar.coverage.exclusions="tests/**,**/*.Tests/**"` — keeps test code out of the coverage denominator
2. **Step 8** — the dormant `if: false` curl-debug block (from an earlier auth-troubleshooting session) is replaced with the actual test run: `dotnet test ... --no-build --settings coverlet.runsettings --collect "XPlat Code Coverage"`.
3. **Step 9** (`sonarscanner end`) unchanged — now has real coverage data to upload.
## Why this matters
Until this lands we **don't know what the real coverage is**. The first scan after merge will surface the actual percentage, after which we can pick which files genuinely deserve test investment instead of writing tests against the existing 0%-shaped void.
## Test plan
- [ ] CI run completes successfully (test step adds ~1-2 min to the workflow)
- [ ] After merge, query `/api/measures/component?component=sql-utilities&metricKeys=coverage,line_coverage,branch_coverage` and confirm `coverage` is no longer 0.0
The Gitea Actions sonarqube workflow was running `dotnet build` and
sonarscanner begin/end without ever invoking `dotnet test`, so Sonar
was reporting coverage = 0% on the server despite a 1180-test suite
already exercising the code locally. `scan-sonar.ps1` (the local
equivalent) has had the coverage flags + test step the whole time —
this aligns CI with it.
Changes:
- `sonarscanner begin` now passes
`/d:sonar.cs.opencover.reportsPaths="tests/**/TestResults/**/coverage.opencover.xml"`
so the scanner picks up the per-project OpenCover reports coverlet
emits, and
`/d:sonar.coverage.exclusions="tests/**,**/*.Tests/**"` so the tests
themselves don't count toward the coverage denominator.
- The dormant step 8 (a disabled `if: false` curl-debug block from an
earlier auth-troubleshooting session) is replaced with the actual
test run: `dotnet test ... --no-build --settings
coverlet.runsettings --collect "XPlat Code Coverage"`.
- Step 9 (`sonarscanner end`) unchanged — it now has coverage data
available to upload.
Once this lands, the next scan will surface the *real* coverage number
for sql-utilities. Until we see that number we don't know which files
actually need test investment vs. were already covered by the existing
suite.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
CI was reporting coverage = 0.0% on SonarQube despite a 1180-test suite that already exercises the production code. Cause: the Gitea Actions workflow ran
dotnet buildandsonarscanner begin/endbut neverdotnet test, and the scanner was never told where to find coverage reports.scan-sonar.ps1(the local equivalent) has had the right flags + test step the whole time — this aligns CI with it.Changes
.gitea/workflows/sonarqube.yml:sonarscanner beginnow passes:/d:sonar.cs.opencover.reportsPaths="tests/**/TestResults/**/coverage.opencover.xml"— picks up the per-project OpenCover reports coverlet emits/d:sonar.coverage.exclusions="tests/**,**/*.Tests/**"— keeps test code out of the coverage denominatorif: falsecurl-debug block (from an earlier auth-troubleshooting session) is replaced with the actual test run:dotnet test ... --no-build --settings coverlet.runsettings --collect "XPlat Code Coverage".sonarscanner end) unchanged — now has real coverage data to upload.Why this matters
Until this lands we don't know what the real coverage is. The first scan after merge will surface the actual percentage, after which we can pick which files genuinely deserve test investment instead of writing tests against the existing 0%-shaped void.
Test plan
/api/measures/component?component=sql-utilities&metricKeys=coverage,line_coverage,branch_coverageand confirmcoverageis no longer 0.0