# Write RSpec Tests for a Rails App

Yes. An agent runs the suite under SimpleCov, finds the controllers with the thinnest coverage, writes request specs with your factories and opens a pull request.

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

## What the agent does

The agent begins with the test setup as it is. In a Rails app like lobsters/lobsters it reads `.ruby-version` (Ruby 4.0.0), the `Gemfile` (an unpinned `rails`, `sqlite3` for every environment, `solid_queue` and `solid_cache`, and in the test group `rspec-rails`, `factory_bot_rails`, `database_cleaner`, `capybara`, `simplecov` with `require: false`, `standard`, `brakeman`, `vcr` and `webmock`), `.rspec` (`--require spec_helper`) and `spec/rails_helper.rb`, which requires SimpleCov, calls `SimpleCov.start "rails"`, loads `spec/support/**/*.rb`, and in `before(:suite)` creates a System user and a `category1` category with the tags `placeholder`, `tag1` and `tag2` that every spec can rely on. `config/database.yml.sample` defines `primary`, `cache`, `queue` and `rack_attack` SQLite databases per environment, and `.github/workflows/check.yml` shows the exact test sequence CI uses: copy `.github/workflows/check/*` into `config/`, copy the database sample into place, `./bin/rails db:schema:load db:migrate`, then `bundle exec rspec`, with `libvips` installed first.

It runs that same sequence in the sandbox so `coverage/` is written (the directory is gitignored), then reads `coverage/.last_run.json` and the per-file table in `coverage/index.html` to rank `app/controllers/`. It also compares the controller list against `spec/requests/` and `spec/controllers/` directly: `stories`, `comments`, `home`, `settings`, `login`, `signup`, `messages`, `tags` and the `mod/` controllers have request specs, while `hats_controller.rb` (index, doff, doff_by_user, edit, update_in_place, update_by_recreating), `invitations_controller.rb` (build, index, confirm_email, create, create_by_request, send_for_request, delete_request), `filters_controller.rb`, `suggestions_controller.rb`, `categories_controller.rb` and `avatars_controller.rb` have none, even though some of them have a feature spec under `spec/features/` that drives the browser instead of the request layer.

For each chosen controller the agent writes one file in `spec/requests/`, in the style of `spec/requests/tags_spec.rb`: `describe "...", type: :request`, `let(:user) { create(:user, :admin) }`, `sign_in user` from `AuthenticationHelper::RequestHelper` (which posts to `/login`), then plain `get` and `post` calls against the real paths from `config/routes.rb` (`/invitations/request`, `/invitations/confirm/:code`, `/filters`, `/categories/new`, `/hats/:id/edit`) with expectations on the redirect, the flash and the rows written. Factories already exist for `hat`, `invitation`, `tag`, `story`, `comment` and `user` with traits such as `:banned` and `:noinvite`, so the specs create data through them rather than through fixtures. Where a controller branches on `@user.can_invite?` or `require_logged_in_moderator`, the spec covers both the allowed and the refused path, because that is where the untested code is.

It re-runs `bundle exec rspec`, then `bundle exec standardrb` on the new files since CI's lint job runs it, and records the coverage of each controller before and after from SimpleCov's output. Production code under `app/` is left unchanged; the README says testing is deliberately lighter for moderator features, so the pull request description says which controllers it chose and which it left alone and why.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "Set up the test database the way .github/workflows/check.yml does and run the suite with SimpleCov. From the coverage report and the spec directory, find the controllers with the lowest coverage, and write request specs for their actions in spec/requests using the existing factories, the sign_in helper and the real routes. Cover the refused paths as well as the happy ones. Run bundle exec rspec and standardrb, and open a pull request with the coverage before and after for each controller. Do not change production code.",
  "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 a pull request on a new branch that adds request specs under `spec/requests/`, for example `invitations_spec.rb`, `hats_spec.rb`, `filters_spec.rb`, `suggestions_spec.rb` and `categories_spec.rb`, each using `create(:user, ...)`, `sign_in` and the routes as written in `config/routes.rb`. No file under `app/`, `config/` or `db/` changes, and the Gemfile is untouched because SimpleCov and FactoryBot were already there.

The description holds a small table of the controllers it covered with their SimpleCov line coverage before and after, the `bundle exec rspec` summary line, and the `standardrb` result. The trajectory keeps the full output of the schema load, the first coverage run and the final run, so a reviewer can check that the new specs ran against the same database setup CI uses.

Before merging, a reviewer checks that the specs assert on behaviour (a redirect to `/settings` with a flash when a user cannot invite, an `InvitationRequest` marked verified after `/invitations/confirm/:code`) rather than on template text, that nothing relies on ordering between specs given `DatabaseCleaner` runs with the transaction strategy, and that moderator paths use a user with the right flags rather than stubbing `require_logged_in_moderator`.

## Run it on every Monday morning

Create an automation in the Project with this prompt and a `schedule` trigger (`cron` `0 7 * * 1` in your `timezone`). Each week the run re-measures coverage and takes the next lowest controllers, so the suite grows a few request specs at a time and each pull request stays small enough to read before standup.

Give the API key a webhook URL so `run.completed` arrives with the `session_url`; a handler on your side can post the pull request link to the channel where tests are reviewed. A run whose new specs do not pass ends without a pull request and says why in its result.

## Why run it on Rush?

A Rails suite with four SQLite databases, a credentials workaround and a libvips dependency takes real setup before the first spec runs. On Rush the agent does that setup in a sandbox from the CI workflow itself, keeps the schema load and the SimpleCov output in the record, and nothing is installed on a laptop or left behind in a shared test database.

Coverage work is repetitive and easy to put off, and it is exactly the kind of job that runs well in parallel: one sandbox per application, each returning a pull request whose description already carries the before and after numbers. The reviewer reads specs, not setup logs.

## Frequently asked questions

### Request specs or controller specs?

Request specs, because that is what the repository writes for new coverage (`spec/requests/` has over twenty files, `spec/controllers/` six older ones) and because they exercise routing, before_actions and the session the way a browser does. The agent follows the existing style in `tags_spec.rb` and `stories_spec.rb`.

### Does it need MySQL or Postgres?

No. This application runs its tests on SQLite as `config/database.yml.sample` describes, with separate files for the primary, cache, queue and rack_attack databases. The agent copies the sample into place exactly as the CI workflow does.

### Will it add gems?

Not for this job. SimpleCov, RSpec, FactoryBot and DatabaseCleaner are already in the Gemfile's test group. If a repository lacked SimpleCov the agent would add it to the test group and note the change, but here the Gemfile and lockfile stay as they are.

### What about the feature specs that already cover hats and filters?

They stay. A Capybara feature spec drives the page; a request spec checks the controller's branches, redirects and database effects directly and runs faster. The pull request description says which controllers already had feature coverage so a reviewer can judge the overlap.

### Does it run brakeman and database_consistency too?

It runs `standardrb` because the new files must pass the lint job. Brakeman and database_consistency check `app/` and the schema, which this job does not change, so the agent leaves them to CI and says so.
