mock_nexus/lib.rs
1//! Mock Nexus raw-storage server with a failure-scenario table.
2//!
3//! Implements just enough HTTP/1.1 (std only) to exercise the nexus-raw transport contract.
4//! `GET`/`HEAD`/`PUT` with `Content-Length` or chunked bodies, `DELETE` with 204/404.
5//! Percent-encoded paths stored verbatim.
6//! One request per connection, `Connection: close` on every response.
7//! GET honors resumable downloads: a single open `Range: bytes=N-` is answered with `206` and `Content-Range`, out-of-range starts get `416`.
8//! Any other `Range` form is ignored.
9//! A [`Scenario`] selects a failure mode: truncated PUT and GET bodies, connection resets, held uploads, missing `Content-Length`, slow links, drifted documents, flaky 503s, 503s with a `Retry-After` pause, rate-limit 429s with `Retry-After`, redirects, and Basic-auth gating.
10//!
11//! Rust conformance tests use the library API directly.
12//! The `mock-nexus` binary exposes the same scenarios to shell- and Python-driven tests:
13//!
14//! ```text
15//! let server = mock_nexus::MockNexus::start(mock_nexus::Scenario::Atomic)?;
16//! let url = format!("{}1.14.0/version.json", server.base_url());
17//! ```
18//!
19//! Presets are named points in a behavior space.
20//! A [`Spec`] composes the space itself:
21//! layers stack (`Spec::atomic().with_auth(..).with_flaky(3)`), and `MockNexus::start_spec`
22//! runs a composed instance. A composed instance never enters the contract tables:
23//! presets and their drift guards are the contract, specs are code-level composition.
24//!
25//! A group repository is a separate deployment kind, not a scenario:
26//! [`MockNexus::start_group`] aggregates two or more running members, forwarding reads in member order and refusing writes.
27//! On a group handle the store mutators ([`MockNexus::insert`], [`MockNexus::enable_drift`]) are no-ops: a group stores nothing.
28//!
29//! The client-facing contract page `docs/reference/invariants.md` is rendered from [`SCENARIO_DOCS`].
30//! A drift test in the `invariants` module fails when a scenario change does not regenerate it.
31
32mod base64;
33mod group;
34mod handle;
35mod http;
36mod invariants;
37mod scenario;
38mod server;
39mod service_api;
40mod spec;
41mod storage;
42mod store;
43mod support;
44#[cfg(test)]
45mod tests;
46
47pub use handle::MockNexus;
48pub use invariants::{render_invariants_markdown, ScenarioDoc, SCENARIO_DOCS};
49pub use scenario::{Scenario, SCENARIOS};
50pub use server::Drip;
51pub use spec::Spec;
52pub use store::{Outcome, ReqLog};