All jobs

Write Property-Based Tests for a Rust Crate

Yes. An agent reads your crate's public API and tests, writes proptest properties against a simple model, runs them under every CI feature set, and opens a PR.

Run this on RushIn a Project that has your repository.

What the agent does

The agent reads the crate and its CI before writing a property, because a property test that breaks the MSRV job or the no_std build is a net loss. In a crate shaped like tokio-rs/bytes, Cargo.toml declares version 1.12.1 on edition 2021 with rust-version = "1.57", features std on by default, an optional serde dependency and an optional extra-platforms dependency on portable-atomic; the only dev-dependency is serde_test, and loom is a dev-dependency under cfg(loom) only. clippy.toml repeats msrv = "1.57". .github/workflows/ci.yml runs cargo fmt --all --check, a minrust job with cargo hack check --feature-powerset --optional-deps --rust-version, ci/test-stable.sh, which runs cargo hack test --each-feature --optional-deps and cargo test --all-features, cross-target tests, no_std builds for thumbv6m and thumbv7m with --no-dev-deps, and nightly jobs for ci/miri.sh, ci/tsan.sh, ci/panic-abort.sh and loom with RUSTFLAGS="--cfg loom" cargo test --lib.

Then it reads what the existing tests already pin down, so the properties cover the gaps rather than restate examples. tests/test_bytes.rs has example-based tests for slice, split_off, split_to, truncate, the freeze_after_* family, reserve_* growth and reuse, extend_from_within, advance_* and the bytes_mut_unsplit_* cases; tests/test_buf.rs, test_buf_mut.rs, test_chain.rs, test_take.rs, test_limit.rs, test_reader.rs and test_iter.rs cover the Buf and BufMut adaptors; test_bytes_odd_alloc.rs and test_bytes_vec_alloc.rs swap the global allocator. Every one of them fixes specific lengths and offsets. The public surface in src/bytes.rs and src/bytes_mut.rs is where the invariants live: Bytes::slice, slice_ref, split_off, split_to, truncate, try_into_mut, and BytesMut::split, split_to, split_off, unsplit, try_unsplit, reserve, try_reclaim, extend_from_slice, extend_from_within, resize, zeroed and freeze.

The properties are written against a Vec<u8> model in a new tests/test_properties.rs, with proptest added as a dev-dependency. For random bytes and an index up to the length: split_off(at) followed by concatenation equals the original and the lengths add up; split_to the same from the other side; slice(a..b) equals the model's &data[a..b] for every valid range form, and slice_ref of that subslice equals slice(a..b); truncate(n) leaves the first min(n, len) bytes; BytesMut::split_to then unsplit restores the original bytes; a random sequence of extend_from_slice, extend_from_within, resize, truncate, split_to and advance applied to both a BytesMut and the model leaves them equal at every step, and freeze at the end equals the model too. For the adaptors, a.chain(b) read to the end equals the two inputs concatenated, take(n) yields exactly min(n, remaining) bytes, and copy_to_bytes of any prefix matches the model. Panicking cases, such as split_off past the length, get should_panic properties on indices above len.

The agent then runs the properties the way CI will see them. It runs cargo test --all-features and cargo hack test --each-feature --optional-deps, so the new file compiles with and without std and serde, and gates it with #![cfg(feature = "std")] if a property needs the Vec model under std only. It confirms the minrust job still resolves by running cargo hack check --feature-powerset --optional-deps --rust-version with the new dev-dependency, and pins proptest to an older release if the current one will not resolve on 1.57. It marks the file #[cfg(not(miri))] so ci/miri.sh does not spend its budget on random inputs, notes that loom runs --lib and never sees integration tests, and runs cargo fmt --all --check. It sets PROPTEST_CASES low in CI through a proptest! config with a comment on how to raise it locally, and opens the pull request with the test output and any shrunken counterexample it found.

Start it with one API call

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

