# Write Tests for a Rocket Server

Yes. An agent adds unit tests for your Rocket server's pure helpers and local-client tests for its open routes, runs cargo test and opens a pull request.

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

## What the agent does

The agent reads how the server is built before it writes a test. In dani-garcia/vaultwarden the package is `vaultwarden` 1.0.0, edition 2024, `rust-version` 1.97.1, with `rust-toolchain.toml` pinning channel 1.99.0 with `rustfmt` and `clippy`. The `[features]` block has an empty `default`, so a build must pick `sqlite`, `mysql` or `postgresql`; `sqlite` also bundles `libsqlite3-sys`. There is no `[dev-dependencies]` section and no `tests/` directory, and because the crate is a binary with everything under `src/main.rs`, tests live in `#[cfg(test)] mod tests` blocks inside the modules they cover. Nine files have one today: `src/auth.rs` tests `client_ip_from_header_values` with several `X-Forwarded-For` shapes, `src/api/core/two_factor/email.rs` tests `obscure_email`, and `src/util.rs` has two IP range tests that are `#[ignore]` and behind the `unstable` feature.

That leaves two pure modules with no tests at all. `src/crypto.rs` wraps `ring`: `hash_password` and `verify_password_hash` do PBKDF2-HMAC-SHA256 with a caller-supplied iteration count, `hmac_sign` signs with HMAC-SHA1 and returns lowercase hex, `get_random_string_numeric` and `get_random_string_alphanum` draw from fixed alphabets, `generate_send_file_id` encodes 32 random bytes as hex, `generate_api_key` returns 30 alphanumeric characters, `ct_eq` is a constant-time compare and `sha256_hex` hashes to hex. In `src/util.rs`, `get_display_size` divides by 1024 up to PB and formats two decimals, `upcase_first` and `lcase_first` change one character, `format_date` prints RFC 3339 with microseconds and a `Z`, `validate_and_format_date` falls back to `1970-01-01T00:00:00.000000Z` on bad input, `format_datetime_http` prints RFC 2822 with `GMT`, `parse_date` reads RFC 3339, `is_valid_email` rejects addresses whose domain does not parse as a host, `convert_json_key_lcase_first` walks nested objects and arrays, and `parse_experimental_client_feature_flags` filters a comma list against `SUPPORTED_FEATURE_FLAGS`. The agent writes a test module in each file with a round trip or a known vector per function: a hash verified with the same salt and rejected with another, `get_display_size(1536)` and the PB boundary, the 1970 fallback, an email with a path in its domain.

For the routes it reads `src/main.rs`. `launch_rocket` builds `rocket::custom(config)` and mounts `api::web_routes()` at `/`, `api::core_routes()` at `/api`, then `/admin`, `/events`, `/identity`, `/icons` and `/notifications`, registers the catchers, manages the `DbPool` and attaches the `AppHeaders`, `Cors` and `BetterLogging` fairings. In `src/api/core/mod.rs` the `meta_routes` list holds `alive`, `now`, `version`, `config` and `get_api_webauthn`: `GET /api/now` returns the current time through `format_date`, `GET /api/version` returns `crate::VERSION`, and `GET /api/alive` takes a `DbConn` guard, which `src/db/mod.rs` resolves from the managed pool and answers 503 when it cannot. The two unauthenticated routes the prompt asks for are `/api/now` and `/api/version`: the agent builds a Rocket with `core_routes()` mounted at `/api`, uses `rocket::local::blocking::Client::tracked`, and asserts the status, the `Content-Type` and that the body parses as the date or the version string. `CONFIG` is a `LazyLock` that reads `.env` and `data/config.json`, so the test sets `DATA_FOLDER` to a temporary directory before touching it.

Then it runs what `.github/workflows/build.yml` runs, with `RUSTFLAGS=-Dwarnings`: `cargo test --profile ci --features sqlite`, `cargo clippy --profile ci --features sqlite,mysql,postgresql,enable_mimalloc,s3` and `cargo fmt --all -- --check` with the repository's `rustfmt.toml` (`max_width = 120`). The `[workspace.lints]` sections forbid `unsafe_code`, deny `warnings` and the `rust_2024_compatibility` group, and warn on the clippy `pedantic` and `style` groups, so a test with an unused import or a needless `clone` fails the build the same way production code would.

## Start it with one API call

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

