Skip to main content

cratefield_testing/
lib.rs

1//! `cratefield-testing`: the conformance kit every Factory Zero module runs
2//! against (issue #9) — fake ports, an in-memory SQLite `Database`, and
3//! request helpers over the axum router with **no network**.
4//!
5//! ```no_run
6//! use cratefield_testing::{conformance, TestHarness, request};
7//!
8//! #[test]
9//! fn my_module_conforms() {
10//!     conformance(Box::new(my_module::MyModule::new()));
11//! }
12//!
13//! #[pollster::test]
14//! async fn join_returns_202() {
15//!     let kit = TestHarness::new(vec![Box::new(my_module::MyModule::new())]);
16//!     let response = request(&kit.router, http::Method::POST,
17//!         "/v1/my-module/join", Some(r#"{"email":"nick@example.com"}"#)).await;
18//!     assert_eq!(response.status, http::StatusCode::ACCEPTED);
19//! }
20//! ```
21
22#![doc = include_str!("../README.md")]
23#![forbid(unsafe_code)]
24
25// Needs only `cratefield-core`, so it is ungated like `vectors` and
26// `tmp`: a light consumer can assert the batch contract without the
27// harness graph (issues #126, #201).
28mod batch;
29// The `Actors` contract (issue #583): gated with the harness because it
30// drives a host with the kit's handler and needs `async-trait`.
31#[cfg(feature = "harness")]
32mod actor;
33// Byte round-trips (issue #39): ungated for the same reason as `batch`
34// — it binds through sea-query's `From` impls without naming the crate,
35// so it needs nothing beyond `cratefield-core`.
36mod blob;
37// The outbound-budget conformance checks (issue #765): gated with the
38// harness like `actor`, because the fixture needs `async-trait`, the kit's
39// `ManualClock` and `serde_json` — harness-only dependencies here.
40#[cfg(feature = "harness")]
41mod budget;
42#[cfg(feature = "harness")]
43mod conformance;
44#[cfg(feature = "harness")]
45mod dialect;
46#[cfg(feature = "harness")]
47mod fakes;
48#[cfg(feature = "harness")]
49mod harness;
50#[cfg(feature = "postgres")]
51mod pg;
52#[cfg(feature = "port-conformance")]
53mod port;
54#[cfg(feature = "harness")]
55mod privacy_provider;
56#[cfg(feature = "harness")]
57mod request;
58#[cfg(feature = "harness")]
59mod sidecar;
60// The `Speech` port contract (issue #861): ungated like `port` — it
61// needs nothing beyond `cratefield-core`.
62#[cfg(feature = "port-conformance")]
63mod speech;
64// Webhook request signers (issue #666): mint the headers a provider
65// sends, for a test to feed `WebhookVerifier`. Like `batch` and `blob`
66// it is ungated — it needs only `cratefield-core` and the `http`/HMAC
67// crates core already carries, none of the harness graph.
68mod signing;
69mod tmp;
70pub mod vectors;
71
72#[cfg(feature = "harness")]
73pub use actor::{
74    CONTRACT_ACTOR_KIND, assert_actor_contract, contract_actor_handler, contract_actor_handlers,
75};
76pub use batch::assert_batch_is_atomic;
77pub use blob::{assert_blob_large_round_trips, assert_blob_round_trips};
78#[cfg(feature = "harness")]
79pub use budget::{
80    assert_budget_daily_quota, assert_budget_pacing, assert_budget_passthrough,
81    assert_budget_retries,
82};
83#[cfg(feature = "harness")]
84pub use conformance::{conformance, conformance_in_process_only, full_fake_ports, sidecar_parity};
85#[cfg(feature = "harness")]
86pub use dialect::Dialect;
87#[cfg(feature = "harness")]
88pub use fakes::{
89    Ask, AuthMode, ClassifierMode, CommentedCall, EmptyDatabase, FAKE_EMBEDDER_DIMENSIONS,
90    FAKE_EMBEDDER_MODEL, FakeAuth, FakeCaptcha, FakeClassifier, FakeCustomHostnames, FakeDefer,
91    FakeDispatcher, FakeEmbedder, FakeHttpClient, FakeMailer, FakePayments, FakePush,
92    FakeRateLimiter, FakeRealtime, FakeTextModel, FakeTracker, FakeUrlSafety, FiledCall,
93    FixedClock, MailerMode, ManualClock, MemoryActors, MemoryBlob, MemoryKeyValue, PaymentsCall,
94    PaymentsMode, PushMode, StatusedCall, TextModelMode, TrackerMode, error_json, problem_json,
95    with_retry_after,
96};
97#[cfg(feature = "harness")]
98pub use harness::TestHarness;
99#[cfg(feature = "port-conformance")]
100pub use port::{
101    TEXT_MODEL_CONFORMANCE_CACHED_INPUT_TOKENS, TEXT_MODEL_CONFORMANCE_INPUT_TOKENS,
102    TEXT_MODEL_CONFORMANCE_OUTPUT_TOKENS, TEXT_MODEL_CONFORMANCE_REPLY, assert_wasm_safe_deps,
103    classifier_conformance, classifier_conformance_questions, classifier_conformance_state,
104    classifier_not_configured, classifier_rejects_malformed_questions,
105    classifier_truncates_long_state, push_recipient_conformance, text_model_conformance,
106    text_model_conformance_not_configured, text_model_conformance_prompt,
107    text_model_image_bounds_conformance, vector_index_conformance,
108};
109#[cfg(feature = "harness")]
110pub use privacy_provider::{FakePrivacyProvider, ProviderCall};
111#[cfg(feature = "harness")]
112pub use request::{TestResponse, request, request_as, request_chunks};
113#[cfg(feature = "harness")]
114pub use sidecar::{FakeSidecar, Fault, shared as shared_sidecar};
115pub use signing::{sign_provider_scheme, sign_stripe_style};
116#[cfg(feature = "port-conformance")]
117pub use speech::{
118    SPEECH_CONFORMANCE_AUDIO_BYTES, SPEECH_CONFORMANCE_AUDIO_MILLIS, SPEECH_CONFORMANCE_SYNTH_TEXT,
119    SPEECH_CONFORMANCE_TRANSCRIPT_TEXT, SPEECH_CONFORMANCE_WORD_TIMINGS, speech_conformance,
120    speech_conformance_not_configured, speech_conformance_transcribe_request,
121};
122pub use tmp::TempDir;
123
124// The fixed test secret for the kit's Signer — an obvious dummy, never real.
125pub const TEST_HARNESS_SECRET: &str = "cratefield-testing-dummy-secret-0123456789";
126
127// Mirrors cratefield-adapter-postgres's integration-test skip reason.
128// The dialect axis (behind `harness`) prints it when the Postgres leg is
129// unavailable, so it exists whenever that axis or the leg itself does.
130#[cfg(any(feature = "harness", feature = "postgres"))]
131const POSTGRES_SKIP_REASON: &str = "start a local postgres:16 \
132     (docker run --rm -e POSTGRES_PASSWORD=postgres -p 5433:5432 postgres:16) \
133     and set FZ_TEST_POSTGRES_URL=postgres://postgres:postgres@127.0.0.1:5433/postgres";