{
  "agent": "claude",
  "input": "Write property-based tests for this Rust crate. Read Cargo.toml, clippy.toml, .github/workflows/ci.yml, the ci directory, every file under tests, and the public functions in src/bytes.rs and src/bytes_mut.rs first. Add proptest as a dev-dependency and write tests/test_properties.rs against a Vec<u8> model: split_off and split_to recombine to the original, slice and slice_ref agree with the model for every range form, truncate keeps min(n, len) bytes, BytesMut split_to then unsplit restores the bytes, a random operation sequence over BytesMut and the model stays equal at each step and after freeze, chain and take and copy_to_bytes match the model, and out-of-range indices panic. Run cargo test --all-features and cargo hack test --each-feature --optional-deps, run the minrust check with the new dev-dependency and pin proptest if it does not resolve on the rust-version, gate the file for miri, run cargo fmt --all --check, and open a pull request with the output and any counterexample found. Change no code under src.",
  "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 its own branch that adds tests/test_properties.rs and a proptest line under [dev-dependencies] in Cargo.toml, with nothing changed under src/. The description lists each property in one line with the API it exercises, states which feature combinations it ran through cargo hack, says whether proptest had to be pinned for the minrust job and to what, and quotes the test summary. If a property found a counterexample, the shrunken input and the assertion are in the description, and the property stays in the file marked #[ignore] with a comment, because a failing property on a released crate is a finding for the maintainers rather than something to silence.

A reviewer reads the model-based sequence test first, since it carries most of the coverage, and checks that the model's semantics match the documented behaviour of advance and truncate rather than the implementation's. They confirm the cfg gates: std for the model, not(miri) for the nightly job, and nothing that hides the tests from cargo test --all-features. The transcript holds every cargo run, so the feature matrix in the description can be checked against the commands that produced it.

Run it on every Sunday night

An automation in the Project with a schedule trigger, a cron expression for Sunday night and your timezone, starts one run per week with a prompt that asks for properties on whichever public functions still have only example tests. Each run opens its own pull request on its own branch, and a week with nothing left to cover opens none.

For several crates, a script on your server lists the organization's repositories with GET /o/{org}/github, checks each Cargo.toml for a proptest or quickcheck dev-dependency, and sends one POST /o/{org}/sessions with this prompt for each crate without one, with a request_id per repository so a re-run is safe. A webhook_url on the API key delivers run.completed with the session_url for each pull request.

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

Why run it on Rush?

Property tests are cheap to write and expensive to land: the first draft breaks the MSRV job, the second does not compile without std, the third makes miri take an hour. The agent runs the crate's own CI matrix in its sandbox before opening the pull request, with cargo hack and the rust-version check, so what arrives is already green against the jobs that would have rejected it.

The pull request explains each property in a sentence and shows the runs behind it, and a counterexample arrives as a shrunken input with the assertion that failed, which is the most useful bug report a maintainer can get. Several crates can be covered at once, each in its own sandbox and branch.

Frequently asked questions

Why proptest and not quickcheck?

Both work; proptest shrinks structured inputs such as operation sequences well and lets the case count be set per test, which matters for the model-based test. If your crate already uses quickcheck, say so in the prompt and the agent writes the same properties in that style instead of adding a second framework.

Will the MSRV job still pass?

The agent checks rather than hopes. rust-version is 1.57 and the minrust job runs cargo hack check --rust-version, which resolves dev-dependencies even though it does not compile them. The agent runs that command with the new dev-dependency and pins proptest to a release that resolves on 1.57 if the latest does not, and the pull request says which.

Does it change any code under src?

No. The prompt forbids it, and the pull request adds a test file and a dev-dependency only. If a property finds a real bug, the agent reports the shrunken counterexample in the description and leaves the property in place marked #[ignore] with a comment, so the maintainers decide on the fix.

What about miri, loom and the no_std targets?

The new file is gated with #[cfg(not(miri))], because random inputs under miri are slow and the example tests already run there. Loom runs cargo test --lib and never compiles integration tests. The no_std builds use --no-dev-deps, so proptest is never compiled for thumbv6m or thumbv7m. The pull request states each of these so a reviewer need not rediscover them.