All jobs

Write Tests for a Vapor Server

Yes. An agent writes VaporTesting route tests for your Swift server against an in-memory SQLite database, runs swift test on Linux and opens a PR.

Run this on RushIn a Project that has your repository.

What the agent does

The agent reads Package.swift first, because it decides which testing library a Vapor app already has. A server generated from vapor/template declares swift-tools-version:6.3, Vapor from: "4.121.4", Fluent 4.13.0 with one driver, and a test target that depends on the VaporTesting product. The template's Tests/AppTests/AppTests.swift uses Swift Testing rather than XCTest: a @Suite marked .serialized when Fluent is on, @Test functions and #expect, and a private withApp helper that makes an Application in .testing, calls configure, runs autoMigrate() and autoRevert() around each test, and shuts the app down. The agent keeps that style and that helper rather than adding XCTVapor beside it.

Then it maps the routes. routes.swift registers GET / ("It works!", or a Leaf index view when Leaf was chosen), GET /hello, and TodoController, whose boot groups todos with GET, POST and DELETE /todos/:todoID. The four generated tests cover the hello route, listing two seeded todos, creating one and deleting one. Nothing checks GET /, deleting an id that does not exist, deleting with a path segment that is not a UUID, posting a body with no title (the TodoDTO field is optional, while Todo.title is a required @Field), posting malformed JSON, or listing when the table is empty. Those become the new tests, each asserting the status and, where there is one, the decoded [TodoDTO] body.

For the database, configure.swift uses .sqlite(.file("db.sqlite")) for SQLite apps and reads DATABASE_HOST, DATABASE_NAME and the other variables for Postgres or MySQL. The prompt asks for in-memory SQLite, so the agent makes the smallest change that gives the tests that: configure picks .sqlite(.memory) when app.environment == .testing, and adds the fluent-sqlite-driver package to the test setup only if the app was generated for another database. Every test then starts from an empty, freshly migrated database that disappears with the process, and nothing writes a db.sqlite file into the working tree.

It runs swift test on Linux, using the Swift 6.3 toolchain the template's Dockerfile and CI pin (swift:6.3-noble). The upcoming features in swiftSettings, such as ExistentialAny and InternalImportsByDefault, make the compiler strict about how tests import and use types, so the agent compiles and fixes warnings in the tests before calling them done. A route that answers differently from what its code implies, such as a create with no title returning a server error instead of a 400, is kept as a test with the status the agent saw, marked with withKnownIssue, and described in the pull request.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Write tests with XCTVapor or VaporTesting for every route of this Vapor app's Todo API against an in-memory SQLite database, covering create, list, delete and invalid input. Read Package.swift and the existing tests first and use the same library, test style and app helper they use. In the test environment only, configure SQLite in memory; change nothing else in Sources/. Cover every route registered in routes.swift and the controllers, including an empty list, a missing or malformed body, an unknown id and an id that is not a UUID. Run swift test on Linux. If a route returns something its code does not intend, keep the test with the status you observed, mark it as a known issue, and explain it in the pull request. Open a pull request.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-vapor-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

The pull request adds the new @Test functions to the existing suite in Tests/AppTests/, or a second file in the same target when the first grows long, and a small change in configure.swift that selects in-memory SQLite under .testing. Package.swift changes only if a SQLite driver had to be added for tests. The description lists every route with the cases now covered, and quotes the swift test summary from the sandbox.

The session record keeps that swift test output in full, with each test's name and result from Swift Testing, so a reviewer can see the new tests compiled under the strict upcoming-feature settings and passed on Linux. If the repository has its own workflow it runs on the pull request as well. Any test marked with withKnownIssue appears in both places, with the response the agent recorded.

Before merging, a reviewer checks that the in-memory switch only applies under .testing and cannot change production configuration, that the known issues are real defects worth a follow-up, and that the tests read the response body through TodoDTO rather than string matching, so a renamed field breaks them.

Run it on every new route

Have a GitHub pull_request webhook send this request when a pull request changes routes.swift or a file under Sources/App/Controllers/ without touching Tests/, with the pull request URL in resources. The agent checks out that branch, writes the tests for the routes it adds, and works in its own session and sandbox.

A webhook URL on the API key receives run.completed or run.failed when the turn ends, with the session URL, so your server can post the result back on the original pull request.

The use case this job follows, with its automation and trigger: Write the missing tests.

Why run it on Rush?

Server-side Swift runs on Linux in production, and tests that only ever run in Xcode on a Mac can miss what differs there, from Foundation behaviour to how the strict concurrency checks treat a test. On Rush the agent builds with the Linux Swift toolchain in its own sandbox and runs swift test there, so the tests in the pull request are known to compile and pass on the platform the server ships on, and your Mac is not tied up by a cold Swift build.

Each swift build and swift test the agent ran is kept with its output in the session, so a known issue it reports can be checked against the response it actually saw. The same request can run on several Vapor services at once, each in its own sandbox, each with its own pull request.

Frequently asked questions

XCTVapor or VaporTesting?

Whichever your app already uses. Apps generated from the current vapor/template depend on VaporTesting and use Swift Testing's @Test and #expect; older apps use XCTVapor with XCTestCase and app.test(...). The agent reads Package.swift and the existing tests and writes the new ones the same way.

Do I need a Mac or Xcode?

No. The tests build and run with the Swift toolchain on Linux, the same one the template's Dockerfile uses, and that is where the agent runs them. They still run in Xcode on a Mac afterwards, since nothing in them is Linux-specific.

My app uses Postgres. Can the tests still use SQLite?

For simple models, yes, and that is what the prompt asks for. SQLite does not have every Postgres type or constraint, so if your migrations use something it lacks, the agent says so and asks in its answer whether to run those tests against a real Postgres in the sandbox instead.

Will it change my configure.swift?

Only to choose an in-memory SQLite database when app.environment is .testing. Every other environment keeps the configuration it had, and the diff shows the exact lines.