teleo-infrastructure/tests/test_hermes_leoclean_db_context_plugin.py
twentyOne2x 2264ad1f2b
Some checks are pending
CI / lint-and-test (push) Waiting to run
Enforce DB-composed Leo responses before delivery (#116)
2026-07-13 13:27:54 +02:00

184 lines
8 KiB
Python

"""Tests for automatic live database-contract injection into Leo turns."""
from __future__ import annotations
import importlib.util
import json
import subprocess
from pathlib import Path
from types import SimpleNamespace
ROOT = Path(__file__).resolve().parents[1]
PLUGIN = ROOT / "hermes-agent" / "leoclean-plugins" / "vps" / "leo-db-context" / "__init__.py"
def load_plugin():
spec = importlib.util.spec_from_file_location("leo_db_context_plugin", PLUGIN)
assert spec and spec.loader
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
def test_plugin_registers_generation_and_delivery_hooks() -> None:
module = load_plugin()
registered = []
module.register(SimpleNamespace(register_hook=lambda name, callback: registered.append((name, callback))))
assert [name for name, _callback in registered] == ["pre_llm_call", "post_llm_call"]
def test_plugin_injects_only_operational_contracts_and_writes_redacted_trace(
tmp_path: Path, monkeypatch
) -> None:
module = load_plugin()
home = tmp_path / "profile"
kb_tool = home / "bin" / "kb_tool.py"
kb_tool.parent.mkdir(parents=True)
kb_tool.write_text("# fixture\n", encoding="utf-8")
trace = tmp_path / "trace.jsonl"
monkeypatch.setenv("LEO_DB_CONTEXT_TRACE_PATH", str(trace))
payload = {
"query": "must not be injected",
"claims": [{"text": "must not be injected"}],
"operational_contracts": [
{"id": "reply_budget", "hard_max_words": 220},
{"id": "source_intake", "capability_tier": "build-local compiler only"},
],
"compiled_response": "must not be injected either",
}
calls = []
def fake_runner(command, **kwargs):
calls.append((command, kwargs))
return subprocess.CompletedProcess(command, 0, stdout=json.dumps(payload), stderr="")
context = module.build_database_context(
"operator secret-shaped but nonsecret message", hermes_home=home, runner=fake_runner
)
assert "reply_budget" in context
assert "source_intake" in context
assert "build-local compiler only" in context
assert "must not be injected" not in context
assert "operator secret-shaped" not in trace.read_text(encoding="utf-8")
record = json.loads(trace.read_text(encoding="utf-8"))
assert record["status"] == "ok"
assert record["injected"] is True
assert record["contract_ids"] == ["reply_budget", "source_intake"]
command, kwargs = calls[0]
assert command[2:5] == ["--local", "context", "operator secret-shaped but nonsecret message"]
assert command[-6:] == ["--limit", "0", "--context-limit", "0", "--format", "json"]
assert kwargs["timeout"] == module.DEFAULT_TIMEOUT_SECONDS
def test_plugin_replaces_contract_violating_draft_with_compiled_db_response(
tmp_path: Path, monkeypatch
) -> None:
module = load_plugin()
home = tmp_path / "profile"
kb_tool = home / "bin" / "kb_tool.py"
kb_tool.parent.mkdir(parents=True)
kb_tool.write_text("# fixture\n", encoding="utf-8")
trace = tmp_path / "trace.jsonl"
monkeypatch.setenv("HERMES_HOME", str(home))
monkeypatch.setenv("LEO_DB_CONTEXT_TRACE_PATH", str(trace))
compiled = (
"Map claims with sources and evidence; put the framework in reasoning_tools, the rule in "
"behavioral_rules, an evaluative gate in governance_gates, and stage the belief correction. "
"Stage one pending_review proposal. approve_claim applies only claims, sources, evidence, edges, and "
"reasoning_tools; approve_claim supports neither behavioral_rules nor governance_gates. Belief updates "
"remain staged until a separate reviewed apply capability exists. Receipt: proposal applied_at and "
"postflight row readback."
)
payload = {
"operational_contracts": [
{"id": "reply_budget", "hard_max_words": 200},
{"id": "mixed_packet_composition"},
],
"compiled_response": compiled,
}
def fake_runner(command, **_kwargs):
return subprocess.CompletedProcess(command, 0, stdout=json.dumps(payload), stderr="")
snapshot = module._load_database_snapshot("compose this packet", hermes_home=home, runner=fake_runner)
module._store_snapshot("session-1", snapshot)
result = module._post_llm_call(
session_id="session-1",
user_message="compose this packet",
assistant_response=(
"Claims, sources, evidence, reasoning_tools, behavioral_rules, governance_gates, and beliefs. "
"Stage and review a proposal, then apply only the supported collections (claims, sources, evidence, "
"edges, reasoning_tools, belief updates). approve_claim does not cover behavioral_rules or "
"governance_gates; use a separate reviewed apply path. Receipt: applied_at and postflight readback."
),
)
assert result == {"assistant_response": compiled}
records = [json.loads(line) for line in trace.read_text(encoding="utf-8").splitlines()]
assert records[-1]["event"] == "post_llm_call"
assert records[-1]["transformed"] is True
assert records[-1]["issues"] == [
"unsupported_collection_listed_as_supported",
"unsupported_collection_listed_in_apply",
]
assert "compose this packet" not in trace.read_text(encoding="utf-8")
def test_plugin_keeps_contract_valid_draft(tmp_path: Path) -> None:
module = load_plugin()
home = tmp_path / "profile"
kb_tool = home / "bin" / "kb_tool.py"
kb_tool.parent.mkdir(parents=True)
kb_tool.write_text("# fixture\n", encoding="utf-8")
valid = (
"Map claims with sources and evidence; put the framework in reasoning_tools, the rule in "
"behavioral_rules, an evaluative gate in governance_gates, and stage the belief correction. "
"Stage one pending_review proposal. approve_claim applies only claims, sources, evidence, edges, and "
"reasoning_tools; approve_claim supports neither behavioral_rules nor governance_gates. Belief updates "
"remain staged until a separate reviewed apply capability exists. Receipt: proposal applied_at and "
"postflight row readback."
)
payload = {
"operational_contracts": [
{"id": "reply_budget", "hard_max_words": 200},
{"id": "mixed_packet_composition"},
],
"compiled_response": valid,
}
def fake_runner(command, **_kwargs):
return subprocess.CompletedProcess(command, 0, stdout=json.dumps(payload), stderr="")
snapshot = module._load_database_snapshot("compose this packet", hermes_home=home, runner=fake_runner)
module._store_snapshot("session-2", snapshot)
assert module._post_llm_call(
session_id="session-2", user_message="compose this packet", assistant_response=valid
) is None
def test_plugin_fails_closed_when_database_contract_lookup_fails(tmp_path: Path) -> None:
module = load_plugin()
home = tmp_path / "profile"
kb_tool = home / "bin" / "kb_tool.py"
kb_tool.parent.mkdir(parents=True)
kb_tool.write_text("# fixture\n", encoding="utf-8")
def fake_runner(command, **_kwargs):
return subprocess.CompletedProcess(command, 9, stdout="", stderr="database unavailable")
context = module.build_database_context("What is live?", hermes_home=home, runner=fake_runner)
assert 'status="unavailable"' in context
assert "Do not infer current schema" in context
assert "database unavailable" not in context
def test_handler_harness_retains_database_context_proof_fields() -> None:
source = (ROOT / "scripts" / "run_leo_direct_claim_handler_suite.py").read_text(encoding="utf-8")
assert "LEO_DB_CONTEXT_TRACE_PATH" in source
assert '"database_context_trace"' in source
assert '"database_context_injection_count"' in source
assert '"database_context_all_ok"' in source
assert '"database_response_validation_count"' in source
assert '"database_response_validation_all_ok"' in source
assert '"database_response_transform_count"' in source