# Write Unit Tests for a Gin Project

Yes. An agent runs the Gin project's coverage script, finds the functions it leaves uncovered, writes Go testing tests for them and opens a PR with the delta.

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

## What the agent does

The agent measures before it writes. gothinkster/golang-gin-realworld-example-app is three packages, `users`, `articles` and `common`, with Gin 1.10, GORM 2 on SQLite, `golang-jwt/jwt/v5` and `testify` in `go.mod` under `go 1.21`. Its readme already reports high coverage, 93.4% for `articles`, 99.5% for `users` and 85.7% for `common`, so the job is not a first suite but the functions the existing one skips. The agent runs `bash ./scripts/coverage.sh`, which writes an atomic-mode `coverage.out` across the packages, and then `go tool cover -func=coverage.out | sort -k3 -n` to list every function by percentage rather than every file, since a 90% package can still hide a function at 0%.

It then reads what the existing tests do so the new ones fit. `articles/unit_test.go` builds a router with `setupRouter()`, creates users and articles with `createTestUser` and `createArticleWithUser`, and opens the SQLite test database in `TestMain` through `common.TestDBInit()`, removing it with `common.TestDBFree`; `users/unit_test.go` has `newUserModel` and `userModelMocker`, and `common/test_helpers.go` offers `HeaderTokenMock`, `ExtractTokenFromHeader` and `VerifyTokenClaims`. The uncovered lines are mostly error branches and serializer paths: the `tx.Rollback()` returns in `FindManyArticle` and `GetArticleFeed` in `articles/models.go`, `ResponseWithPreloaded` in `articles/serializers.go`, the `fmt.Println` fallbacks in `common/database.go` when a path cannot be created, `GenToken`'s signing-error return in `common/utils.go`, and the `Param()` branch of `NewValidatorError` for tags like `min=8`.

The agent writes table-driven tests with `testing` and `testify/assert`, named for behaviour: `TestFindManyArticleRollsBackOnAssociationError`, `TestResponseWithPreloadedUsesGivenCounts`, `TestNewValidatorErrorFormatsParam`, `TestEnsureDirRejectsFilePath`. Each exercises the function through its inputs and the SQLite database the package already uses, not by reaching into GORM internals. Where a branch can only be hit by a closed or corrupted database, it opens a second `gorm.DB`, closes the underlying `sql.DB`, and calls the function with it, which is enough to reach the error return without a mock framework. Tests that need a user get one from the existing helpers so the shared test database stays consistent.

Before it pushes it runs `bash ./scripts/gofmt.sh`, `bash ./scripts/coverage.sh` again on Go 1.21, 1.22 and 1.23 as `.github/workflows/ci.yml` does, and `golangci-lint run` with the v1.61 configuration in `.golangci.yml` (`staticcheck`, `unused`, `misspell`), because a new test file that trips `unused` fails the Lint job. It does not change production code unless a test reveals a bug; if one does, the fix is a separate commit and the pull request says what it found.

## Start it with one API call

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

```json
{
  "agent": "codex",
  "input": "Run bash ./scripts/coverage.sh in this Gin project, then go tool cover -func=coverage.out, and list the functions with the lowest coverage across the users, articles and common packages. Write Go testing tests with testify for the uncovered branches that hold real logic, using the existing helpers (setupRouter, createTestUser, createArticleWithUser, common.TestDBInit and the token helpers in common/test_helpers.go) and the SQLite test database the packages already open in TestMain. Name tests for the behaviour they check, keep them table-driven where several inputs share a shape, and reach error branches through real inputs such as a closed database rather than by mocking GORM. Do not change production code unless a test reveals a bug; if it does, fix it in a separate commit and say so. Run scripts/gofmt.sh, scripts/coverage.sh on Go 1.21, 1.22 and 1.23, and golangci-lint run, then open a pull request with the per-function coverage before and after.",
  "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

A pull request on its own branch that adds test functions to `articles/unit_test.go`, `users/unit_test.go` and `common/unit_test.go`, or new `_test.go` files beside them, and leaves the production files unchanged. The description pastes the `go tool cover -func` lines for the functions it targeted, before and after, with the package totals, and lists the `gofmt`, `coverage.sh` and `golangci-lint` runs on each Go version.

If a test found a bug, such as `FindManyArticle` committing after a rollback, the description names it, the fix is in its own commit, and the test that found it stays. The session transcript keeps every command with its output, including the coverage listings, and `GET /o/{org}/sessions/{id}/messages` returns it.

A reviewer checks that the new tests assert behaviour and not SQL strings, that none of them leave rows that break the existing `TestArticleFeedCount` or `TestUserModel`, and that the error-branch tests close their own database handle rather than the shared `test_db`.

## Run it on every nightly

Create an automation in the Project with a `schedule` trigger, a `cron` such as `0 2 * * *` with your `timezone`, and a prompt asking for tests on the functions whose coverage fell since the last run. Each night opens one pull request on its own branch, and the coverage table in each description is the record of where the suite stands.

Set a webhook URL on the API key and Rush POSTs `run.completed` when the turn finishes, with the `session_url` and the result, so a handler can post the pull request link and the package totals into the channel where the team starts its day.

## Why run it on Rush?

Pushing a well-tested Go project from 90% to the last functions is fiddly work: the gaps are error returns and rollbacks that need a closed database or a bad path to reach, and nobody wants to spend an afternoon on them. The agent finds them from the coverage profile, reaches them with real inputs in its own sandbox, and runs the matrix the workflow runs.

The pull request shows the per-function coverage before and after and the transcript of every run, so the delta is something you can check line by line. Several Go services can each get a run at the same time, and a person still merges each one.

## Frequently asked questions

### The readme says coverage is already 90%. Is there anything left?

Package totals hide functions at 0%. `go tool cover -func` lists each function, and the gaps here are error branches: the rollback returns in `articles/models.go`, the fallback prints in `common/database.go`, `GenToken`'s signing failure and the `Param()` branch of `NewValidatorError`. Those are what the agent targets.

### testify or the standard library?

Both, as the project does: `testing` for structure and `testify/assert` for assertions, which `go.mod` already requires. The agent adds no new test framework or mocking library.

### Does it need CGO for SQLite?

Yes. `gorm.io/driver/sqlite` uses `mattn/go-sqlite3`, which needs a C compiler, and the agent installs one in its sandbox, so the tests run the same way they do in the `Test` job.

### Will it modify the Newman API tests?

No. `scripts/run-api-tests.sh` and the Postman collection are integration tests of the running server and out of scope for this run. The prompt is about Go unit tests in the three packages.
