test_garden_layer.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import pytest
  2. import requests
  3. from typing import Any, Dict
  4. from garden_layer import GardenLayer
  5. from garden_layer.config import CLONE_OF, GRAPH, KEROSENE_ROOT, MCP_URL
  6. CYCLE_2026_3 = "http://world.eu.org/example1#ProductionCycle_21d96b6c-179"
  7. def pytest_report_header(config):
  8. return "Garden layer tests: requires virtuoso_mcp running at GardenLayer.MCP_URL"
  9. @pytest.fixture
  10. def garden_layer():
  11. layer = GardenLayer()
  12. try:
  13. layer.traverse(KEROSENE_ROOT, CLONE_OF, direction="incoming", limit=1)
  14. except requests.RequestException as exc:
  15. pytest.skip(f"Cannot reach MCP server: {exc}")
  16. return layer
  17. def test_traverse_clone_tree(garden_layer):
  18. """Walk `cloneOf` incoming edges and ensure the root has clones to inspect."""
  19. result = garden_layer.traverse(KEROSENE_ROOT, CLONE_OF, direction="incoming", limit=5)
  20. assert isinstance(result, dict)
  21. bindings = result.get("results", {}).get("bindings", [])
  22. assert isinstance(bindings, list)
  23. assert bindings, "Expected at least one clone binding"
  24. print(f"Traverse of {KEROSENE_ROOT} returned {len(bindings)} clone candidates.")
  25. def test_describe_subject(garden_layer):
  26. """Describe the root plant and surface the top predicates the garden helpers can reuse."""
  27. summary = garden_layer.describe_subject(KEROSENE_ROOT, limit=5)
  28. assert isinstance(summary, dict)
  29. bindings = summary.get("results", {}).get("bindings", [])
  30. assert bindings, "Expected describe_subject to return bindings"
  31. triples = [f"{b['predicate']['value']} -> {b['object'].get('value', '<literal>')}" for b in bindings]
  32. print("describe_subject returned:")
  33. for triple in triples:
  34. print(" ", triple)
  35. def test_path_traverse_lineage(garden_layer):
  36. """Use the path helper to follow `cloneOf` and observe the lineage step."""
  37. path = garden_layer.path_traverse(KEROSENE_ROOT, [CLONE_OF], direction="incoming", limit=5)
  38. assert isinstance(path, dict)
  39. bindings = path.get("result", {}).get("results", {}).get("bindings", [])
  40. assert isinstance(bindings, list)
  41. assert bindings, "Path traverse should find at least one step"
  42. print(f"Property path {CLONE_OF} produced {len(bindings)} step bindings.")
  43. def test_property_usage_statistics(garden_layer):
  44. """Summarize how the `cloneOf` property is used so future helpers can rely on frequency data."""
  45. stats = garden_layer.property_usage_statistics(CLONE_OF, examples_limit=3)
  46. assert isinstance(stats, dict)
  47. count_bindings = stats.get("count", {}).get("results", {}).get("bindings", [])
  48. assert count_bindings, "Expected usage count bindings"
  49. usage_count = count_bindings[0].get("usageCount", {}).get("value")
  50. example_bindings = stats.get("examples", {}).get("results", {}).get("bindings", [])
  51. assert isinstance(example_bindings, list)
  52. print(f"cloneOf usage count: {usage_count}")
  53. print("example bindings:")
  54. for binding in example_bindings:
  55. subject = binding.get("subjectLabel", {}).get("value") or binding.get("subject", {}).get("value")
  56. object_ = binding.get("objectLabel", {}).get("value") or binding.get("object", {}).get("value")
  57. print(f" - {subject} -> {object_}")
  58. def test_batch_insert(garden_layer):
  59. """Batch-insert a TTL snippet and verify the query shape"""
  60. ttl = '<http://world.eu.org/example1#batch_test_subject> <http://www.w3.org/2000/01/rdf-schema#label> "garden batch" .'
  61. result = garden_layer.batch_insert(ttl=ttl, graph=GRAPH)
  62. assert isinstance(result, dict)
  63. assert "query" in result
  64. assert "INSERT DATA" in result.get("query", "").upper()
  65. print("batch_insert generated:")
  66. print(result.get("query", ""))
  67. def call_mcp_tool(tool_name: str, payload: Dict[str, Any]) -> Any:
  68. response = requests.post(MCP_URL, json={"tool": tool_name, "input": payload}, timeout=10)
  69. response.raise_for_status()
  70. body = response.json()
  71. assert body.get("status") == "ok", f"{tool_name} failed: {body.get('detail') or 'unknown reason'}"
  72. return body["result"]
  73. def test_mcp_garden_describe_subject():
  74. result = call_mcp_tool(
  75. "garden_describe_subject",
  76. {"subject_uri": KEROSENE_ROOT, "limit": 5},
  77. )
  78. bindings = result.get("results", {}).get("bindings", [])
  79. assert bindings, "garden_describe_subject should return bindings"
  80. print("garden_describe_subject returned", len(bindings), "bindings via /mcp")
  81. def test_mcp_garden_property_usage_statistics():
  82. result = call_mcp_tool(
  83. "garden_property_usage_statistics",
  84. {"property_uri": CLONE_OF, "examples_limit": 2},
  85. )
  86. count_bindings = result.get("count", {}).get("results", {}).get("bindings", [])
  87. assert count_bindings, "Expected usage count bindings from the garden tool"
  88. examples = result.get("examples", {}).get("results", {}).get("bindings", [])
  89. assert isinstance(examples, list)
  90. print("garden_property_usage_statistics sample bindings:", len(examples))
  91. def test_mcp_garden_cycle_plants():
  92. result = call_mcp_tool(
  93. "garden_cycle_plants",
  94. {"cycle_uri": CYCLE_2026_3, "limit": 5},
  95. )
  96. bindings = result.get("results", {}).get("bindings", [])
  97. assert bindings, "Expected at least one plant binding from the cycle"
  98. print("garden_cycle_plants returned", len(bindings), "plants for cycle", CYCLE_2026_3)
  99. for binding in bindings:
  100. plant_uri = binding.get("plant", {}).get("value")
  101. assert plant_uri, "Each binding should include a plant URI"