ChunkWriter mirrors chunks into Neo4j after OpenSearch indexing, creating
HAS_CHUNK edges and DERIVED_FROM back-references to the source Elements
(via doc_items propagated from the local chunker).
Graph API: GET /api/documents/{id}/graph returns a cytoscape-shaped
payload with nodes + edges for Document / Element / Page / Chunk.
Hard cap at 200 pages returns HTTP 413 per design §8.4.
Frontend: new Graph tab in Studio results, rendered with Cytoscape.js +
dagre layout (lazy-loaded, ~175 KB gz). Legend, node styling per element
label, directional edges styled per edge type.
README gains a Neo4j section with the schema, three demo Cypher
queries, and env vars. Backend tests skip cleanly when the neo4j python
package is not installed locally.
Refs #186
40 lines
1.2 KiB
Python
40 lines
1.2 KiB
Python
"""Shared fixtures for Neo4j integration tests.
|
|
|
|
These tests are skipped unless a live Neo4j is reachable via NEO4J_TEST_URI
|
|
(defaulting to bolt://localhost:7687). CI spins up `neo4j:5.15-community`
|
|
alongside the job; locally run `docker compose -f docker-compose.dev.yml up neo4j`.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
import pytest
|
|
|
|
# Skip the entire module cleanly when the neo4j driver package is absent
|
|
# (e.g. local dev without the dependency installed).
|
|
pytest.importorskip("neo4j")
|
|
|
|
from infra.neo4j import close_driver, get_driver # noqa: E402
|
|
|
|
|
|
def _cfg() -> tuple[str, str, str]:
|
|
return (
|
|
os.environ.get("NEO4J_TEST_URI", "bolt://localhost:7687"),
|
|
os.environ.get("NEO4J_TEST_USER", "neo4j"),
|
|
os.environ.get("NEO4J_TEST_PASSWORD", "changeme"),
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
async def neo4j_driver():
|
|
uri, user, password = _cfg()
|
|
try:
|
|
neo = await get_driver(uri, user, password)
|
|
except Exception as exc:
|
|
pytest.skip(f"Neo4j not reachable at {uri}: {exc}")
|
|
# Wipe DB before each test — integration tests assume an empty graph.
|
|
async with neo.driver.session(database=neo.database) as session:
|
|
await session.run("MATCH (n) DETACH DELETE n")
|
|
yield neo
|
|
await close_driver()
|