Development Documentation (main branch) - For stable release docs, see docs.rs/eidetica
Skip to main content

Module testing

Module testing 

Source
Expand description

In-process multi-instance test harness (Tier 0).

Standing up several real Instances that sync with one another takes a pile of identical boilerplate: create a user and key, enable sync, register a transport, start serving, resolve the bound address. Cluster does that plumbing and nothing else — it hands back wired peers and leaves every policy decision (auth, what to write, how to drive sync) to the test.

That split is deliberate. A correctness harness for authenticated CRDT sync must keep the two things it exists to exercise — the transport and the auth — under the test’s control, not baked into the setup:

  • Transport is a seam. ClusterBuilder::transport takes any TestTransport; the default is HttpLoopback. A controllable in-memory transport (deliver / reorder / drop / single-step — Tier 1) is a drop-in here, which is the point: Tier 1 extends this, it doesn’t replace it.
  • Auth is the test’s. The harness never grants keys or permissions for you. A peer exposes its User, key id, and key name; the test creates its database with whatever auth posture it’s exercising. add_auth_keys and set_global_auth_key are policy-neutral tools the test composes — they apply the keys you pass, they don’t choose them.

What the harness owns is plumbing only: wiring peers, marking a tree sync-enabled (Peer::serve), driving an exchange (Cluster::exchange), and observing convergence (Cluster::converged). It does not hold your databases — the test opens and keeps those itself.

This is topology A (multi-peer sync): N independent Instances, each owning an in-memory backend. Sync is driven explicitly (no background timers), so a test fully orders the exchange. The multi-client / single-service topology is a separate harness that lands with the service feature.

Gated behind cfg(any(test, feature = "testing")) alongside FixedClock and Instance::create_backend_with_clock; never compiled into a release build.

use eidetica::{
    auth::{Permission, types::AuthKey},
    crdt::Doc,
    testing::{Cluster, set_global_auth_key},
    user::types::SyncSettings,
};

let mut net = Cluster::builder().peers(2).build().await?;

// Peer 0 creates a database with auth the *test* chooses, then serves it.
let key0 = net.peer(0).key_id().clone();
let mut settings = Doc::new();
settings.set("name", "chat");
let db = net.peer_mut(0).user_mut().create_database(settings, &key0).await?;
let room = db.root_id().clone();
set_global_auth_key(&db, AuthKey::active(None, Permission::Write(10))).await?;
net.peer_mut(0).serve(&room).await?;

// Peer 1 bootstraps with its own key, then converges against peer 0.
let key1 = net.peer(1).key_id().clone();
let signing_key1 = net.peer(1).user().get_signing_key(&key1)?;
let name1 = net.peer(1).key_name().to_string();
let addr0 = net.peer(0).address().clone();
net.peer(1)
    .sync()
    .sync_with_peer_for_bootstrap_with_key(
        &addr0,
        &room,
        &signing_key1,
        &name1,
        Permission::Write(10),
    )
    .await?;
net.peer_mut(1)
    .user_mut()
    .track_database(room.clone(), &key1, SyncSettings::disabled())
    .await?;

net.exchange(1, 0, &room).await?;
assert!(net.converged(&[0, 1], &room).await?);

Structs§

Cluster
A set of in-process eidetica peers wired for multi-peer sync. Each peer is a full Instance with its own backend. The cluster owns the wiring; the test owns the databases, the auth, and the order of operations.
ClusterBuilder
Builder for a Cluster. Obtain via Cluster::builder.
HttpLoopback
Default TestTransport: HTTP over an OS-assigned loopback port.
Peer
One peer in a Cluster: a full Instance plus the handles a test needs to act as that peer. It does not hold the peer’s application databases — the test opens and keeps those.
SimLoopback
TestTransport backed by a SimNetwork: an in-memory drop-in for HttpLoopback. Build a cluster over it with Cluster::builder().transport(Arc::new(SimLoopback::new(net.clone()))) and keep net to drive partitions.
SimNetwork
In-memory message fabric shared by every SimTransport in a cluster, and the control handle a test uses to inject faults. A drop-in for HttpLoopback via ClusterBuilder::transport that additionally lets a test partition links and heal them.
SimTransport
In-memory SyncTransport. Routes a SyncRequest straight to the target peer’s SyncHandler through the shared SimNetwork — no sockets, no serialization — and honors the network’s partition state.

Traits§

TestTransport
How a peer makes itself reachable to other peers. The one seam Tier 1 swaps: implement this over an in-memory, controllable network and the rest of the harness is unchanged.

Functions§

add_auth_keys
Apply per-key auth to a database via a settings transaction. The caller chooses the keys and permissions; this just writes them.
set_global_auth_key
Set the global (wildcard) auth key on a database via a settings transaction. The caller chooses the permission level.