```json
{
  "agent": "claude",
  "input": "Write tests for this Rocket server. Read Cargo.toml and rust-toolchain.toml first and build with the toolchain and the database feature they imply (in a server shaped like dani-garcia/vaultwarden, --features sqlite). The crate is a binary, so put unit tests in #[cfg(test)] mod tests blocks inside the modules they cover, in the style of the existing tests in src/auth.rs. Cover every public function in src/crypto.rs and the untested helpers in src/util.rs (display sizes, date formatting and parsing, email validation, JSON key conversion, feature flag parsing) with round trips and known vectors. Add Rocket local-client tests for two unauthenticated routes in src/api/core/mod.rs, GET /api/now and GET /api/version, by mounting core_routes() at /api on a test Rocket and asserting status, content type and body; set DATA_FOLDER to a temporary directory before anything reads CONFIG. Change no production code unless a test reveals a bug; if it does, fix it in its own commit and say so. Run cargo test --profile ci --features sqlite, cargo clippy --profile ci --features sqlite,mysql,postgresql,enable_mimalloc,s3 with RUSTFLAGS=-Dwarnings, and cargo fmt --all -- --check, and open a pull request listing each function now covered.",
  "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 a `mod tests` block at the end of `src/crypto.rs`, extends the one in `src/util.rs` with tests that run without the `unstable` feature, and adds a `mod tests` to `src/api/core/mod.rs` for the two meta routes. No `tests/` directory appears, `Cargo.toml` is unchanged because `rocket::local` needs no extra dependency, and `Cargo.lock` does not move.

The description lists each function with the cases it now has, quotes the `cargo test --profile ci --features sqlite` summary with the number of tests that ran, and the clippy and fmt results. If a test found a bug, a separate commit carries the fix and the description names it. The transcript keeps the full output of every command, including the first compile with the `sqlite` feature.

Before merging, a reviewer checks that the PBKDF2 vectors use a small iteration count so the suite stays fast, that the date tests pin the microsecond and `Z` format the clients depend on, and that the route tests mount only `core_routes()` rather than reaching for the full `launch_rocket`, which needs a database pool.

## Run it on every nightly

Create an automation in the Project with this prompt and a `schedule` trigger (`cron` `0 2 * * *` in your `timezone`). Each night's run looks at what changed and adds tests for the untested functions the diff touched; a night with nothing new to cover ends with no pull request and a one-line result saying so. Set `max_runs_per_hour` to 1 so a retried fire cannot start two runs on the same tree.

To start it from your own scheduler instead, send this body to `POST /o/{org}/sessions` with an `Idempotency-Key` built from the repository name and the date, and set a webhook URL on the API key: Rush then POSTs `run.completed` with `session_url` when the turn ends, and your handler can assign the pull request to whoever owns the module that gained tests.

## Why run it on Rush?

A Rocket server with no `default` feature and a hard `-Dwarnings` policy is slow to test by hand: the first build with `sqlite` and `argon2` at `opt-level 3` takes a while, and a stray warning in a test module fails the whole run. On Rush the agent takes that build in its own sandbox, iterates until clippy and fmt are clean under the repository's `[workspace.lints]`, and hands back a pull request that is already green on the same commands CI runs.

The pull request carries its own proof: the test list, the exact `cargo test` summary, and the clippy output in the transcript. A reviewer reads the vectors rather than trusting a coverage number, and the same request runs against every Rocket service you maintain in parallel.

## Frequently asked questions

### The crate has no lib.rs. Where do the tests go?

Inside the modules, as `#[cfg(test)] mod tests` blocks, the way `src/auth.rs` and `src/api/core/two_factor/email.rs` already do. A `tests/` directory cannot see a binary crate's private functions, so the agent does not create one unless you ask it to add a library target first.

### Which database feature does it build with?

The one the prompt names, `sqlite`, which bundles SQLite and needs no server. CI also tests `mysql` and `postgresql`, but the helper and meta-route tests do not touch the database, so one feature is enough to prove them; the description says which feature set ran.

### Does a local-client test need a running database?

Not for `/api/now` and `/api/version`, which take no `DbConn`. `/api/alive` does, and `src/db/mod.rs` answers 503 when the pool is missing, so the agent leaves it out of the first pass or tests the 503 explicitly if you ask.

### Will it run the Playwright suite under playwright/?

No. That suite starts MariaDB, MySQL and Postgres through Docker Compose and drives the web vault in Firefox, which is a different job. The pull request says the Rust suite ran and the Playwright suite did not.
