Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Neo4jContainer

A single-node Neo4j Community container, queried over its HTTP Cypher transaction endpoint (/db/neo4j/tx/commit) — no bolt driver dependency needed, matching the house convention for HTTP-first modules (ClickHouse, Pinot). The bolt port (7687) is still exposed and its URI available via bolt_url() for callers who do want a real driver.

Default image: neo4j:5-community Guest ports: 7474 (HTTP), 7687 (bolt)

MethodOnEffect
Neo4jContainer::new()builderPinned default image, neo4j/rightsize-test, with_memory_limit(1024).
Neo4jContainer::with_image(image)builderCaller-chosen image.
.with_password(p)builderOverrides the password half before start() (the image requires ≥8 characters).
.start()builder → Result<Neo4jGuard>Boots the container.
.username()guardThe fixed admin username (neo4j — no env var to change it).
.password()guardThe configured admin password.
.http_url()guardThe HTTP interface’s base URI — Cypher transactions via POST {http_url}/db/neo4j/tx/commit.
.bolt_url()guardThe bolt interface’s URI, for callers using a real bolt driver.
.stop()guardStops and removes the container, releases its ports.

There’s no with_username — the username is fixed by the image at neo4j.

Readiness — Started. is the exact log line, verified against a real boot

Captured verbatim from neo4j:5-community:

... INFO  Bolt enabled on 0.0.0.0:7687.
... INFO  HTTP enabled on 0.0.0.0:7474.
... INFO  Remote interface available at http://localhost:7474/
... INFO  Started.

Started. is logged only after both connectors are already listening, so it’s both accurate and simpler than a two-port HTTP/bolt race.

Ships as .*Started\..* — a real regex, so the escaped dot means exactly what it reads: a literal . right after “Started”, not a wildcard.

The image refuses short passwords

neo4j/neo4j is rejected at boot — passwords must be at least 8 characters — so this module defaults to neo4j/rightsize-test, giving zero-configuration use of http_url() plus basic auth. with_password still requires an 8+-character value.

Memory — measured, needed the ladder

At msb’s default ~450 MB microVM RAM, the server logs ERROR Invalid memory configuration - exceeds physical memory and shuts itself down cleanly (INFO Stopped.) rather than hanging or getting OOM-killed — Neo4j’s own memory-recommendation calculator sizes the page cache and heap off total visible RAM and refuses to start if the sums don’t fit. Retried with -m 1024M: boots clean. with_memory_limit(1024) is this module’s default, same number as KeycloakContainer.

Complete example

use rightsize_modules::Neo4jContainer;

#[tokio::test]
async fn create_then_match_round_trips() -> Result<(), Box<dyn std::error::Error>> {
    let guard = Neo4jContainer::new().start().await?;

    let agent = ureq::Agent::new_with_defaults();
    let auth = format!("Basic {}", "bmVvNGo6cmlnaHRzaXplLXRlc3Q"); // base64 in the real test
    let commit_url = format!("{}/db/neo4j/tx/commit", guard.http_url());
    let commit = |statement: &str| {
        let body = format!(r#"{{"statements":[{{"statement":"{statement}"}}]}}"#);
        agent
            .post(&commit_url)
            .header("Content-Type", "application/json")
            .header("Authorization", &auth)
            .send(body)
    };

    let mut create = commit("CREATE (n:Test {name: 'hello'}) RETURN n.name AS name")?;
    let create_body = create.body_mut().read_to_string()?;
    assert!(create_body.contains("\"errors\":[]"));

    let mut matched = commit("MATCH (n:Test {name: 'hello'}) RETURN n.name AS name")?;
    let match_body = matched.body_mut().read_to_string()?;
    assert!(match_body.contains("\"hello\""));

    guard.stop().await?;
    Ok(())
}

Backend notes

with_memory_limit(1024) is set unconditionally by the module — see Memory above.