syncular-testkit
Rust-first testing utilities for Syncular apps and SDK bindings.
The crate mirrors the value of @syncular/testkit: tests should use real SQLite
stores, generated app schema metadata, scripted transports, fault injection, and
assertions instead of mocking Syncular internals.
Initial scope:
TempDbPathfor disposable SQLite databases, including-wal,-shm, and-journalcleanup.TestTransportfor in-process sync, websocket, auth-header, chunk, and blob assertions, including static and request-dependent scripted responses.TestSyncServerfor disposable HTTP sync endpoints in native/client tests.AppTestServerfor stateful generated-schema app tests: it stores rows, applies pushed commits, returns later pull snapshots/commits, merges server-merge CRDT/Yjs payloads, filters self commits, can reverse/duplicate delivery, preserves encrypted field payloads, can revoke/restore subscriptions, can change required auth during a test, and emits realtime sync wakeups. It also implementsBlobTransportfor queued upload/download tests.AppTestHttpServerfor stateful HTTP/WebSocket app tests over the production native transport shape, including captured HTTP/WebSocket requests for auth and schema-version assertions. The same fixture can enforce a required authorization token for HTTP sync and WebSocket connection tests.TestBlobServerfor local HTTP blob upload/download integration tests.FaultTransportfor scripted transport failures and latency.- Protocol builders for snapshot pages/chunks, pull commits, conflict, revoked, schema-required/latest, and not-ok server responses.
- Shared conformance fixture helpers for
examples/todo-app/conformance/sync-scenarios.json, so Rust runtime, SDK, and app tests read the same scenario values. Usesync_conformance_fixture()for the typed Rust contract, and keep the path helpers for small one-off assertions. - Offline auth lease helpers for issuing and verifying deterministic ES256 test tokens without making app tests build a private signer.
AppFixturehelpers that accept a generatedAppSchemafrom consuming apps, including file-backed temp DBs and Rust-only in-memory DBs.TodoFixturehelpers backed by the generated todo schema and Diesel SQLite.- Native fixture helpers for opening real
NativeSyncularClientinstances with embedded schema JSON or direct app schemas, plus event waiters. - CRDT field helpers for applying text updates and asserting materialized Rust/native field values.
- Assertions for outbox, conflicts, stateful app server rows/commits/auth, captured HTTP request headers, blob queue, blob cache state, and stable native error/diagnostic codes.
App Usage
Add the local testkit crate as a dev dependency while testing against this workspace:
[]
= { = "/Users/bkniffler/conductor/workspaces/syncular/indianapolis/rust/crates/testkit" }
= { = "/Users/bkniffler/conductor/workspaces/syncular/indianapolis/rust/crates/runtime" }
Use your generated app schema, not the repo todo fixture:
use ;
For multi-client app behavior, use AppTestServer. It is generic over the
generated AppSchema, so app-specific tests only need to provide seed rows and
assert app-specific fields:
use ;
For binding or app-shell tests that need the production HTTP/WebSocket transport
shape, wrap the same stateful server in AppTestHttpServer:
use ;
use ;
To test app auth behavior without a private server mock, configure the stateful server with a required authorization header:
use ;
let app_schema = app_schema;
let server = start_with_server.unwrap;
// Requests without `authorization: Bearer test-token` receive HTTP 401, and
// WebSocket connections with the wrong token are rejected during the handshake.
// After an authorized request:
assert_app_server_auth_header;
The required token can also change inside one test, which is useful for auth refresh flows:
server.app_server.require_authorization;
// trigger a rejected sync with stale auth, refresh app auth state, then retry
server.app_server.clear_required_authorization;
For rolling-deploy tests, the same stateful server can advertise future schema versions and let the real client fail closed:
use ;
let app_schema = app_schema;
let required_schema_version = app_schema.current_schema_version + 1;
let server = start_with_server.unwrap;
// A client with the current generated schema receives `sync.schema_mismatch`
// and must not apply rows from this response.
For scoped access revocation, the stateful server can return a real revoked subscription response, which lets the client clear the previous scoped rows and reset its cursor without a scripted transport:
server.app_server.revoke_subscription;
// next sync clears rows for the previous subscription state
server.app_server.restore_subscription;
// following sync bootstraps visible rows again
For native-style tests, open a real native client with the same generated schema or with generated schema JSON:
use Duration;
use NativeEventKind;
use ;
For host/HTTP integration tests, use TestSyncServer instead of standing up a
production server:
use ;
let server = spawn.unwrap;
let base_url = server.url;
For request-dependent protocol cases, queue a response function. This is useful for push conflicts or duplicate acknowledgements where the response needs the client commit id from the actual request:
use ;
fixture.transport.push_http_response_fn;
Conformance Gates
When changing the testkit or shared fixtures, run the fast Rust-first conformance lane:
Use the heavier lanes when changing browser/Hono behavior or native bindings:
Reusable runtime test patterns that should move here over time:
protocol_contract.rs: most generic protocol scripting now usesTestTransport, protocol builders, andFaultTransport. The remaining local mock is intentionally scoped to encrypted row/chunk/blob fixtures and lock-reentrancy tests.native_facade.rs/native_ffi.rs: temp database paths, todo app schema JSON setup, native event waiters, and generated row assertions.blob_transport.rs: blob queue/cache assertions and local HTTP blob transport.crdt_field.rs: remaining encrypted CRDT system-table fixtures and ciphertext roundtrip assertions. Server-merge convergence fixtures now useAppTestServer.store_backends.rs: generic backend parity sync fixtures now useTestTransportand protocol builders. The remaining local transports are encrypted CRDT system-table fixtures.