Skip to content

Commit f61e7dd

Browse files
committed
fix(notebooks,tests): clarify embedded isolation, narrow FTS xfail scope
- nb00: update intro and final message to explain that LocalClient(":memory:") is process-local — notebooks 01-03 cannot see seeded data in embedded mode; add note that cross-notebook sharing requires a shared CoordiNode server - tests: remove raises=AssertionError from _fts xfail marker; instead call pytest.xfail() inline in the two hit-bearing tests when results are empty, so all other AssertionErrors (wrong types, malformed scores) surface as real failures on FTS-capable servers
1 parent 9e9c343 commit f61e7dd

2 files changed

Lines changed: 21 additions & 41 deletions

File tree

demo/notebooks/00_seed_data.ipynb

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@
99
"\n",
1010
"[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/structured-world/coordinode-python/blob/main/demo/notebooks/00_seed_data.ipynb)\n",
1111
"\n",
12-
"Populates CoordiNode with a **tech industry knowledge graph** you can explore\n",
13-
"in notebooks 01–03.\n",
12+
"Populates CoordiNode with a **tech industry knowledge graph**.\n",
13+
"\n",
14+
"> **Note:** When using `coordinode-embedded` (`LocalClient(\":memory:\")`), the seeded data\n",
15+
"> lives only inside this notebook process — notebooks 01–03 will start with an empty graph.\n",
16+
"> To share the graph across notebooks, point all of them at the same running CoordiNode\n",
17+
"> server via `COORDINODE_ADDR`.\n",
1418
"\n",
1519
"**Graph contents:**\n",
1620
"- 10 people (engineers, researchers, founders)\n",
@@ -293,35 +297,7 @@
293297
"id": "a1b2c3d4-0000-0000-0000-000000000014",
294298
"metadata": {},
295299
"outputs": [],
296-
"source": [
297-
"print(\"=== Who works at Synthex? ===\")\n",
298-
"rows = client.cypher(\n",
299-
" \"MATCH (p:Person {demo_tag: $tag})-[:WORKS_AT]->(c:Company {name: $co, demo_tag: $tag}) \"\n",
300-
" \"RETURN p.name AS name, p.role AS role\",\n",
301-
" params={\"co\": \"Synthex\", \"tag\": DEMO_TAG},\n",
302-
")\n",
303-
"for r in rows:\n",
304-
" print(f\" {r['name']} — {r['role']}\")\n",
305-
"\n",
306-
"print(\"\\n=== What does Synthex use? ===\")\n",
307-
"rows = client.cypher(\n",
308-
" \"MATCH (c:Company {name: $co, demo_tag: $tag})-[:USES]->(t:Technology {demo_tag: $tag}) RETURN t.name AS name\",\n",
309-
" params={\"co\": \"Synthex\", \"tag\": DEMO_TAG}\n",
310-
")\n",
311-
"for r in rows:\n",
312-
" print(f\" {r['name']}\")\n",
313-
"\n",
314-
"print(\"\\n=== GraphRAG dependency chain ===\")\n",
315-
"rows = client.cypher(\n",
316-
" \"MATCH (t:Technology {name: $tech, demo_tag: $tag})-[:BUILDS_ON*1..3]->(dep:Technology {demo_tag: $tag}) RETURN dep.name AS dependency\",\n",
317-
" params={\"tech\": \"GraphRAG\", \"tag\": DEMO_TAG},\n",
318-
")\n",
319-
"for r in rows:\n",
320-
" print(f\" → {r['dependency']}\")\n",
321-
"\n",
322-
"print(\"\\n✓ Demo data ready — open notebooks 01, 02, 03 to explore!\")\n",
323-
"client.close()"
324-
]
300+
"source": "print(\"=== Who works at Synthex? ===\")\nrows = client.cypher(\n \"MATCH (p:Person {demo_tag: $tag})-[:WORKS_AT]->(c:Company {name: $co, demo_tag: $tag}) \"\n \"RETURN p.name AS name, p.role AS role\",\n params={\"co\": \"Synthex\", \"tag\": DEMO_TAG},\n)\nfor r in rows:\n print(f\" {r['name']} — {r['role']}\")\n\nprint(\"\\n=== What does Synthex use? ===\")\nrows = client.cypher(\n \"MATCH (c:Company {name: $co, demo_tag: $tag})-[:USES]->(t:Technology {demo_tag: $tag}) RETURN t.name AS name\",\n params={\"co\": \"Synthex\", \"tag\": DEMO_TAG}\n)\nfor r in rows:\n print(f\" {r['name']}\")\n\nprint(\"\\n=== GraphRAG dependency chain ===\")\nrows = client.cypher(\n \"MATCH (t:Technology {name: $tech, demo_tag: $tag})-[:BUILDS_ON*1..3]->(dep:Technology {demo_tag: $tag}) RETURN dep.name AS dependency\",\n params={\"tech\": \"GraphRAG\", \"tag\": DEMO_TAG},\n)\nfor r in rows:\n print(f\" → {r['dependency']}\")\n\nprint(\"\\n✓ Demo data seeded.\")\nprint(\"To query it from notebooks 01–03, connect them to the same CoordiNode server (COORDINODE_ADDR).\")\nclient.close()"
325301
}
326302
],
327303
"metadata": {

tests/integration/test_sdk.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -566,19 +566,21 @@ def test_vector_search_returns_results(client):
566566
def _fts(fn):
567567
"""Mark an FTS test as expected-failure on servers without TextService.
568568
569-
Two expected failure modes:
570-
- ``AssertionError``: server returns an empty result set (no FTS index), caught
571-
by the marker directly so pytest reports it as xfail.
572-
- ``grpc.StatusCode.UNIMPLEMENTED``: TextService RPC does not exist yet; we call
573-
``pytest.xfail()`` explicitly so only that specific status code is silenced.
574-
Any other ``grpc.RpcError`` (e.g. INVALID_ARGUMENT from a malformed request)
575-
propagates as a real test failure, preventing regressions from being masked.
569+
Expected failure modes handled explicitly — NOT via ``raises=`` on the marker:
570+
- ``grpc.StatusCode.UNIMPLEMENTED``: TextService RPC does not exist; caught in
571+
the wrapper and converted to ``pytest.xfail()`` so only this status is silenced.
572+
- Empty result set: tests that require at least one hit call ``pytest.xfail()``
573+
inline (``if not results: pytest.xfail(...)``), keeping the xfail scoped to
574+
that specific condition.
575+
576+
Any ``AssertionError`` that is NOT the "no results" case (e.g. wrong return type,
577+
malformed score) propagates as a real test failure so regressions on FTS-capable
578+
servers are visible in CI.
576579
"""
577580

578581
@pytest.mark.xfail(
579582
reason="TextService requires CoordiNode >=0.3.8 with FTS support",
580583
strict=False,
581-
raises=AssertionError,
582584
)
583585
@functools.wraps(fn)
584586
def wrapper(*args, **kwargs):
@@ -603,7 +605,8 @@ def test_text_search_returns_results(client):
603605
try:
604606
results = client.text_search("FtsTest", "machine learning", limit=5)
605607
assert isinstance(results, list)
606-
assert len(results) >= 1, "text_search returned no results"
608+
if not results:
609+
pytest.xfail("text_search returned no results — FTS index not available on this server")
607610
r = results[0]
608611
assert isinstance(r, TextResult)
609612
assert isinstance(r.node_id, int)
@@ -656,7 +659,8 @@ def test_hybrid_text_vector_search_returns_results(client):
656659
limit=5,
657660
)
658661
assert isinstance(results, list)
659-
assert len(results) >= 1, "hybrid_text_vector_search returned no results"
662+
if not results:
663+
pytest.xfail("hybrid_text_vector_search returned no results — FTS index not available on this server")
660664
r = results[0]
661665
assert isinstance(r, HybridResult)
662666
assert isinstance(r.node_id, int)

0 commit comments

Comments
 (0)