# Find Test Coverage Gaps in a Gin Service

Yes. An agent runs your Gin service's tests under coverage in its sandbox and ranks the untested handlers, with the test that would cover each, changing nothing.

[Run this on Rush](https://cloud.getrush.ai/run/test-coverage-gaps/gin/), in a Project that has your repository.

## What the agent does

The agent clones the service into its own cloud sandbox and runs coverage the way the repository already does, so its numbers match what the team sees. In a Gin API like gothinkster/golang-gin-realworld-example-app, `scripts/coverage.sh` loops over every directory under `./*` that holds Go files, runs `go test -coverprofile=profile.out -covermode=atomic` in each, and appends the lines to one `coverage.out`; the `test` job in `.github/workflows/ci.yml` runs that script on Go 1.21, 1.22 and 1.23 and uploads the 1.23 result to Coveralls. `readme.md` quotes the result as roughly 90 percent in total, with `users` at 99.5 percent, `articles` in the low nineties and `common` at 85.7 percent. The agent runs the script, then `go tool cover -func=coverage.out`, which lists every function with its own percentage, and that list, not the package totals, is the raw material of the report.

The first finding comes from the script itself. `find ./* -maxdepth 10 -type d` lists directories, so the repository root is never visited and `hello.go`, package `main`, is never measured: `Migrate`, which calls `AutoMigrate` for every model, and `main`, which builds the router, order the middleware and read `PORT`, do not appear in `coverage.out` at all. The report says so and distinguishes the two kinds of gap: code that is measured and uncovered, and code that is not measured. The agent also notes that `api/Conduit.postman_collection.json`, run by `scripts/run-api-tests.sh` with newman in the `api-test` job, exercises the real binary end to end but produces no coverage data, so an endpoint can be exercised by Postman and still show as uncovered.

Then it reads the uncovered functions against the tests that exist. `articles/unit_test.go` has 41 test functions, `common/unit_test.go` has 16 and `users/unit_test.go` has 6; they build a router with `setupRouter`, which mirrors the groups and the two `AuthMiddleware` calls from `hello.go`, open a SQLite database through `common.TestDBInit` at `TEST_DB_PATH`, create users with `createTestUser` and a bcrypt hash, and send requests through `httptest` with `common.HeaderTokenMock` for the token header. For each function under its package's figure the agent reads the branches the tests skip: the `database` error paths in `articles/routers.go` that return 422 from `common.NewError`, the `access_token` query parameter branch in `users/middlewares.go` that `extractToken` supports but the header-only tests never take, and whichever of `ProfileFollow`, `ProfileUnfollow`, `ArticleFavorite`, `ArticleUnfavorite` and `ArticleCommentDelete` the function list shows below full coverage.

The ranking weighs how reachable the gap is from the outside against how much the function does. An unauthenticated branch of a handler ranks above an unreachable error path in a serializer, and a handler that writes to the database ranks above one that reads. Every entry names the function, its file and the percentage from `go tool cover -func`, the branch or condition the tests miss, and the test that would cover it written in the file's own style: a `setupRouter` request through `httptest`, a `createTestUser` fixture, a token from `HeaderTokenMock` and the `assert` calls from testify. The agent changes no file, pushes nothing and opens no pull request; the report is the result.

## Start it with one API call

`POST https://api.prix.dev/o/{org}/sessions`

```json
{
  "agent": "claude",
  "input": "Find the test coverage gaps in this Gin service and write coverage-gaps.md. Run bash scripts/coverage.sh exactly as .github/workflows/ci.yml does, then go tool cover -func=coverage.out, and keep the full function list. Compare the package totals with the figures readme.md quotes. Check which directories scripts/coverage.sh visits and report any Go code it never measures, such as package main in hello.go. Read articles/unit_test.go, users/unit_test.go and common/unit_test.go to learn the test style, including setupRouter, TestDBInit, createTestUser and HeaderTokenMock, and read the handlers in articles/routers.go and users/routers.go and the middleware in users/middlewares.go to see which branches the tests skip. Rank the ten riskiest uncovered functions, putting handler branches reachable over HTTP first, and for each give the function, file, percentage, the missed branch and the test that would cover it in the existing style. Add a section on what api/Conduit.postman_collection.json exercises that coverage does not count. Do not change any file, commit, push or open a pull request.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app"
    }
  ]
}
```

1. **Get an API key.** In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
2. **Send the request.** POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
3. **Read what came back.** Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

## What comes back

What comes back is one file, `coverage-gaps.md`, as an artifact on the session, with a download link that works for an hour and is refreshed on each read of the session. It opens with the package totals from the run beside the figures `readme.md` quotes, and the full output of `go tool cover -func` as an appendix so every number in the report can be traced to a line. Then it lists the code the coverage script never measures, with the `find` expression from `scripts/coverage.sh` that explains why.

The body is the ranked list: for each of the ten riskiest gaps, the function, its file and percentage, the branch the suite skips in a sentence, and a test sketch in the repository's own style, so the next step is a copy into the right `unit_test.go` rather than a redesign. A closing section maps the Postman collection's requests to endpoints and marks the ones that reach code the unit tests do not, which is where an end-to-end check is standing in for a unit test.

A reviewer reads the ranking first and the appendix second, checks that the ranking is about reachability rather than only the percentage, and decides which sketches to turn into tests. A follow-up on the same session that asks for the top three as a pull request picks up the same clone with `coverage.out` already written.

## Run it on every release tag

Coverage gaps matter most just before a release, when the team decides what to test by hand. No automation trigger fires on a tag push, so the release script on your server starts the run: it sends this request with `resources` set to `https://github.com/your-org/your-app/tree/<tag>` and a `request_id` made from the tag, so a retried call returns the run already started, and the report describes exactly what ships.

Give the key a webhook URL and Rush POSTs `run.completed` with `data.session_url` when the turn ends; a handler reads the session, takes the `coverage-gaps.md` link from its artifacts and attaches it to the release.

## Why run it on Rush?

The function-level list behind a coverage badge takes a Go toolchain, a writable path for the SQLite test database and a run of the whole suite, and reading it well takes the handlers and the tests side by side. The agent does that in its own sandbox from a fresh clone, so the report reflects the branch as pushed rather than a laptop with a stale `coverage.out`, and nothing it runs touches a shared database.

Every number is traceable. The transcript at `GET /o/{org}/sessions/{id}/messages` holds the coverage script's output and the `go tool cover -func` list, and the report cites them. Several services, or several tags of one, can be measured at once in separate sandboxes.

## Frequently asked questions

### Does it change any code or add tests?

No. This run writes the report only. Each gap comes with a test sketch in the style of the existing `unit_test.go` files, and a follow-up on the same session can turn the top entries into a pull request with the tests written and run.

### Why does the report mention hello.go when the badge says 90 percent?

Because `scripts/coverage.sh` only visits directories under `./*`, so package `main` at the repository root is never run under coverage and its `Migrate` and `main` functions are absent from `coverage.out`. The badge is honest about what it measures; the report says what it does not.

### Does the Postman collection count as coverage?

Not in the numbers. `scripts/run-api-tests.sh` builds the binary and runs the collection against it with newman, which exercises the real routes but writes no coverage profile. The report maps those requests to endpoints so the team can see which handlers are tested only end to end.

### What makes one gap riskier than another?

Reachability and effect. A handler branch a client can hit over HTTP ranks above an internal error path, and a branch that writes, such as a follow or a comment delete, ranks above one that reads. The percentage from `go tool cover -func` is shown but is not the sort key on its own.
