This is an automated email from the ASF dual-hosted git repository. imbajin pushed a commit to branch goal-test in repository https://gitbox.apache.org/repos/asf/hugegraph-ai.git
commit d9a6aaaaa2ded01766cb48e2384777d7f2802352 Author: imbajin <[email protected]> AuthorDate: Sun May 31 02:05:51 2026 +0800 test(llm): harden hugegraph boundary coverage - add real HugeGraph boundary tests for schema, write, read, and gremlin paths - fix primary-key edge endpoint mapping in Commit2Graph - record LLM boundary failures and regression evidence --- .../quality-program/checkpoints/04-llm-boundary.md | 79 ++++++++ .workflow/quality-program/quality-state.json | 41 +++- .../reports/production-change-ledger.md | 1 + .../2026-05-31-hugegraph-ai-quality-program.md | 16 +- .../operators/hugegraph_op/commit_to_hugegraph.py | 16 +- hugegraph-llm/src/tests/conftest.py | 1 + .../tests/integration/test_hugegraph_boundary.py | 225 +++++++++++++++++++++ .../hugegraph_op/test_commit_to_hugegraph.py | 23 +++ .../hugegraph_op/test_fetch_graph_data.py | 4 + .../operators/hugegraph_op/test_schema_manager.py | 4 + 10 files changed, 386 insertions(+), 24 deletions(-) diff --git a/.workflow/quality-program/checkpoints/04-llm-boundary.md b/.workflow/quality-program/checkpoints/04-llm-boundary.md new file mode 100644 index 00000000..6f0ae38b --- /dev/null +++ b/.workflow/quality-program/checkpoints/04-llm-boundary.md @@ -0,0 +1,79 @@ +# G3 LLM HugeGraph Boundary Checkpoint + +## Status + +Complete. + +## Completed Scope + +- Added a real HugeGraph boundary test module for `hugegraph-llm`. +- Covered production `SchemaManager` against live schema readback. +- Covered production `Commit2Graph` write path against live HugeGraph Server `1.7.0`, including vertex count, edge count, edge source, and edge target. +- Covered production `FetchGraphData` against live graph data and stable summary shape. +- Covered production `GremlinExecuteNode` invalid-query error surface without requiring LLM, embedding, reranker, vector DB, or UI credentials. +- Marked existing `hugegraph_op` unittest files as Layer A `unit` so strict marker selection does not silently deselect the suite. +- Made the `hugegraph-llm` test harness root-command friendly by switching test cwd to the module root before production config import. + +## Red Tests Observed + +```text +TestCommit2Graph.test_load_into_graph_raises_explicit_error_when_vertex_creation_fails + failed with AttributeError: 'NoneType' object has no attribute 'id' + +test_commit_to_graph_writes_vertices_and_edges + failed with Server Exception: Invalid vertex id 'quality_person:marko' +``` + +## Production Fix + +| File | Change | Proving test | +|---|---|---| +| `hugegraph-llm/src/hugegraph_llm/operators/hugegraph_op/commit_to_hugegraph.py` | Map primary-key `label:value` edge endpoints to server-created VIDs and raise an explicit `ValueError` when vertex creation returns `None`. | `uv run pytest hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py::TestCommit2Graph::test_load_into_graph_raises_explicit_error_when_vertex_creation_fails -q`; `HUGEGRAPH_REQUIRED=true uv run pytest hugegraph-llm/src/tests/integration/test_hu [...] + +## Commands Run + +```bash +uv run pytest hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py::TestCommit2Graph::test_load_into_graph_raises_explicit_error_when_vertex_creation_fails -q +HUGEGRAPH_REQUIRED=true uv run pytest hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py -v --tb=short --maxfail=1 +uv run pytest hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py::TestCommit2Graph::test_load_into_graph_raises_explicit_error_when_vertex_creation_fails hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py::TestCommit2Graph::test_load_into_graph_maps_llm_vertex_ids_to_created_vertex_ids -q +HUGEGRAPH_REQUIRED=true uv run pytest hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py -v --tb=short +uv run pytest hugegraph-llm/src/tests/operators/hugegraph_op -m "unit or contract" -q +uv run ruff format --check hugegraph-llm/src/tests/conftest.py hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py hugegraph-llm/src/tests/operators/hugegraph_op/test_schema_manager.py hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py hugegraph-llm/src/tests/operators/hugegraph_op/test_fetch_graph_data.py hugegraph-llm/src/hugegraph_llm/operators/hugegraph_op/commit_to_hugegraph.py +uv run ruff check hugegraph-llm/src/tests/conftest.py hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py hugegraph-llm/src/tests/operators/hugegraph_op/test_schema_manager.py hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py hugegraph-llm/src/tests/operators/hugegraph_op/test_fetch_graph_data.py hugegraph-llm/src/hugegraph_llm/operators/hugegraph_op/commit_to_hugegraph.py +uv run pytest hugegraph-llm/src/tests --collect-only -q +git diff --check +``` + +## Verification Result + +| Layer | Command | Result | +|---|---|---| +| LLM HugeGraph operator unit/contract | `uv run pytest hugegraph-llm/src/tests/operators/hugegraph_op -m "unit or contract" -q` | `43 passed` | +| LLM HugeGraph boundary integration | `HUGEGRAPH_REQUIRED=true uv run pytest hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py -v --tb=short` | `4 passed` | +| LLM collection from repo root | `uv run pytest hugegraph-llm/src/tests --collect-only -q` | `281 tests collected` | +| Formatting | `uv run ruff format --check ...` | `6 files already formatted` | +| Lint | `uv run ruff check ...` | `All checks passed` | +| Whitespace | `git diff --check` | passed | + +## Failure Classification + +- `Commit2Graph` primary-key endpoint mapping was an LLM conversion boundary gap. +- `Commit2Graph` `NoneType.id` was a failure-surface contract gap. +- No HugeGraph service setup, server contract, real LLM, embedding, reranker, vector DB, or UI credential dependency blocked G3. + +## Files Touched + +- `.workflow/quality-program/checkpoints/04-llm-boundary.md` +- `.workflow/quality-program/quality-state.json` +- `.workflow/quality-program/reports/production-change-ledger.md` +- `docs/superpowers/plans/2026-05-31-hugegraph-ai-quality-program.md` +- `hugegraph-llm/src/hugegraph_llm/operators/hugegraph_op/commit_to_hugegraph.py` +- `hugegraph-llm/src/tests/conftest.py` +- `hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py` +- `hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py` +- `hugegraph-llm/src/tests/operators/hugegraph_op/test_fetch_graph_data.py` +- `hugegraph-llm/src/tests/operators/hugegraph_op/test_schema_manager.py` + +## Next Goal Readiness + +G4 can begin. The current test harness supports root-level LLM collection, and no G3 boundary blocker remains. diff --git a/.workflow/quality-program/quality-state.json b/.workflow/quality-program/quality-state.json index 5b85a147..fd83550b 100644 --- a/.workflow/quality-program/quality-state.json +++ b/.workflow/quality-program/quality-state.json @@ -1,5 +1,5 @@ { - "current_goal": "G3", + "current_goal": "G4", "repo_sha_start": "d048bc9fa07042833549dd1def5ff3222c47be70", "base_branch": "main", "open_pr_snapshot_time": "2026-05-30T17:18:30Z", @@ -7,7 +7,8 @@ "P0", "G0", "G1", - "G2" + "G2", + "G3" ], "files_touched": [ ".workflow/quality-program/README.md", @@ -60,10 +61,16 @@ "docs/quality/hugegraph-integration.md", ".workflow/quality-program/checkpoints/02-service-fixture.md", ".workflow/quality-program/checkpoints/03-client-contract.md", + ".workflow/quality-program/checkpoints/04-llm-boundary.md", ".workflow/quality-program/coverage/client-g2.json", ".workflow/quality-program/reports/production-change-ledger.md", "docs/superpowers/plans/2026-05-31-hugegraph-ai-quality-program.md", - "hugegraph-python-client/src/pyhugegraph/utils/util.py" + "hugegraph-python-client/src/pyhugegraph/utils/util.py", + "hugegraph-llm/src/hugegraph_llm/operators/hugegraph_op/commit_to_hugegraph.py", + "hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py", + "hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py", + "hugegraph-llm/src/tests/operators/hugegraph_op/test_fetch_graph_data.py", + "hugegraph-llm/src/tests/operators/hugegraph_op/test_schema_manager.py" ], "production_changes": [ { @@ -71,6 +78,12 @@ "file": "hugegraph-python-client/src/pyhugegraph/utils/util.py", "change": "Preserve backend error envelope details for non-404 HTTP errors and prefer server message fields.", "test": "uv run pytest hugegraph-python-client/src/tests/api/test_response_validation.py -q" + }, + { + "goal": "G3", + "file": "hugegraph-llm/src/hugegraph_llm/operators/hugegraph_op/commit_to_hugegraph.py", + "change": "Map primary-key label:value edge endpoints to server-created VIDs and raise explicit vertex creation errors before accessing id.", + "test": "uv run pytest hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py::TestCommit2Graph::test_load_into_graph_raises_explicit_error_when_vertex_creation_fails -q; HUGEGRAPH_REQUIRED=true uv run pytest hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py -v --tb=short" } ], "tests_added_or_changed": [ @@ -78,7 +91,9 @@ "Added HugeGraph service fixtures and conftest wiring for client and LLM test suites.", "Added response validation contract tests for backend and malformed error envelopes.", "Updated client integration harness so hugegraph-marked legacy tests use the service fixture gate before unittest setUpClass.", - "Added real HugeGraph schema CRUD, graph ID, and Gremlin error-surface contract tests for G2." + "Added real HugeGraph schema CRUD, graph ID, and Gremlin error-surface contract tests for G2.", + "Added LLM HugeGraph boundary integration tests for SchemaManager, Commit2Graph, FetchGraphData, and GremlinExecuteNode.", + "Marked existing hugegraph_op unittest files as unit so strict marker selection exercises them." ], "commands_run": [ "sed -n '1,220p' AGENTS.md", @@ -131,13 +146,17 @@ "HUGEGRAPH_REQUIRED=true uv run pytest hugegraph-python-client/src/tests -m 'integration and hugegraph' -v --tb=short", "uv run ruff format --check hugegraph-python-client/src/tests/conftest.py hugegraph-python-client/src/tests/api/test_schema.py hugegraph-python-client/src/tests/api/test_graph.py hugegraph-python-client/src/tests/api/test_gremlin.py", "uv run ruff check hugegraph-python-client/src/tests/conftest.py hugegraph-python-client/src/tests/api/test_schema.py hugegraph-python-client/src/tests/api/test_graph.py hugegraph-python-client/src/tests/api/test_gremlin.py", - "uv run pytest hugegraph-python-client/src/tests -m 'unit or contract' --cov=pyhugegraph --cov-report=term --cov-report=json:.workflow/quality-program/coverage/client-g2.json" - ], - "known_failures": [ - "Root-level LLM collection command fails before collection because existing prompt config requires cwd hugegraph-llm.", - "Initial coverage command failed before uv sync because pytest-cov was missing from the active environment.", - "LLM integration and hugegraph collection is empty before G3 adds real-boundary tests; pytest returns exit code 5." + "uv run pytest hugegraph-python-client/src/tests -m 'unit or contract' --cov=pyhugegraph --cov-report=term --cov-report=json:.workflow/quality-program/coverage/client-g2.json", + "uv run pytest hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py::TestCommit2Graph::test_load_into_graph_raises_explicit_error_when_vertex_creation_fails -q", + "HUGEGRAPH_REQUIRED=true uv run pytest hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py -v --tb=short --maxfail=1", + "uv run pytest hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py::TestCommit2Graph::test_load_into_graph_raises_explicit_error_when_vertex_creation_fails hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py::TestCommit2Graph::test_load_into_graph_maps_llm_vertex_ids_to_created_vertex_ids -q", + "HUGEGRAPH_REQUIRED=true uv run pytest hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py -v --tb=short", + "uv run pytest hugegraph-llm/src/tests/operators/hugegraph_op -m 'unit or contract' -q", + "uv run ruff format --check hugegraph-llm/src/tests/conftest.py hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py hugegraph-llm/src/tests/operators/hugegraph_op/test_schema_manager.py hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py hugegraph-llm/src/tests/operators/hugegraph_op/test_fetch_graph_data.py hugegraph-llm/src/hugegraph_llm/operators/hugegraph_op/commit_to_hugegraph.py", + "uv run ruff check hugegraph-llm/src/tests/conftest.py hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py hugegraph-llm/src/tests/operators/hugegraph_op/test_schema_manager.py hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py hugegraph-llm/src/tests/operators/hugegraph_op/test_fetch_graph_data.py hugegraph-llm/src/hugegraph_llm/operators/hugegraph_op/commit_to_hugegraph.py", + "uv run pytest hugegraph-llm/src/tests --collect-only -q" ], + "known_failures": [], "deferred_items": [ "Avoid #350/#277 YAML config migration files unless tests require a narrow compatibility assertion.", "Avoid #179 async/streaming API refactor surface.", @@ -145,5 +164,5 @@ "Avoid #240 vector/property embedding abstraction surface.", "Quarantine client/test fixes overlapping #323/#329/#342 until exact changed files are inspected." ], - "next_recommended_action": "Start G3 hugegraph-llm HugeGraph boundary hardening with the local HugeGraph 1.7.0 service" + "next_recommended_action": "Start G4 deterministic parser, API, and operator coverage" } diff --git a/.workflow/quality-program/reports/production-change-ledger.md b/.workflow/quality-program/reports/production-change-ledger.md index b5f2eb99..1fbe4888 100644 --- a/.workflow/quality-program/reports/production-change-ledger.md +++ b/.workflow/quality-program/reports/production-change-ledger.md @@ -3,3 +3,4 @@ | Goal | File | Change | Test proving it | Reason | Risk | |---|---|---|---|---|---| | G2 | `hugegraph-python-client/src/pyhugegraph/utils/util.py` | Preserve backend error envelope details for non-404 HTTP errors and prefer server `message` over `exception`. | `uv run pytest hugegraph-python-client/src/tests/api/test_response_validation.py -q` | Response validation contract gap for 500 backend envelopes. | Low; narrows error wrapping to preserve parsed server details while keeping 404 mapping. | +| G3 | `hugegraph-llm/src/hugegraph_llm/operators/hugegraph_op/commit_to_hugegraph.py` | Map primary-key `label:value` edge endpoints to server-created VIDs and raise explicit vertex creation errors before accessing `.id`. | `uv run pytest hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py::TestCommit2Graph::test_load_into_graph_raises_explicit_error_when_vertex_creation_fails -q`; `HUGEGRAPH_REQUIRED=true uv run pytest hugegraph-llm/src/tests/integration/test_hug [...] diff --git a/docs/superpowers/plans/2026-05-31-hugegraph-ai-quality-program.md b/docs/superpowers/plans/2026-05-31-hugegraph-ai-quality-program.md index f27af976..0e9ecce8 100644 --- a/docs/superpowers/plans/2026-05-31-hugegraph-ai-quality-program.md +++ b/docs/superpowers/plans/2026-05-31-hugegraph-ai-quality-program.md @@ -850,7 +850,7 @@ git commit -m "test(client): harden hugegraph contract coverage" -m "- add real - Update: `.workflow/quality-program/reports/production-change-ledger.md` - Create/Update: `.workflow/quality-program/checkpoints/04-llm-boundary.md` -- [ ] **Step G3.1: Add real-boundary fixture data** +- [x] **Step G3.1: Add real-boundary fixture data** Create helper functions inside `hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py`: @@ -893,7 +893,7 @@ QUALITY_GRAPH = { Use this fixture as a source for `Commit2Graph.run({"schema": schema, "vertices": vertices, "edges": edges})`. Convert the compact fixture into the schema format already consumed by `Commit2Graph`: `propertykeys`, `vertexlabels`, and `edgelabels`. -- [ ] **Step G3.2: Add schema manager real-service test** +- [x] **Step G3.2: Add schema manager real-service test** Add a test that imports production `SchemaManager` and asserts real schema readback: @@ -914,7 +914,7 @@ def test_schema_manager_reads_real_schema(hugegraph_service): assert isinstance(context["schema"]["vertexlabels"], list) ``` -- [ ] **Step G3.3: Add Commit2Graph write/read integration test** +- [x] **Step G3.3: Add Commit2Graph write/read integration test** Add a failing integration test that writes fixture data through production `Commit2Graph`, then reads it using pyhugegraph or production fetch code. @@ -973,7 +973,7 @@ The test must assert: - creation failures raise explicit errors, not secondary NoneType.id errors ``` -- [ ] **Step G3.4: Add FetchGraphData integration test** +- [x] **Step G3.4: Add FetchGraphData integration test** Add a test that imports `FetchGraphData`, reads known graph data, and asserts stable shape: @@ -995,7 +995,7 @@ def test_fetch_graph_data_returns_counts_and_samples(hugegraph_service): assert isinstance(result["edges"], list) ``` -- [ ] **Step G3.5: Add Gremlin failure-surface test** +- [x] **Step G3.5: Add Gremlin failure-surface test** Add a test for the production Gremlin execution boundary: @@ -1012,7 +1012,7 @@ def test_gremlin_execute_surfaces_invalid_query(hugegraph_service): This documents the current node contract: the node surfaces execution errors as result strings instead of raising. If the task changes that production contract, add a regression test and ledger row. -- [ ] **Step G3.6: Run LLM boundary suites** +- [x] **Step G3.6: Run LLM boundary suites** Run: @@ -1023,7 +1023,7 @@ HUGEGRAPH_REQUIRED=true uv run pytest hugegraph-llm/src/tests/integration/test_h Expected: boundary failures classify as service setup, pyhugegraph contract, server contract, or LLM conversion. -- [ ] **Step G3.7: Apply minimal LLM boundary fixes** +- [x] **Step G3.7: Apply minimal LLM boundary fixes** Allowed fixes include: @@ -1036,7 +1036,7 @@ Allowed fixes include: For each production edit, add a production ledger row. -- [ ] **Step G3.8: Write checkpoint and commit** +- [x] **Step G3.8: Write checkpoint and commit** Run: diff --git a/hugegraph-llm/src/hugegraph_llm/operators/hugegraph_op/commit_to_hugegraph.py b/hugegraph-llm/src/hugegraph_llm/operators/hugegraph_op/commit_to_hugegraph.py index daade304..caa33b9a 100644 --- a/hugegraph-llm/src/hugegraph_llm/operators/hugegraph_op/commit_to_hugegraph.py +++ b/hugegraph-llm/src/hugegraph_llm/operators/hugegraph_op/commit_to_hugegraph.py @@ -147,20 +147,26 @@ class Commit2Graph: continue # TODO: we could try batch add vertices first, setback to single-mode if failed - original_id = vertex.get("id") - if vertex_label.get("id_strategy") == "CUSTOMIZE_STRING" and original_id: + explicit_id = vertex.get("id") + mapping_id = explicit_id + if not mapping_id and len(primary_keys) == 1: + mapping_id = f"{input_label}:{input_properties[primary_keys[0]]}" + + if vertex_label.get("id_strategy") == "CUSTOMIZE_STRING" and explicit_id: result = self._handle_graph_creation( self.client.graph().addVertex, input_label, input_properties, - id=original_id, + id=explicit_id, ) else: result = self._handle_graph_creation(self.client.graph().addVertex, input_label, input_properties) + if result is None: + raise ValueError(f"Failed to create vertex '{input_label}' with properties {input_properties}") vid = result.id vertex["id"] = vid - if original_id: - vid_mapping[original_id] = vid + if mapping_id: + vid_mapping[mapping_id] = vid for edge in edges: start = vid_mapping.get(edge.get("outV"), edge.get("outV")) diff --git a/hugegraph-llm/src/tests/conftest.py b/hugegraph-llm/src/tests/conftest.py index 42033626..ec14a6e3 100644 --- a/hugegraph-llm/src/tests/conftest.py +++ b/hugegraph-llm/src/tests/conftest.py @@ -23,6 +23,7 @@ import nltk # Get project root directory project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")) +os.chdir(project_root) # Add to Python path sys.path.insert(0, project_root) # Add src directory to Python path diff --git a/hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py b/hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py new file mode 100644 index 00000000..6184a023 --- /dev/null +++ b/hugegraph-llm/src/tests/integration/test_hugegraph_boundary.py @@ -0,0 +1,225 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +from copy import deepcopy + +import pytest +from pyhugegraph.client import PyHugeClient + +pytestmark = [pytest.mark.integration, pytest.mark.hugegraph] + +QUALITY_SCHEMA = { + "vertices": [ + {"vertex_label": "quality_person", "properties": ["name", "age"], "primary_keys": ["name"]}, + {"vertex_label": "quality_software", "properties": ["name", "lang"], "primary_keys": ["name"]}, + ], + "edges": [ + { + "edge_label": "quality_created", + "source_vertex_label": "quality_person", + "target_vertex_label": "quality_software", + "properties": ["date"], + } + ], +} + +QUALITY_GRAPH = { + "vertices": [ + {"label": "quality_person", "properties": {"name": "marko", "age": 29}}, + {"label": "quality_software", "properties": {"name": "lop", "lang": "java"}}, + ], + "edges": [ + { + "label": "quality_created", + "source": "marko", + "target": "lop", + "properties": {"date": "2026-05-31"}, + } + ], +} + +QUALITY_COMMIT_SCHEMA = { + "propertykeys": [ + {"name": "name", "data_type": "TEXT", "cardinality": "SINGLE"}, + {"name": "age", "data_type": "INT", "cardinality": "SINGLE"}, + {"name": "lang", "data_type": "TEXT", "cardinality": "SINGLE"}, + {"name": "date", "data_type": "TEXT", "cardinality": "SINGLE"}, + ], + "vertexlabels": [ + { + "name": "quality_person", + "properties": ["name", "age"], + "primary_keys": ["name"], + "nullable_keys": [], + }, + { + "name": "quality_software", + "properties": ["name", "lang"], + "primary_keys": ["name"], + "nullable_keys": [], + }, + ], + "edgelabels": [ + { + "name": "quality_created", + "source_label": "quality_person", + "target_label": "quality_software", + "properties": ["date"], + } + ], +} + +QUALITY_COMMIT_DATA = { + "schema": QUALITY_COMMIT_SCHEMA, + "vertices": [ + {"label": "quality_person", "properties": {"name": "marko", "age": 29}}, + {"label": "quality_software", "properties": {"name": "lop", "lang": "java"}}, + ], + "edges": [ + { + "label": "quality_created", + "outV": "quality_person:marko", + "inV": "quality_software:lop", + "properties": {"date": "2026-05-31"}, + } + ], +} + + +def _make_client(service): + return PyHugeClient( + url=service.url, + graph=service.graph, + user=service.user, + pwd=service.password, + graphspace=service.graphspace, + ) + + [email protected]() +def configured_hugegraph(hugegraph_service): + from hugegraph_llm.config import huge_settings + + original = { + "graph_url": huge_settings.graph_url, + "graph_name": huge_settings.graph_name, + "graph_user": huge_settings.graph_user, + "graph_pwd": huge_settings.graph_pwd, + "graph_space": huge_settings.graph_space, + } + huge_settings.graph_url = hugegraph_service.url + huge_settings.graph_name = hugegraph_service.graph + huge_settings.graph_user = hugegraph_service.user + huge_settings.graph_pwd = hugegraph_service.password + huge_settings.graph_space = hugegraph_service.graphspace + + client = _make_client(hugegraph_service) + client.graphs().clear_graph_all_data() + yield hugegraph_service + client.graphs().clear_graph_all_data() + for key, value in original.items(): + setattr(huge_settings, key, value) + + +def _create_quality_schema(client): + schema = client.schema() + schema.propertyKey("name").asText().ifNotExist().create() + schema.propertyKey("age").asInt().ifNotExist().create() + schema.propertyKey("lang").asText().ifNotExist().create() + schema.propertyKey("date").asText().ifNotExist().create() + schema.vertexLabel("quality_person").properties("name", "age").primaryKeys("name").ifNotExist().create() + schema.vertexLabel("quality_software").properties("name", "lang").primaryKeys("name").ifNotExist().create() + schema.edgeLabel("quality_created").sourceLabel("quality_person").targetLabel("quality_software").properties( + "date" + ).ifNotExist().create() + + +def _commit_quality_graph(): + from hugegraph_llm.operators.hugegraph_op.commit_to_hugegraph import Commit2Graph + + commit = Commit2Graph() + data = deepcopy(QUALITY_COMMIT_DATA) + return commit.run(data) + + +def test_schema_manager_reads_real_schema(configured_hugegraph): + from hugegraph_llm.operators.hugegraph_op.schema_manager import SchemaManager + + client = _make_client(configured_hugegraph) + _create_quality_schema(client) + + manager = SchemaManager(graph_name=configured_hugegraph.graph) + context = manager.run({}) + + assert "schema" in context + assert "simple_schema" in context + assert isinstance(context["schema"]["vertexlabels"], list) + assert any(label["name"] == "quality_person" for label in context["schema"]["vertexlabels"]) + + +def test_commit_to_graph_writes_vertices_and_edges(configured_hugegraph): + _commit_quality_graph() + + client = _make_client(configured_hugegraph) + counts = client.gremlin().exec( + """ + g.V().hasLabel('quality_person', 'quality_software').count() + """ + )["data"][0] + edges = client.gremlin().exec( + """ + g.E().hasLabel('quality_created'). + project('out', 'in', 'date'). + by(outV().values('name')). + by(inV().values('name')). + by(values('date')) + """ + )["data"] + + assert counts == 2 + assert len(edges) == 1 + assert edges[0]["out"] == "marko" + assert edges[0]["in"] == "lop" + assert edges[0]["date"] == "2026-05-31" + + +def test_fetch_graph_data_returns_counts_and_samples(configured_hugegraph): + from hugegraph_llm.operators.hugegraph_op.fetch_graph_data import FetchGraphData + + _commit_quality_graph() + result = FetchGraphData(_make_client(configured_hugegraph)).run({}) + + assert {"vertex_num", "edge_num", "vertices", "edges", "note"}.issubset(result) + assert result["vertex_num"] == 2 + assert result["edge_num"] == 1 + assert isinstance(result["vertices"], list) + assert isinstance(result["edges"], list) + + +def test_gremlin_execute_surfaces_invalid_query(configured_hugegraph): + from hugegraph_llm.nodes.hugegraph_node.gremlin_execute import GremlinExecuteNode + + node = GremlinExecuteNode() + node.wk_input = type("Input", (), {"requested_outputs": ["raw_execution_result"]})() + result = node.operator_schedule({"raw_result": "g.V2()"}) + + assert result["template_exec_res"] == "" + assert ( + "g.V2" in result["raw_exec_res"] + or "No signature" in result["raw_exec_res"] + or "NotFound" in result["raw_exec_res"] + ) diff --git a/hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py b/hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py index b13f9042..d89936d8 100644 --- a/hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py +++ b/hugegraph-llm/src/tests/operators/hugegraph_op/test_commit_to_hugegraph.py @@ -19,11 +19,14 @@ import unittest from unittest.mock import MagicMock, patch +import pytest from pyhugegraph.utils.exceptions import CreateError, NotFoundError from hugegraph_llm.operators.hugegraph_op.commit_to_hugegraph import Commit2Graph from hugegraph_llm.operators.llm_op.property_graph_extract import PropertyGraphExtract +pytestmark = [pytest.mark.unit] + class TestCommit2Graph(unittest.TestCase): def setUp(self): @@ -521,6 +524,26 @@ class TestCommit2Graph(unittest.TestCase): {"role": "Forrest Gump"}, ) + @patch("hugegraph_llm.operators.hugegraph_op.commit_to_hugegraph.Commit2Graph._handle_graph_creation") + def test_load_into_graph_raises_explicit_error_when_vertex_creation_fails(self, mock_handle_graph_creation): + """Test failed vertex creation is reported before edge creation.""" + mock_handle_graph_creation.return_value = None + + vertices = [{"label": "person", "properties": {"name": "Tom Hanks", "age": 67}}] + edges = [ + { + "label": "acted_in", + "properties": {"role": "Forrest Gump"}, + "outV": "person:Tom Hanks", + "inV": "movie:Forrest Gump", + } + ] + + with self.assertRaisesRegex(ValueError, "Failed to create vertex"): + self.commit2graph.load_into_graph(vertices, edges, self.schema) + + mock_handle_graph_creation.assert_called_once() + @patch("hugegraph_llm.operators.hugegraph_op.commit_to_hugegraph.Commit2Graph._handle_graph_creation") def test_load_into_graph_with_data_type_validation_failure(self, mock_handle_graph_creation): """Test load_into_graph method with data type validation failure.""" diff --git a/hugegraph-llm/src/tests/operators/hugegraph_op/test_fetch_graph_data.py b/hugegraph-llm/src/tests/operators/hugegraph_op/test_fetch_graph_data.py index 8527502d..44b51f44 100644 --- a/hugegraph-llm/src/tests/operators/hugegraph_op/test_fetch_graph_data.py +++ b/hugegraph-llm/src/tests/operators/hugegraph_op/test_fetch_graph_data.py @@ -18,8 +18,12 @@ import unittest from unittest.mock import MagicMock +import pytest + from hugegraph_llm.operators.hugegraph_op.fetch_graph_data import FetchGraphData +pytestmark = [pytest.mark.unit] + class TestFetchGraphData(unittest.TestCase): def setUp(self): diff --git a/hugegraph-llm/src/tests/operators/hugegraph_op/test_schema_manager.py b/hugegraph-llm/src/tests/operators/hugegraph_op/test_schema_manager.py index b77ae01f..05d9659f 100644 --- a/hugegraph-llm/src/tests/operators/hugegraph_op/test_schema_manager.py +++ b/hugegraph-llm/src/tests/operators/hugegraph_op/test_schema_manager.py @@ -18,8 +18,12 @@ import unittest from unittest.mock import MagicMock, patch +import pytest + from hugegraph_llm.operators.hugegraph_op.schema_manager import SchemaManager +pytestmark = [pytest.mark.unit] + class TestSchemaManager(unittest.TestCase): def setUp(self):
