Skip to content

Conversation

@CaliLuke
Copy link
Owner

@CaliLuke CaliLuke commented Oct 12, 2025

Summary

  • reorganize test suites under tests/unit, tests/integration, and tests/e2e
  • extract shared helper utilities for drivers and embeddings
  • update pytest markers, documentation, and CI to match new layout

Testing

  • uv run pytest tests/unit/search/test_lucene_utils.py -q
  • uv run pytest --collect-only

Summary by CodeRabbit

  • Tests

    • Reorganized tests into unit, integration, and e2e tiers with provider-specific markers and reusable integration fixtures.
    • Added integration placeholders and e2e harness scaffolding; introduced shared service helpers, deterministic embedding helpers, and a mock embedder.
    • Updated many tests to the new layout and fixture conventions; added unit coverage for Lucene sanitization.
  • Documentation

    • Added a Test Restructure Plan and testing guide updates; removed an obsolete MCP smoke debugging document.
  • Chores

    • CI workflow updated to run tests at their new locations and honor new markers.

@coderabbitai
Copy link

coderabbitai bot commented Oct 12, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

Reorganizes tests into unit/integration/e2e tiers, updates CI test targets to the new locations, replaces legacy test helpers with modular helpers/fixtures, adds provider-specific integration fixtures and placeholders, adjusts pytest markers/docs, and removes an obsolete MCP debug doc. No production API changes.

Changes

Cohort / File(s) Summary
CI workflow targets
\.github/workflows/ci.yml
CI test step now runs tests from tests/unit/... and tests/integration/... and points integration test target to tests/integration/core/shared/test_ingestion_pipeline.py::test_add_episode_persists_nodes_and_edges.
Test docs & plan
TEST_RESTRUCTURE_PLAN.md, docs/testing.md, AGENTS.md
Adds a Test Restructure Plan and updates testing docs to reflect unit/integration/e2e layout, commands, and hygiene guidance.
Pytest configuration
pytest.ini, tests/evals/pytest.ini
Adds markers (neo4j, falkordb, kuzu, neptune, e2e), refines integration marker description, and removes per-dir pytest/asyncio entries.
Global conftest fixture change
conftest.py
Replaces legacy imports with tests.helpers.embeddings.make_mock_embedder, exposes a mock_embedder pytest fixture, and updates __all__.
Helpers split & additions
tests/helpers/embeddings.py, tests/helpers/services.py, tests/helpers_test.py
Adds embeddings.py (mock embedder + embedding store) and services.py (provider discovery, driver provisioning, save/delete helpers, assertions); deletes legacy tests/helpers_test.py.
Integration fixture provisioning
tests/integration/shared/fixtures_services.py, tests/integration/conftest.py
New integration graph_driver fixture that selects available providers, applies provider markers, prepares and tears down drivers via tests.helpers.services.
Integration core tests refactor
tests/integration/core/shared/*.py
Core integration tests updated to accept graph_driver, import helpers from tests.helpers.services, and use standardized GROUP_ID; removed per-driver parametrization.
Provider placeholders
tests/integration/neo4j/test_placeholder.py, tests/integration/falkordb/test_placeholder.py, tests/integration/kuzu/test_placeholder.py
Added provider-specific placeholder tests decorated with corresponding markers that immediately pytest.skip().
E2E eval harness adjustments
tests/e2e/graph/eval_cli.py, tests/e2e/graph/eval_graph_building.py, tests/e2e/graph/test_eval_harness.py
Moved e2e imports, introduced Path-based IO and Neo4j settings, and added a skipped e2e placeholder test.
Unit test import fixes & additions
tests/unit/embedder/*.py, tests/unit/search/test_lucene_utils.py
Converted absolute fixture imports to relative imports; added tests/unit/search/test_lucene_utils.py verifying lucene sanitize escaping.
Misc cleanup / removals
tests/driver/__init__.py, MCP_SMOKE_DEBUG.md, tests/helpers_test.py (deleted)
Removed a module docstring, deleted MCP_SMOKE_DEBUG.md and the legacy helpers_test module.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant CI as CI Runner
  participant Collector as Pytest Discovery
  participant IntConftest as tests/integration/conftest.py
  participant Fixtures as fixtures_services.graph_driver
  participant Services as tests.helpers.services
  participant Driver as GraphDriver

  CI->>Collector: discover tests (unit/integration/e2e)
  Collector->>IntConftest: load integration conftest
  IntConftest->>Fixtures: request graph_driver fixture
  Fixtures->>Services: available_providers()
  alt provider available
    Fixtures->>Services: prepare_driver(provider)
    Services->>Driver: instantiate & clear test groups
    Services-->>Fixtures: return driver
    Fixtures-->>Collector: yield driver to tests
    Collector-->>Fixtures: tests complete
    Fixtures->>Services: teardown_driver(driver)
    Services->>Driver: close()
  else no provider
    Fixtures-->>Collector: skip tests (no provider)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

I thump my paw — directories rearranged,
Unit hops, integration bounds re-ranged.
A mock embedder hums a reproducible tune,
Drivers wake, then nap beneath the moon.
CI sniffs new paths and marks each skipped rune. 🥕🐇

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.79% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly summarizes the primary change, namely reorganizing the test suite into distinct unit, integration, and end-to-end tiers, which directly reflects the main objective of the pull request.

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea4941a and 8d44edf.

📒 Files selected for processing (3)
  • AGENTS.md (1 hunks)
  • tests/helpers/services.py (1 hunks)
  • tests/integration/conftest.py (1 hunks)

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
tests/unit/search/test_lucene_utils.py (1)

7-17: Consider using @pytest.mark.parametrize for better test isolation.

The current loop-based approach works, but using @pytest.mark.parametrize would provide clearer test names and better failure reporting for each case.

Apply this diff to refactor using parametrize:

+@pytest.mark.parametrize(
+    'query,expected',
+    [
+        (
+            'This has every escape character + - && || ! ( ) { } [ ] ^ " ~ * ? : \\ /',
+            '\\This has every escape character \\+ \\- \\&\\& \\|\\| \\! \\( \\) \\{ \\} \\[ \\] \\^ \\" \\~ \\* \\? \\: \\\\ \\/',
+        ),
+        ('this has no escape characters', 'this has no escape characters'),
+    ],
+)
-def test_lucene_sanitize():
-    queries = [
-        (
-            'This has every escape character + - && || ! ( ) { } [ ] ^ " ~ * ? : \\ /',
-            '\\This has every escape character \\+ \\- \\&\\& \\|\\| \\! \\( \\) \\{ \\} \\[ \\] \\^ \\" \\~ \\* \\? \\: \\\\ \\/',
-        ),
-        ('this has no escape characters', 'this has no escape characters'),
-    ]
-
-    for query, expected in queries:
-        assert lucene_sanitize(query) == expected
+def test_lucene_sanitize(query, expected):
+    assert lucene_sanitize(query) == expected
tests/helpers/embeddings.py (1)

18-46: Consider setting a numpy seed for deterministic embeddings.

The embeddings are generated with np.random.uniform() without a seed, which means they'll differ across test runs. If tests rely on consistent embedding values (beyond just shape/presence), this could cause non-deterministic behavior.

Apply this diff to add deterministic seeding:

 def default_embeddings() -> dict[str, list[float]]:
+    # Set seed for deterministic test embeddings
+    rng = np.random.Generator(np.random.PCG64(42))
     keys = [
         'Alice',
         'Bob',
         ...
     ]
     embeddings = {
-        key: np.random.uniform(0.0, 0.9, EMBEDDING_DIM).tolist() for key in keys
+        key: rng.uniform(0.0, 0.9, EMBEDDING_DIM).tolist() for key in keys
     }
     embeddings['Alice Smith'] = embeddings['Alice']
     return embeddings
tests/integration/conftest.py (1)

6-6: Consider removing the noqa directive.

Ruff indicates that F401 is not enabled in your configuration, making the noqa: F401 directive unnecessary for Ruff. If you're not using other linters (like flake8) that would flag this import, you can safely remove the directive.

Apply this diff if Ruff is your only linter:

-from tests.integration.shared.fixtures_services import graph_driver  # noqa: F401
+from tests.integration.shared.fixtures_services import graph_driver
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f296a6 and 47fc495.

📒 Files selected for processing (28)
  • .github/workflows/ci.yml (1 hunks)
  • MCP_SMOKE_DEBUG.md (0 hunks)
  • TEST_RESTRUCTURE_PLAN.md (1 hunks)
  • conftest.py (2 hunks)
  • docs/testing.md (3 hunks)
  • pytest.ini (1 hunks)
  • tests/driver/__init__.py (0 hunks)
  • tests/e2e/graph/eval_cli.py (1 hunks)
  • tests/e2e/graph/eval_graph_building.py (6 hunks)
  • tests/e2e/graph/test_eval_harness.py (1 hunks)
  • tests/evals/pytest.ini (0 hunks)
  • tests/helpers/embeddings.py (1 hunks)
  • tests/helpers/services.py (1 hunks)
  • tests/helpers_test.py (0 hunks)
  • tests/integration/conftest.py (1 hunks)
  • tests/integration/core/shared/test_entity_exclusion.py (5 hunks)
  • tests/integration/core/shared/test_graphium_bootstrap.py (1 hunks)
  • tests/integration/core/shared/test_ingestion_pipeline.py (2 hunks)
  • tests/integration/core/shared/test_repository_edges.py (18 hunks)
  • tests/integration/core/shared/test_repository_nodes.py (9 hunks)
  • tests/integration/falkordb/test_placeholder.py (1 hunks)
  • tests/integration/kuzu/test_placeholder.py (1 hunks)
  • tests/integration/neo4j/test_placeholder.py (1 hunks)
  • tests/integration/shared/fixtures_services.py (1 hunks)
  • tests/unit/embedder/test_gemini.py (1 hunks)
  • tests/unit/embedder/test_openai.py (1 hunks)
  • tests/unit/embedder/test_voyage.py (1 hunks)
  • tests/unit/search/test_lucene_utils.py (1 hunks)
💤 Files with no reviewable changes (4)
  • tests/driver/init.py
  • MCP_SMOKE_DEBUG.md
  • tests/helpers_test.py
  • tests/evals/pytest.ini
🧰 Additional context used
📓 Path-based instructions (4)
tests/**

📄 CodeRabbit inference engine (AGENTS.md)

Author tests under tests/ alongside features

Files:

  • tests/integration/kuzu/test_placeholder.py
  • tests/integration/falkordb/test_placeholder.py
  • tests/unit/embedder/test_gemini.py
  • tests/e2e/graph/eval_cli.py
  • tests/unit/embedder/test_voyage.py
  • tests/integration/core/shared/test_repository_nodes.py
  • tests/e2e/graph/eval_graph_building.py
  • tests/integration/core/shared/test_graphium_bootstrap.py
  • tests/integration/conftest.py
  • tests/helpers/services.py
  • tests/integration/shared/fixtures_services.py
  • tests/integration/neo4j/test_placeholder.py
  • tests/integration/core/shared/test_repository_edges.py
  • tests/e2e/graph/test_eval_harness.py
  • tests/unit/embedder/test_openai.py
  • tests/unit/search/test_lucene_utils.py
  • tests/integration/core/shared/test_entity_exclusion.py
  • tests/helpers/embeddings.py
  • tests/integration/core/shared/test_ingestion_pipeline.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indentation, 100-character line length, and prefer single quotes for Python code (as configured in pyproject.toml)
Use snake_case for module, file, and function names in Python

Files:

  • tests/integration/kuzu/test_placeholder.py
  • tests/integration/falkordb/test_placeholder.py
  • conftest.py
  • tests/unit/embedder/test_gemini.py
  • tests/e2e/graph/eval_cli.py
  • tests/unit/embedder/test_voyage.py
  • tests/integration/core/shared/test_repository_nodes.py
  • tests/e2e/graph/eval_graph_building.py
  • tests/integration/core/shared/test_graphium_bootstrap.py
  • tests/integration/conftest.py
  • tests/helpers/services.py
  • tests/integration/shared/fixtures_services.py
  • tests/integration/neo4j/test_placeholder.py
  • tests/integration/core/shared/test_repository_edges.py
  • tests/e2e/graph/test_eval_harness.py
  • tests/unit/embedder/test_openai.py
  • tests/unit/search/test_lucene_utils.py
  • tests/integration/core/shared/test_entity_exclusion.py
  • tests/helpers/embeddings.py
  • tests/integration/core/shared/test_ingestion_pipeline.py
conftest.py

📄 CodeRabbit inference engine (AGENTS.md)

Pytest configuration should reside in repository-level conftest.py

Files:

  • conftest.py
pytest.ini

📄 CodeRabbit inference engine (AGENTS.md)

Maintain pytest configuration in pytest.ini at the repo root

Files:

  • pytest.ini
🧠 Learnings (2)
📚 Learning: 2025-10-11T16:59:48.957Z
Learnt from: CR
PR: CaliLuke/graphium#0
File: AGENTS.md:0-0
Timestamp: 2025-10-11T16:59:48.957Z
Learning: Applies to tests/*_int.py : Integration tests live in tests/*_int.py and must be marked with pytest.mark.integration

Applied to files:

  • docs/testing.md
  • pytest.ini
📚 Learning: 2025-10-11T16:59:48.957Z
Learnt from: CR
PR: CaliLuke/graphium#0
File: AGENTS.md:0-0
Timestamp: 2025-10-11T16:59:48.957Z
Learning: Use pytest.mark.integration to gate database-reliant scenarios in CI

Applied to files:

  • pytest.ini
🧬 Code graph analysis (15)
conftest.py (1)
tests/helpers/embeddings.py (1)
  • make_mock_embedder (49-61)
tests/unit/embedder/test_gemini.py (1)
tests/unit/embedder/embedder_fixtures.py (1)
  • create_embedding_values (5-7)
tests/e2e/graph/eval_cli.py (1)
tests/e2e/graph/eval_graph_building.py (2)
  • build_baseline_graph (98-115)
  • eval_graph (118-173)
tests/unit/embedder/test_voyage.py (1)
tests/unit/embedder/embedder_fixtures.py (1)
  • create_embedding_values (5-7)
tests/integration/core/shared/test_repository_nodes.py (2)
tests/helpers/services.py (4)
  • assert_community_node_equals (204-215)
  • assert_entity_node_equals (188-201)
  • assert_episodic_node_equals (218-227)
  • get_node_count (160-169)
graphium_core/repository/node_repository.py (1)
  • delete_by_group_id (110-166)
tests/e2e/graph/eval_graph_building.py (1)
graphium_core/settings.py (1)
  • Neo4jSettings (277-280)
tests/integration/core/shared/test_graphium_bootstrap.py (1)
graphium_core/driver/driver.py (1)
  • GraphProvider (32-36)
tests/integration/conftest.py (1)
tests/integration/shared/fixtures_services.py (1)
  • graph_driver (30-39)
tests/helpers/services.py (7)
graphium_core/driver/driver.py (3)
  • GraphDriver (70-119)
  • GraphProvider (32-36)
  • repositories (115-119)
graphium_core/edges.py (3)
  • CommunityEdge (111-112)
  • EntityEdge (47-108)
  • EpisodicEdge (43-44)
graphium_core/nodes.py (3)
  • CommunityNode (152-163)
  • EntityNode (91-149)
  • EpisodicNode (78-88)
graphium_core/orchestration/maintenance/graph_data_operations.py (1)
  • clear_data (218-242)
graphium_core/settings.py (4)
  • FalkorSettings (283-290)
  • KuzuSettings (299-300)
  • Neo4jSettings (277-280)
  • NeptuneSettings (293-296)
graphium_core/repository/graph_repository.py (4)
  • delete_node (65-66)
  • delete_edge (75-76)
  • save_node (62-63)
  • save_edge (72-73)
graphium_core/repository/edge_repository.py (1)
  • load_fact_embedding (421-452)
tests/integration/shared/fixtures_services.py (2)
graphium_core/driver/driver.py (1)
  • GraphProvider (32-36)
tests/helpers/services.py (3)
  • available_providers (56-72)
  • prepare_driver (109-113)
  • teardown_driver (116-117)
tests/integration/core/shared/test_repository_edges.py (2)
tests/helpers/services.py (2)
  • get_edge_count (172-185)
  • get_node_count (160-169)
graphium_core/repository/node_repository.py (1)
  • get_by_entity_node_uuid (360-376)
tests/unit/embedder/test_openai.py (1)
tests/unit/embedder/embedder_fixtures.py (1)
  • create_embedding_values (5-7)
tests/integration/core/shared/test_entity_exclusion.py (2)
tests/integration/shared/fixtures_services.py (1)
  • graph_driver (30-39)
graphium_core/graphium.py (1)
  • Graphium (40-235)
tests/helpers/embeddings.py (3)
graphium_core/embedder/client.py (1)
  • EmbedderClient (18-26)
tests/unit/orchestration/test_initializer_factory.py (1)
  • create (68-69)
tests/unit/search/test_search_helpers.py (4)
  • create (26-27)
  • create (34-35)
  • create (43-44)
  • create (97-99)
tests/integration/core/shared/test_ingestion_pipeline.py (2)
graphium_core/driver/driver.py (1)
  • GraphProvider (32-36)
tests/helpers/services.py (7)
  • assert_entity_edge_equals (230-248)
  • assert_entity_node_equals (188-201)
  • assert_episodic_edge_equals (251-256)
  • assert_episodic_node_equals (218-227)
  • delete_all (149-157)
  • get_edge_count (172-185)
  • get_node_count (160-169)
🪛 GitHub Actions: CI
tests/integration/shared/fixtures_services.py

[error] 25-25: pytest: No graph providers enabled for integration tests (Skipped by fixture).

🪛 markdownlint-cli2 (0.18.1)
TEST_RESTRUCTURE_PLAN.md

19-19: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


61-61: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Ruff (0.13.3)
tests/integration/conftest.py

6-6: Unused noqa directive (non-enabled: F401)

Remove unused noqa directive

(RUF100)

tests/helpers/services.py

106-106: Avoid specifying long messages outside the exception class

(TRY003)


146-146: Avoid specifying long messages outside the exception class

(TRY003)


157-157: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (32)
tests/unit/embedder/test_gemini.py (1)

4-4: LGTM!

The test path comment and relative import correctly reflect the new test structure under tests/unit/embedder/.

Also applies to: 11-12

tests/unit/embedder/test_voyage.py (1)

12-13: LGTM!

The relative import correctly aligns with the restructured test layout under tests/unit/embedder/.

docs/testing.md (3)

21-23: LGTM!

The test layout description clearly documents the three-tier structure (unit/integration/e2e) with appropriate marker annotations.


53-53: LGTM!

The test commands accurately reflect the new paths and marker-based filtering for unit, integration, and e2e tiers.

Also applies to: 59-66


95-96: LGTM!

The example paths correctly point to the new integration test structure.

tests/e2e/graph/eval_graph_building.py (2)

6-6: LGTM!

Using Path(__file__).parent for BASE_DIR and loading Neo4j settings dynamically improves portability and aligns with the project's settings pattern.

Also applies to: 16-24


72-73: Data file verified

The required JSON is present at tests/e2e/graph/data/longmemeval_data/longmemeval_oracle.json.

tests/e2e/graph/eval_cli.py (1)

4-4: LGTM!

The import path correctly reflects the relocation of evaluation utilities to tests/e2e/graph/.

tests/integration/core/shared/test_graphium_bootstrap.py (1)

9-9: LGTM!

Importing GraphProvider from its canonical location in graphium_core.driver.driver removes unnecessary indirection and is the correct practice.

tests/helpers/embeddings.py (1)

49-61: Incomplete input type handling in mock embedder.

The mock_embed function handles str and Iterable[str], but EmbedderClient.create also accepts Iterable[int] and Iterable[Iterable[int]] according to the interface. If tests pass integer iterables, the mock will fail.

If integer inputs are expected in tests, extend the mock:

 def mock_embed(input_data: str | Iterable[str]) -> list[float]:
     if isinstance(input_data, str):
         return store[input_data]
+    # Handle list of ints or nested iterables by converting to string
+    if hasattr(input_data, '__iter__') and not isinstance(input_data, str):
+        try:
+            combined_input = ' '.join(str(x) for x in input_data)
+            return store.get(combined_input, store['Alice'])  # fallback
+        except Exception:
+            return store['Alice']  # fallback for unexpected types
-    combined_input = ' '.join(input_data)
-    return store[combined_input]

Alternatively, if integer inputs are never used in practice, this can remain as-is.

Likely an incorrect or invalid review comment.

pytest.ini (1)

3-8: LGTM!

The expanded marker system provides clear test categorization and enables selective execution by provider or test type. The descriptions are helpful and align well with the test restructuring objectives.

tests/integration/neo4j/test_placeholder.py (1)

7-10: LGTM!

The placeholder test is properly marked with integration and neo4j markers and clearly communicates the pending work. This approach enables the test infrastructure to be in place while implementation is in progress.

tests/unit/embedder/test_openai.py (1)

13-13: LGTM!

The change from absolute to relative import is appropriate and cleaner for a module in the same directory.

tests/integration/falkordb/test_placeholder.py (1)

7-10: LGTM!

The placeholder test follows the established pattern and is properly marked with integration and falkordb markers for selective execution.

tests/e2e/graph/test_eval_harness.py (1)

7-9: LGTM!

The e2e placeholder is properly marked and clearly communicates the requirements for running this test suite.

tests/integration/core/shared/test_repository_nodes.py (3)

15-21: LGTM!

The refactoring to use a shared GROUP_ID constant improves test consistency and aligns with the helpers module reorganization.


34-34: LGTM!

The fixtures now use the shared GROUP_ID constant consistently.

Also applies to: 51-51, 66-66


95-95: LGTM!

All test method calls have been updated to use the shared GROUP_ID constant consistently.

Also applies to: 117-117, 145-145, 166-166, 194-194, 215-215

tests/integration/kuzu/test_placeholder.py (1)

7-10: LGTM!

The placeholder test follows the established pattern and is properly marked with integration and kuzu markers.

conftest.py (1)

42-44: Fixture reuse looks great.

Providing mock_embedder via conftest keeps the test surface consistent across tiers and removes the import boilerplate. Nice cleanup.

tests/integration/core/shared/test_repository_edges.py (1)

11-92: Consistent GROUP_ID usage is a win.

Switching everything over to the shared GROUP_ID helper keeps the fixtures and assertions aligned with the rest of the integration suite. Thanks for normalizing this.

.github/workflows/ci.yml (1)

44-45: Targeted CI commands look good.

Running the fast unit tier plus the focused ingestion smoke matches the restructure plan and keeps the workflow lean.

tests/integration/core/shared/test_ingestion_pipeline.py (1)

47-57: Nice consolidation of shared helpers.

Re-routing the constants and helper assertions through tests.helpers.services keeps the ingestion suite aligned with the new helper module.

tests/integration/core/shared/test_entity_exclusion.py (1)

82-347: Great adoption of the shared graph_driver fixture.

Swapping the bespoke driver plumbing for the common fixture simplifies the suite and keeps it aligned with the new integration scaffold.

tests/helpers/services.py (8)

56-73: LGTM: Clean lazy provider detection.

The pattern of checking environment flags and lazily importing provider drivers keeps the module lightweight and avoids import-time failures when optional dependencies are missing. The logic correctly enables providers when their DISABLE_* flag is not set.


75-107: Driver instantiation logic is correct.

The function correctly handles all four supported providers (Neo4j, FalkorDB, Kuzu, Neptune) and loads their settings appropriately.

Note: The static analysis hint (TRY003) about the error message on line 106 is acceptable for test helper code where readability is prioritized.


109-118: LGTM: Clean driver lifecycle management.

Both functions properly handle driver setup and teardown. The prepare_driver function ensures a clean state by clearing test groups before tests run.


120-158: LGTM: Comprehensive CRUD helpers.

The functions provide both type-specific operations (save_nodes, save_edges) and polymorphic helpers (save_all, delete_all) that handle multiple node and edge types. This gives test authors flexibility in how they manage test data.

Note: Static analysis hints (TRY003) on lines 146 and 157 are acceptable for test helper code where explicit error messages aid debugging.


160-186: LGTM: Provider-aware counting queries.

The get_edge_count function correctly handles Kuzu's special edge representation (RelatesToNode_) by using a UNION ALL query. Both count functions properly convert iterable UUIDs to lists for the Cypher query.


188-202: LGTM: Thorough entity node assertions.

The function validates all critical properties including UUID, name, labels, embeddings (with np.allclose for floating-point comparison), and attributes. Properly loads embeddings before comparison.


230-257: LGTM: Comprehensive edge assertions.

Both entity and episodic edge assertion functions properly validate all critical properties. The entity edge function correctly loads embeddings and uses np.allclose for floating-point comparison. Temporal properties and attributes are thoroughly checked.


259-280: LGTM: Clean lazy import pattern.

The resolver functions properly import provider drivers on-demand, avoiding import-time failures when optional dependencies are unavailable. This pattern keeps the test helpers lightweight and allows selective provider testing.


load_dotenv()

os.environ.setdefault('DISABLE_NEPTUNE', 'True')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Reconsider hardcoding DISABLE_NEPTUNE at import time.

Setting this environment variable as a module-level side effect may interfere with tests that want to explicitly enable Neptune. Since _provider_enabled already checks for the absence of the flag, tests can control provider availability through their own environment setup or fixtures.

Consider removing this line and documenting that Neptune is disabled by default unless explicitly enabled:

-os.environ.setdefault('DISABLE_NEPTUNE', 'True')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
os.environ.setdefault('DISABLE_NEPTUNE', 'True')
🤖 Prompt for AI Agents
In tests/helpers/services.py at line 35, remove the module-level side effect
that sets os.environ.setdefault('DISABLE_NEPTUNE', 'True'); instead delete that
line so tests can control Neptune via their own environment setup or fixtures
before importing this module, and add a brief comment or module docstring
stating that Neptune is disabled by default unless tests explicitly set or unset
DISABLE_NEPTUNE (and examples for enabling it via test fixtures or environment
variables).

Comment on lines 22 to 39
def _integration_providers() -> list[GraphProvider]:
providers = services.available_providers()
if not providers:
pytest.skip('No graph providers enabled for integration tests')
return providers


@pytest.fixture(params=_integration_providers())
async def graph_driver(request: pytest.FixtureRequest):
provider: GraphProvider = request.param
marker = PROVIDER_MARKER.get(provider)
if marker:
request.node.add_marker(marker)
driver = await services.prepare_driver(provider)
try:
yield driver
finally:
await services.teardown_driver(driver)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fix import-time skip that breaks CI.

params=_integration_providers() runs at import time; when no graph providers are available (CI sets DISABLE_*='1'), _integration_providers raises pytest.skip, which aborts collection. The GitHub Actions failure (“pytest: No graph providers enabled for integration tests”) comes straight from this change.

Let the fixture parameterize over a sentinel and defer the skip until fixture execution so Pytest can report a normal skip instead of failing the session.

Apply this diff:

 def _integration_providers() -> list[GraphProvider]:
-    providers = services.available_providers()
-    if not providers:
-        pytest.skip('No graph providers enabled for integration tests')
-    return providers
+    providers = services.available_providers()
+    return providers or [None]
 
 
 @pytest.fixture(params=_integration_providers())
 async def graph_driver(request: pytest.FixtureRequest):
-    provider: GraphProvider = request.param
-    marker = PROVIDER_MARKER.get(provider)
-    if marker:
-        request.node.add_marker(marker)
-    driver = await services.prepare_driver(provider)
-    try:
-        yield driver
-    finally:
-        await services.teardown_driver(driver)
+    provider: GraphProvider | None = request.param
+    if provider is None:
+        pytest.skip('No graph providers enabled for integration tests')
+
+    marker = PROVIDER_MARKER.get(provider)
+    if marker:
+        request.node.add_marker(marker)
+
+    driver = await services.prepare_driver(provider)
+    try:
+        yield driver
+    finally:
+        await services.teardown_driver(driver)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _integration_providers() -> list[GraphProvider]:
providers = services.available_providers()
if not providers:
pytest.skip('No graph providers enabled for integration tests')
return providers
@pytest.fixture(params=_integration_providers())
async def graph_driver(request: pytest.FixtureRequest):
provider: GraphProvider = request.param
marker = PROVIDER_MARKER.get(provider)
if marker:
request.node.add_marker(marker)
driver = await services.prepare_driver(provider)
try:
yield driver
finally:
await services.teardown_driver(driver)
def _integration_providers() -> list[GraphProvider]:
providers = services.available_providers()
return providers or [None]
@pytest.fixture(params=_integration_providers())
async def graph_driver(request: pytest.FixtureRequest):
provider: GraphProvider | None = request.param
if provider is None:
pytest.skip('No graph providers enabled for integration tests')
marker = PROVIDER_MARKER.get(provider)
if marker:
request.node.add_marker(marker)
driver = await services.prepare_driver(provider)
try:
yield driver
finally:
await services.teardown_driver(driver)
🧰 Tools
🪛 GitHub Actions: CI

[error] 25-25: pytest: No graph providers enabled for integration tests (Skipped by fixture).

🤖 Prompt for AI Agents
In tests/integration/shared/fixtures_services.py around lines 22 to 39, the
fixture currently evaluates _integration_providers() at import time causing
pytest.skip to run during collection; change params to a single sentinel value
(e.g. SENTINEL = object()) so parametrization happens with that sentinel instead
of calling the providers function at import time, and update the fixture body so
that when request.param is the sentinel it calls services.available_providers(),
calls pytest.skip if the returned list is empty, otherwise iterates over those
providers to prepare/teardown the driver for each provider (or reparametrize via
pytest.skip+pytest.mark if you prefer), ensuring the skip happens inside fixture
execution rather than at import time.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
TEST_RESTRUCTURE_PLAN.md (2)

19-44: Add language identifier to fenced code block.

The directory structure code block should specify a language identifier for proper syntax highlighting and linting compliance.

Apply this diff:

-```
+```text
 tests/
 ├─ unit/

61-69: Add language identifier to fenced code block.

The pytest.ini snippet should specify a language identifier.

Apply this diff:

-```
+```ini
 [pytest]
 markers =
tests/helpers/services.py (1)

106-106: Consider extracting exception messages to constants.

Static analysis flags these lines for specifying long messages directly in exception constructors, which can make exceptions harder to test and maintain.

Consider extracting common error patterns:

+# Exception message templates
+_MSG_UNSUPPORTED_PROVIDER = 'Unsupported provider: {provider!r}'
+_MSG_UNSUPPORTED_TYPE_SAVE = 'Unsupported item type for save_all: {type!r}'
+_MSG_UNSUPPORTED_TYPE_DELETE = 'Unsupported item type for delete_all: {type!r}'
+
...
-    raise ValueError(f'Unsupported provider: {provider!r}')
+    raise ValueError(_MSG_UNSUPPORTED_PROVIDER.format(provider=provider))

Also applies to: 146-146, 157-157

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9f296a6 and ea22fcd.

📒 Files selected for processing (28)
  • .github/workflows/ci.yml (1 hunks)
  • MCP_SMOKE_DEBUG.md (0 hunks)
  • TEST_RESTRUCTURE_PLAN.md (1 hunks)
  • conftest.py (2 hunks)
  • docs/testing.md (3 hunks)
  • pytest.ini (1 hunks)
  • tests/driver/__init__.py (0 hunks)
  • tests/e2e/graph/eval_cli.py (1 hunks)
  • tests/e2e/graph/eval_graph_building.py (6 hunks)
  • tests/e2e/graph/test_eval_harness.py (1 hunks)
  • tests/evals/pytest.ini (0 hunks)
  • tests/helpers/embeddings.py (1 hunks)
  • tests/helpers/services.py (1 hunks)
  • tests/helpers_test.py (0 hunks)
  • tests/integration/conftest.py (1 hunks)
  • tests/integration/core/shared/test_entity_exclusion.py (5 hunks)
  • tests/integration/core/shared/test_graphium_bootstrap.py (1 hunks)
  • tests/integration/core/shared/test_ingestion_pipeline.py (2 hunks)
  • tests/integration/core/shared/test_repository_edges.py (18 hunks)
  • tests/integration/core/shared/test_repository_nodes.py (9 hunks)
  • tests/integration/falkordb/test_placeholder.py (1 hunks)
  • tests/integration/kuzu/test_placeholder.py (1 hunks)
  • tests/integration/neo4j/test_placeholder.py (1 hunks)
  • tests/integration/shared/fixtures_services.py (1 hunks)
  • tests/unit/embedder/test_gemini.py (1 hunks)
  • tests/unit/embedder/test_openai.py (1 hunks)
  • tests/unit/embedder/test_voyage.py (1 hunks)
  • tests/unit/search/test_lucene_utils.py (1 hunks)
💤 Files with no reviewable changes (4)
  • tests/evals/pytest.ini
  • tests/helpers_test.py
  • tests/driver/init.py
  • MCP_SMOKE_DEBUG.md
🧰 Additional context used
📓 Path-based instructions (4)
tests/**

📄 CodeRabbit inference engine (AGENTS.md)

Author tests under tests/ alongside features

Files:

  • tests/unit/embedder/test_openai.py
  • tests/integration/falkordb/test_placeholder.py
  • tests/e2e/graph/test_eval_harness.py
  • tests/integration/core/shared/test_repository_edges.py
  • tests/unit/search/test_lucene_utils.py
  • tests/unit/embedder/test_gemini.py
  • tests/helpers/embeddings.py
  • tests/unit/embedder/test_voyage.py
  • tests/e2e/graph/eval_cli.py
  • tests/integration/conftest.py
  • tests/integration/core/shared/test_graphium_bootstrap.py
  • tests/e2e/graph/eval_graph_building.py
  • tests/integration/kuzu/test_placeholder.py
  • tests/integration/neo4j/test_placeholder.py
  • tests/integration/core/shared/test_repository_nodes.py
  • tests/integration/shared/fixtures_services.py
  • tests/helpers/services.py
  • tests/integration/core/shared/test_entity_exclusion.py
  • tests/integration/core/shared/test_ingestion_pipeline.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indentation, 100-character line length, and prefer single quotes for Python code (as configured in pyproject.toml)
Use snake_case for module, file, and function names in Python

Files:

  • tests/unit/embedder/test_openai.py
  • tests/integration/falkordb/test_placeholder.py
  • tests/e2e/graph/test_eval_harness.py
  • tests/integration/core/shared/test_repository_edges.py
  • tests/unit/search/test_lucene_utils.py
  • tests/unit/embedder/test_gemini.py
  • conftest.py
  • tests/helpers/embeddings.py
  • tests/unit/embedder/test_voyage.py
  • tests/e2e/graph/eval_cli.py
  • tests/integration/conftest.py
  • tests/integration/core/shared/test_graphium_bootstrap.py
  • tests/e2e/graph/eval_graph_building.py
  • tests/integration/kuzu/test_placeholder.py
  • tests/integration/neo4j/test_placeholder.py
  • tests/integration/core/shared/test_repository_nodes.py
  • tests/integration/shared/fixtures_services.py
  • tests/helpers/services.py
  • tests/integration/core/shared/test_entity_exclusion.py
  • tests/integration/core/shared/test_ingestion_pipeline.py
pytest.ini

📄 CodeRabbit inference engine (AGENTS.md)

Maintain pytest configuration in pytest.ini at the repo root

Files:

  • pytest.ini
conftest.py

📄 CodeRabbit inference engine (AGENTS.md)

Pytest configuration should reside in repository-level conftest.py

Files:

  • conftest.py
🧠 Learnings (2)
📚 Learning: 2025-10-11T16:59:48.957Z
Learnt from: CR
PR: CaliLuke/graphium#0
File: AGENTS.md:0-0
Timestamp: 2025-10-11T16:59:48.957Z
Learning: Applies to tests/*_int.py : Integration tests live in tests/*_int.py and must be marked with pytest.mark.integration

Applied to files:

  • pytest.ini
  • docs/testing.md
📚 Learning: 2025-10-11T16:59:48.957Z
Learnt from: CR
PR: CaliLuke/graphium#0
File: AGENTS.md:0-0
Timestamp: 2025-10-11T16:59:48.957Z
Learning: Use pytest.mark.integration to gate database-reliant scenarios in CI

Applied to files:

  • pytest.ini
🧬 Code graph analysis (15)
tests/unit/embedder/test_openai.py (1)
tests/unit/embedder/embedder_fixtures.py (1)
  • create_embedding_values (5-7)
tests/integration/core/shared/test_repository_edges.py (2)
tests/helpers/services.py (2)
  • get_edge_count (172-185)
  • get_node_count (160-169)
graphium_core/repository/node_repository.py (1)
  • get_by_entity_node_uuid (360-376)
tests/unit/embedder/test_gemini.py (1)
tests/unit/embedder/embedder_fixtures.py (1)
  • create_embedding_values (5-7)
conftest.py (1)
tests/helpers/embeddings.py (1)
  • make_mock_embedder (49-61)
tests/helpers/embeddings.py (3)
graphium_core/embedder/client.py (1)
  • EmbedderClient (18-26)
tests/unit/orchestration/test_initializer_factory.py (1)
  • create (68-69)
tests/unit/search/test_search_helpers.py (4)
  • create (26-27)
  • create (34-35)
  • create (43-44)
  • create (97-99)
tests/unit/embedder/test_voyage.py (1)
tests/unit/embedder/embedder_fixtures.py (1)
  • create_embedding_values (5-7)
tests/e2e/graph/eval_cli.py (1)
tests/e2e/graph/eval_graph_building.py (2)
  • build_baseline_graph (98-115)
  • eval_graph (118-173)
tests/integration/conftest.py (1)
tests/integration/shared/fixtures_services.py (1)
  • graph_driver (32-43)
tests/integration/core/shared/test_graphium_bootstrap.py (1)
graphium_core/driver/driver.py (1)
  • GraphProvider (32-36)
tests/e2e/graph/eval_graph_building.py (2)
graphium_core/settings.py (1)
  • Neo4jSettings (277-280)
graphium_core/graphium.py (1)
  • Graphium (40-235)
tests/integration/core/shared/test_repository_nodes.py (2)
tests/helpers/services.py (4)
  • assert_community_node_equals (204-215)
  • assert_entity_node_equals (188-201)
  • assert_episodic_node_equals (218-227)
  • get_node_count (160-169)
graphium_core/repository/node_repository.py (1)
  • delete_by_group_id (110-166)
tests/integration/shared/fixtures_services.py (2)
graphium_core/driver/driver.py (1)
  • GraphProvider (32-36)
tests/helpers/services.py (3)
  • available_providers (56-72)
  • prepare_driver (109-113)
  • teardown_driver (116-117)
tests/helpers/services.py (7)
graphium_core/driver/driver.py (3)
  • GraphDriver (70-119)
  • GraphProvider (32-36)
  • repositories (115-119)
graphium_core/edges.py (3)
  • CommunityEdge (111-112)
  • EntityEdge (47-108)
  • EpisodicEdge (43-44)
graphium_core/nodes.py (3)
  • CommunityNode (152-163)
  • EntityNode (91-149)
  • EpisodicNode (78-88)
graphium_core/orchestration/maintenance/graph_data_operations.py (1)
  • clear_data (218-242)
graphium_core/settings.py (4)
  • FalkorSettings (283-290)
  • KuzuSettings (299-300)
  • Neo4jSettings (277-280)
  • NeptuneSettings (293-296)
graphium_core/repository/graph_repository.py (4)
  • delete_node (65-66)
  • delete_edge (75-76)
  • save_node (62-63)
  • save_edge (72-73)
graphium_core/repository/edge_repository.py (1)
  • load_fact_embedding (421-452)
tests/integration/core/shared/test_entity_exclusion.py (2)
tests/integration/shared/fixtures_services.py (1)
  • graph_driver (32-43)
graphium_core/graphium.py (1)
  • Graphium (40-235)
tests/integration/core/shared/test_ingestion_pipeline.py (2)
graphium_core/driver/driver.py (1)
  • GraphProvider (32-36)
tests/helpers/services.py (7)
  • assert_entity_edge_equals (230-248)
  • assert_entity_node_equals (188-201)
  • assert_episodic_edge_equals (251-256)
  • assert_episodic_node_equals (218-227)
  • delete_all (149-157)
  • get_edge_count (172-185)
  • get_node_count (160-169)
🪛 markdownlint-cli2 (0.18.1)
TEST_RESTRUCTURE_PLAN.md

19-19: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


61-61: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🪛 Ruff (0.13.3)
tests/integration/conftest.py

6-6: Unused noqa directive (non-enabled: F401)

Remove unused noqa directive

(RUF100)

tests/helpers/services.py

106-106: Avoid specifying long messages outside the exception class

(TRY003)


146-146: Avoid specifying long messages outside the exception class

(TRY003)


157-157: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (28)
tests/unit/search/test_lucene_utils.py (1)

1-17: LGTM! Comprehensive unit test for Lucene sanitization.

The test effectively validates both the escaping of special characters and the pass-through of plain text. The test data covers all Lucene special characters, ensuring thorough coverage of the sanitization logic.

.github/workflows/ci.yml (1)

44-45: LGTM! CI test paths updated correctly.

The test paths have been updated to match the new unit/integration structure. The selected tests provide good coverage while remaining fast enough for CI execution.

tests/e2e/graph/eval_cli.py (1)

4-4: LGTM! Import path updated for test restructuring.

The import path correctly reflects the new e2e test organization under tests/e2e/graph/.

tests/unit/embedder/test_openai.py (1)

12-13: LGTM! Relative import improves test package organization.

Using relative imports within the embedder test package makes the code more maintainable and follows Python best practices for intra-package imports.

tests/integration/conftest.py (1)

6-6: LGTM! Fixture import is correct despite static analysis warning.

The noqa: F401 is intentionally used here to suppress unused import warnings for the graph_driver fixture. This is a standard pytest pattern where fixtures are imported in conftest.py to make them available to all tests in the directory tree. The Ruff RUF100 warning is a false positive in this case.

tests/e2e/graph/test_eval_harness.py (1)

7-9: LGTM! Placeholder test documents e2e requirements.

The placeholder test with a skip message is a good approach for documenting that eval harness tests require manual orchestration. This preserves the test structure while clearly communicating the manual setup requirement.

tests/integration/core/shared/test_ingestion_pipeline.py (2)

11-11: LGTM! GraphProvider import updated correctly.

The import path now correctly references graphium_core.driver.driver for the GraphProvider enum.


47-60: LGTM! Test helper imports and aliases updated correctly.

The import paths now correctly reference tests.helpers.services, and the GROUP_ID constants are appropriately aliased to lowercase names for local usage, following Python naming conventions.

tests/integration/core/shared/test_graphium_bootstrap.py (1)

9-9: LGTM! GraphProvider import updated correctly.

The import path now correctly references graphium_core.driver.driver, consistent with other integration test files in this PR.

tests/unit/embedder/test_gemini.py (1)

12-12: LGTM! Relative import aligns with package structure.

The change from absolute to relative import is appropriate for fixtures within the same test package.

tests/unit/embedder/test_voyage.py (1)

13-13: LGTM! Consistent relative import pattern.

The relative import matches the pattern established in other unit tests within this package.

tests/integration/core/shared/test_repository_nodes.py (3)

15-21: LGTM! Standardized constant usage improves consistency.

The migration from a local variable to the shared GROUP_ID constant from tests.helpers.services provides consistent test data across integration tests.


34-34: LGTM! Fixtures correctly use the shared constant.

All sample node fixtures consistently use GROUP_ID, ensuring uniform test data.

Also applies to: 51-51, 66-66


95-95: LGTM! Test assertions properly use the standardized constant.

All get_by_group_ids and delete_by_group_id calls correctly reference GROUP_ID, maintaining consistency with the fixtures.

Also applies to: 117-117, 145-145, 166-166, 194-194, 215-215

pytest.ini (1)

3-8: LGTM! Enhanced marker system supports selective test execution.

The updated integration marker description and new provider-specific markers (neo4j, falkordb, kuzu, neptune, e2e) enable granular test selection in CI and local development, aligning with the test restructuring objectives.

Based on learnings

tests/integration/kuzu/test_placeholder.py (1)

1-10: LGTM! Placeholder structure enables future Kuzu-specific tests.

The placeholder test with @pytest.mark.integration and @pytest.mark.kuzu markers correctly reserves space for Kuzu-backed integration tests and aligns with the new marker system defined in pytest.ini.

tests/integration/neo4j/test_placeholder.py (1)

1-10: LGTM! Neo4j placeholder follows established pattern.

The placeholder test structure with appropriate markers (@pytest.mark.integration and @pytest.mark.neo4j) is consistent with other provider placeholders and integrates with the expanded marker system.

conftest.py (2)

15-15: LGTM! Centralized embedding helper improves maintainability.

The import of make_mock_embedder from the new tests.helpers.embeddings module consolidates embedding test utilities.


17-17: graph_driver fixture available in integration tests Verified definition in tests/integration/shared/fixtures_services.py (line 32).

tests/e2e/graph/eval_graph_building.py (2)

6-6: LGTM! Modernized path and settings management.

Introducing pathlib.Path for filesystem operations and sourcing Neo4j connection parameters from Neo4jSettings improves maintainability and follows Python best practices. The BASE_DIR and DATA_DIR constants provide clear, reusable path references.

Also applies to: 19-24


72-73: LGTM! Consistent Path usage throughout the file.

All file operations now use Path-based paths (DATA_DIR / ..., BASE_DIR / ...) instead of string concatenation, ensuring cross-platform compatibility and cleaner code.

Also applies to: 101-101, 107-107, 121-122, 133-133

tests/integration/core/shared/test_repository_edges.py (1)

11-11: LGTM! Consolidation of test constants.

The import of GROUP_ID from the new centralized tests.helpers.services module improves maintainability by eliminating hardcoded group IDs throughout the test suite.

tests/integration/falkordb/test_placeholder.py (1)

7-10: LGTM! Provider-specific integration placeholder.

The placeholder test correctly uses integration and provider-specific markers, consistent with the new test organization strategy outlined in the PR.

docs/testing.md (1)

21-23: LGTM! Documentation accurately reflects new test organization.

The updated test layout description correctly documents the separation of unit, integration, and e2e tests with appropriate markers.

tests/integration/core/shared/test_entity_exclusion.py (1)

82-90: LGTM! Consistent fixture usage.

The migration from parameterized driver fixtures to the centralized graph_driver fixture improves test consistency and maintainability across the integration test suite.

tests/helpers/services.py (1)

56-72: LGTM! Flexible provider availability checking.

The lazy provider resolution approach correctly avoids import-time side effects while still enabling environment-driven test configuration.

tests/helpers/embeddings.py (1)

54-58: Iterable branch unused in mock_embed; no change required.

tests/integration/shared/fixtures_services.py (1)

36-38: Use pytest.mark decorators instead of string markers
Passing a string to request.node.add_marker doesn’t register markers. Update as follows in tests/integration/shared/fixtures_services.py:

-    marker = PROVIDER_MARKER.get(provider)
-    if marker:
-        request.node.add_marker(marker)
+    marker_name = PROVIDER_MARKER.get(provider)
+    if marker_name:
+        request.node.add_marker(getattr(pytest.mark, marker_name))

Likely an incorrect or invalid review comment.


load_dotenv()

os.environ.setdefault('DISABLE_NEPTUNE', 'True')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Review environment variable override behavior.

Setting DISABLE_NEPTUNE=True after load_dotenv() will override any user-specified value in .env. If the intent is to provide a default only when unset, use os.environ.setdefault() before load_dotenv(), or check if the variable exists before setting it.

If this is intentional (forcing Neptune to be disabled by default), consider documenting the behavior. Otherwise, apply this diff:

-load_dotenv()
-
 os.environ.setdefault('DISABLE_NEPTUNE', 'True')
+
+load_dotenv()

This ensures the default is set only if not already present in the environment, and .env values take precedence.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
os.environ.setdefault('DISABLE_NEPTUNE', 'True')
os.environ.setdefault('DISABLE_NEPTUNE', 'True')
load_dotenv()
🤖 Prompt for AI Agents
In tests/helpers/services.py around line 35, the code unconditionally sets
DISABLE_NEPTUNE after load_dotenv which overrides any .env value; change this to
only set a default when the variable is absent by either moving
os.environ.setdefault('DISABLE_NEPTUNE','True') to before load_dotenv or by
checking if 'DISABLE_NEPTUNE' not in os.environ before assigning, so .env values
take precedence (or document intentional forced override if that was desired).

@CaliLuke
Copy link
Owner Author

Addressed the actionable review notes:

  • Added language tags to the fenced blocks in TEST_RESTRUCTURE_PLAN.md (commit 750f4e7).
  • Swapped the Lucene sanitize test over to a parametrized form and seeded the helper embeddings for deterministic runs (commit 750f4e7).
  • Introduced shared error-message constants and compared helper assertions against sample.group_id so they work with alternate groups (commits 750f4e7, ea4941a).
  • Updated the integration fixture to yield a sentinel when providers are disabled and to attach provider marks via pytest.mark.<name> (commit ea22fcd). CI now sees a skip instead of aborting.
  • Kept the DISABLE_NEPTUNE default but noted it still honours any value supplied in the environment before import; tests can override it by setting the variable prior to loading the helpers module.

Let me know if anything else needs tweaking!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
tests/integration/shared/fixtures_services.py (1)

31-43: Well-designed fixture orchestration.

The fixture correctly orchestrates per-provider graph driver setup and teardown. The implementation properly adds provider-specific markers, prepares the driver, yields it for test use, and ensures cleanup via the finally block.

The defensive check at lines 34-35 is appropriate even though pytest.param with skip mark should prevent fixture execution, as it guards against edge cases.

For improved type safety with strict type checkers, consider adding an assertion after the skip check:

provider = request.param
if provider is None:
    pytest.skip(_NO_PROVIDER_MSG)
assert provider is not None  # Type guard for stricter type checkers

marker_name = PROVIDER_MARKER.get(provider)
# ...

This helps type checkers understand that provider cannot be None after the skip check (though pytest.skip() already raises an exception, so this is optional).

tests/unit/search/test_lucene_utils.py (2)

6-6: Consider simplifying the import.

The alias lucene_sanitize is clear but adds little value since the module path already indicates it's from Lucene. You could simplify to:

-from graphium_core.search.lucene import sanitize as lucene_sanitize
+from graphium_core.search.lucene import sanitize

And then update line 20 to use sanitize(query).

Also note: there are excessive blank lines between imports (lines 5 and 7-8). PEP 8 recommends one blank line between import groups, but since there's only one import from graphium_core, the extra spacing is unnecessary.


9-20: Consider adding edge case tests.

The current test coverage is good for the happy path, but consider adding tests for:

  • Empty string: ('', '')
  • String with only special characters: ('+-()[]{}', '\\+\\-\\(\\)\\[\\]\\{\\}')
  • Consecutive special characters without spaces
  • Leading/trailing special characters

These additional tests would improve confidence in the sanitize function's robustness.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea22fcd and ea4941a.

📒 Files selected for processing (5)
  • TEST_RESTRUCTURE_PLAN.md (1 hunks)
  • tests/helpers/embeddings.py (1 hunks)
  • tests/helpers/services.py (1 hunks)
  • tests/integration/shared/fixtures_services.py (1 hunks)
  • tests/unit/search/test_lucene_utils.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/helpers/embeddings.py
  • tests/helpers/services.py
🧰 Additional context used
📓 Path-based instructions (2)
tests/**

📄 CodeRabbit inference engine (AGENTS.md)

Author tests under tests/ alongside features

Files:

  • tests/unit/search/test_lucene_utils.py
  • tests/integration/shared/fixtures_services.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indentation, 100-character line length, and prefer single quotes for Python code (as configured in pyproject.toml)
Use snake_case for module, file, and function names in Python

Files:

  • tests/unit/search/test_lucene_utils.py
  • tests/integration/shared/fixtures_services.py
🧬 Code graph analysis (1)
tests/integration/shared/fixtures_services.py (2)
graphium_core/driver/driver.py (1)
  • GraphProvider (32-36)
tests/helpers/services.py (3)
  • available_providers (60-76)
  • prepare_driver (113-117)
  • teardown_driver (120-121)
🔇 Additional comments (5)
TEST_RESTRUCTURE_PLAN.md (1)

1-442: Well-structured test restructure plan.

The document provides comprehensive guidance for reorganizing tests into unit, integration, and e2e tiers. The goals, pain points, directory layout, execution strategy, and migration roadmap are clearly defined and will serve as an effective reference during implementation.

Minor note: The pytest.ini snippet (lines 61-69) doesn't include a neptune marker, but tests/integration/shared/fixtures_services.py includes Neptune support in PROVIDER_MARKER. Consider adding neptune: requires a running Neptune instance to the markers list when updating the actual pytest.ini configuration.

tests/integration/shared/fixtures_services.py (2)

14-19: LGTM!

The provider-to-marker mapping is correct and follows pytest naming conventions. All four providers (NEO4J, FALKORDB, KUZU, NEPTUNE) are properly mapped to their corresponding marker strings.


24-28: Correctly defers skip to test execution time.

The function properly addresses the past review concern by returning pytest.param(None, marks=pytest.mark.skip(...)) instead of calling pytest.skip() at import time. This ensures that collection succeeds even when no providers are available, with the skip happening during test execution.

tests/unit/search/test_lucene_utils.py (2)

1-3: LGTM!

Standard license header with appropriate modification notice.


12-15: Remove unintended letter mappings from sanitize
sanitize’s escape_map currently includes 'O','R','N','T','A','D', so non-special letters (e.g. T) get escaped and the test is correct. Remove those entries from escape_map in graphium_core/search/lucene.py so only true Lucene special characters are escaped.

Likely an incorrect or invalid review comment.

@CaliLuke CaliLuke merged commit f43ef4b into main Oct 12, 2025
1 check passed
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/helpers/services.py (2)

62-78: Simplify provider checks after fixing the type hint.

Following the type hint correction in _import_if_available, the truthiness checks on lines 66, 69, 72, and 76 become unnecessary since the function either returns a driver class or raises an exception.

After applying the previous type hint fix, apply this diff:

 def available_providers() -> list[GraphProvider]:
     """Return the graph providers that can be exercised for integration tests."""
     providers: list[GraphProvider] = []
     if _provider_enabled('DISABLE_NEO4J'):
-        if _import_if_available(GraphProvider.NEO4J, _resolve_neo4j_driver):
-            providers.append(GraphProvider.NEO4J)
+        _import_if_available(GraphProvider.NEO4J, _resolve_neo4j_driver)
+        providers.append(GraphProvider.NEO4J)
     if _provider_enabled('DISABLE_FALKORDB'):
-        if _import_if_available(GraphProvider.FALKORDB, _resolve_falkordb_driver):
-            providers.append(GraphProvider.FALKORDB)
+        _import_if_available(GraphProvider.FALKORDB, _resolve_falkordb_driver)
+        providers.append(GraphProvider.FALKORDB)
     if _provider_enabled('DISABLE_KUZU'):
-        if _import_if_available(GraphProvider.KUZU, _resolve_kuzu_driver):
-            providers.append(GraphProvider.KUZU)
+        _import_if_available(GraphProvider.KUZU, _resolve_kuzu_driver)
+        providers.append(GraphProvider.KUZU)
     if _provider_enabled('DISABLE_NEPTUNE'):
-        driver = _import_if_available(GraphProvider.NEPTUNE, _resolve_neptune_driver)
-        if driver:
-            providers.append(GraphProvider.NEPTUNE)
+        _import_if_available(GraphProvider.NEPTUNE, _resolve_neptune_driver)
+        providers.append(GraphProvider.NEPTUNE)
     return providers

81-112: Remove redundant int() casts.

Lines 96 and 109 cast settings.port to int, but the settings classes already declare port as int (per FalkorSettings and NeptuneSettings). While harmless, these casts are unnecessary.

Apply this diff if you want to remove the redundancy:

     if provider == GraphProvider.FALKORDB:
         settings = FalkorSettings()
         driver_cls = _resolve_falkordb_driver()
         return driver_cls(
             host=settings.host,
-            port=int(settings.port),
+            port=settings.port,
             username=settings.username,
             password=settings.password,
         )
     
     ...
     
     if provider == GraphProvider.NEPTUNE:
         settings = NeptuneSettings()
         driver_cls = _resolve_neptune_driver()
         return driver_cls(
             host=settings.host or 'localhost',
-            port=int(settings.port),
+            port=settings.port,
             aoss_host=settings.aoss_host,
         )
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ea4941a and 3d69a2e.

📒 Files selected for processing (2)
  • tests/helpers/services.py (1 hunks)
  • tests/integration/conftest.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/integration/conftest.py
🧰 Additional context used
📓 Path-based instructions (2)
tests/**

📄 CodeRabbit inference engine (AGENTS.md)

Author tests under tests/ alongside features

Files:

  • tests/helpers/services.py
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indentation, 100-character line length, and prefer single quotes for Python code (as configured in pyproject.toml)
Use snake_case for module, file, and function names in Python

Files:

  • tests/helpers/services.py
🧬 Code graph analysis (1)
tests/helpers/services.py (6)
graphium_core/driver/driver.py (3)
  • GraphDriver (70-119)
  • GraphProvider (32-36)
  • repositories (115-119)
graphium_core/nodes.py (3)
  • CommunityNode (152-163)
  • EntityNode (91-149)
  • EpisodicNode (78-88)
graphium_core/orchestration/maintenance/graph_data_operations.py (1)
  • clear_data (218-242)
graphium_core/settings.py (4)
  • FalkorSettings (283-290)
  • KuzuSettings (299-300)
  • Neo4jSettings (277-280)
  • NeptuneSettings (293-296)
graphium_core/repository/graph_repository.py (4)
  • delete_node (65-66)
  • delete_edge (75-76)
  • save_node (62-63)
  • save_edge (72-73)
graphium_core/repository/edge_repository.py (1)
  • load_fact_embedding (421-452)
🔇 Additional comments (7)
tests/helpers/services.py (7)

1-44: LGTM! Module setup is well-structured.

The environment variable initialization order is correct: existing environment values and .env settings take precedence, with DISABLE_NEPTUNE=True serving only as a fallback default. The module docstring accurately documents this behavior. Using constants for error messages ensures consistency across the module.


115-123: LGTM! Driver lifecycle management is clean.

The prepare_driver and teardown_driver functions correctly handle driver provisioning and cleanup. Clearing specific test groups before tests run prevents cross-contamination between test runs.


126-141: LGTM! Save and delete helpers correctly use available repository APIs.

The batch operations for saves and individual operations for deletes reflect the underlying repository API design. The implementation is clean and correct.


144-167: LGTM! Generic save/delete helpers with proper type checking.

The save_all and delete_all functions correctly dispatch to the appropriate repository methods based on instance type. Using shared error message constants ensures consistency.


170-195: LGTM! Count helpers correctly handle provider-specific edge models.

The get_edge_count function's UNION ALL query correctly accounts for Kuzu's hybrid edge representation (both relationship edges and RelatesToNode_ nodes), while remaining compatible with other providers.


198-267: LGTM! Assertion helpers correctly compare against sample properties.

All five assertion functions now properly compare retrieved.group_id against sample.group_id (lines 204, 220, 231, 245, 263), addressing the concerns from previous reviews. The embedding comparisons using np.allclose are appropriate for floating-point arrays.


269-290: LGTM! Lazy resolver functions follow consistent pattern.

The private resolver functions correctly implement lazy loading by importing driver classes only when called. This approach keeps test collection fast and avoids unnecessary imports, as documented in the module docstring.

Comment on lines +51 to +59
def _import_if_available(
provider: GraphProvider,
import_factory: Callable[[], type[GraphDriver]],
) -> type[GraphDriver] | None:
try:
return import_factory()
except ImportError as exc:
msg = f'Provider {provider.value} is enabled but unavailable: {exc}'
raise ImportError(msg) from exc
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Type hint doesn't match implementation behavior.

The function is typed to return type[GraphDriver] | None, but the implementation always raises ImportError on failure and never returns None. This causes callers to perform redundant truthiness checks.

Apply this diff to make the type hint accurate:

 def _import_if_available(
     provider: GraphProvider,
     import_factory: Callable[[], type[GraphDriver]],
-) -> type[GraphDriver] | None:
+) -> type[GraphDriver]:
     try:
         return import_factory()
     except ImportError as exc:
         msg = f'Provider {provider.value} is enabled but unavailable: {exc}'
         raise ImportError(msg) from exc
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _import_if_available(
provider: GraphProvider,
import_factory: Callable[[], type[GraphDriver]],
) -> type[GraphDriver] | None:
try:
return import_factory()
except ImportError as exc:
msg = f'Provider {provider.value} is enabled but unavailable: {exc}'
raise ImportError(msg) from exc
def _import_if_available(
provider: GraphProvider,
import_factory: Callable[[], type[GraphDriver]],
) -> type[GraphDriver]:
try:
return import_factory()
except ImportError as exc:
msg = f'Provider {provider.value} is enabled but unavailable: {exc}'
raise ImportError(msg) from exc
🤖 Prompt for AI Agents
In tests/helpers/services.py around lines 51 to 59, the function
_import_if_available is annotated to return type[GraphDriver] | None but in
practice it never returns None and instead raises ImportError on failure; update
the function signature to return type[GraphDriver] (remove the | None) so the
annotation matches behavior and remove any redundant None-handling in callers if
present.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants