chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
"""Tests for graphify.querylog."""
|
||||
import json
|
||||
import os
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
from graphify.querylog import log_query, nodes_from_result
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# nodes_from_result
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_nodes_from_result_parses_header():
|
||||
result = "Traversal: BFS depth=2 | Start: ['foo'] | 7 nodes found\n\nNODE foo"
|
||||
assert nodes_from_result(result) == 7
|
||||
|
||||
|
||||
def test_nodes_from_result_singular():
|
||||
assert nodes_from_result("1 node found") == 1
|
||||
|
||||
|
||||
def test_nodes_from_result_missing():
|
||||
assert nodes_from_result("no match here") is None
|
||||
|
||||
|
||||
def test_nodes_from_result_empty():
|
||||
assert nodes_from_result("") is None
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# log_query — basic write
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_log_query_writes_jsonl(tmp_path, monkeypatch):
|
||||
log_file = tmp_path / "q.log"
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
|
||||
monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)
|
||||
|
||||
log_query(kind="query", question="what is X", corpus="/some/graph.json",
|
||||
result="3 nodes found\nNODE a", duration_ms=12.5, mode="bfs", depth=2)
|
||||
|
||||
lines = log_file.read_text().splitlines()
|
||||
assert len(lines) == 1
|
||||
rec = json.loads(lines[0])
|
||||
assert rec["kind"] == "query"
|
||||
assert rec["question"] == "what is X"
|
||||
assert rec["corpus"] == "/some/graph.json"
|
||||
assert rec["nodes_returned"] == 3
|
||||
assert rec["result_chars"] > 0
|
||||
assert rec["duration_ms"] == pytest.approx(12.5, abs=0.01)
|
||||
assert rec["mode"] == "bfs"
|
||||
assert "ts" in rec
|
||||
|
||||
|
||||
def test_log_query_appends(tmp_path, monkeypatch):
|
||||
log_file = tmp_path / "q.log"
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
|
||||
monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)
|
||||
|
||||
log_query(kind="query", question="q1", corpus="/g.json")
|
||||
log_query(kind="query", question="q2", corpus="/g.json")
|
||||
|
||||
lines = log_file.read_text().splitlines()
|
||||
assert len(lines) == 2
|
||||
assert json.loads(lines[0])["question"] == "q1"
|
||||
assert json.loads(lines[1])["question"] == "q2"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# opt-out / opt-in
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_disable_env(tmp_path, monkeypatch):
|
||||
log_file = tmp_path / "q.log"
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG_DISABLE", "1")
|
||||
|
||||
log_query(kind="query", question="q", corpus="/g.json")
|
||||
|
||||
assert not log_file.exists()
|
||||
|
||||
|
||||
def test_disable_env_true(tmp_path, monkeypatch):
|
||||
log_file = tmp_path / "q.log"
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG_DISABLE", "true")
|
||||
|
||||
log_query(kind="query", question="q", corpus="/g.json")
|
||||
|
||||
assert not log_file.exists()
|
||||
|
||||
|
||||
def test_responses_not_logged_by_default(tmp_path, monkeypatch):
|
||||
log_file = tmp_path / "q.log"
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
|
||||
monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)
|
||||
monkeypatch.delenv("GRAPHIFY_QUERY_LOG_RESPONSES", raising=False)
|
||||
|
||||
log_query(kind="query", question="q", corpus="/g.json", result="NODE foo")
|
||||
|
||||
rec = json.loads(log_file.read_text())
|
||||
assert "response" not in rec
|
||||
|
||||
|
||||
def test_responses_optin(tmp_path, monkeypatch):
|
||||
log_file = tmp_path / "q.log"
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG_RESPONSES", "1")
|
||||
monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)
|
||||
|
||||
log_query(kind="query", question="q", corpus="/g.json", result="NODE foo bar")
|
||||
|
||||
rec = json.loads(log_file.read_text())
|
||||
assert rec["response"] == "NODE foo bar"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# robustness — never raises
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_log_never_raises(tmp_path, monkeypatch):
|
||||
# Point at a directory — open() for append will fail
|
||||
bad_path = tmp_path / "is_a_dir"
|
||||
bad_path.mkdir()
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(bad_path))
|
||||
monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)
|
||||
|
||||
# Must not raise
|
||||
log_query(kind="query", question="q", corpus="/g.json")
|
||||
|
||||
|
||||
def test_log_creates_parent_dirs(tmp_path, monkeypatch):
|
||||
log_file = tmp_path / "deep" / "nested" / "q.log"
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
|
||||
monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)
|
||||
|
||||
log_query(kind="query", question="q", corpus="/g.json")
|
||||
|
||||
assert log_file.exists()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# field coverage
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_nodes_returned_inferred_from_result(tmp_path, monkeypatch):
|
||||
log_file = tmp_path / "q.log"
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
|
||||
monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)
|
||||
|
||||
log_query(kind="query", question="q", corpus="/g.json",
|
||||
result="5 nodes found\nNODE a\nNODE b")
|
||||
|
||||
rec = json.loads(log_file.read_text())
|
||||
assert rec["nodes_returned"] == 5
|
||||
|
||||
|
||||
def test_explicit_nodes_returned_takes_precedence(tmp_path, monkeypatch):
|
||||
log_file = tmp_path / "q.log"
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
|
||||
monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)
|
||||
|
||||
log_query(kind="path", question="A -> B", corpus="/g.json", nodes_returned=3)
|
||||
|
||||
rec = json.loads(log_file.read_text())
|
||||
assert rec["nodes_returned"] == 3
|
||||
|
||||
|
||||
def test_kind_mcp_query(tmp_path, monkeypatch):
|
||||
log_file = tmp_path / "q.log"
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(log_file))
|
||||
monkeypatch.delenv("GRAPHIFY_QUERY_LOG_DISABLE", raising=False)
|
||||
|
||||
log_query(kind="mcp_query", question="q", corpus="/g.json")
|
||||
|
||||
rec = json.loads(log_file.read_text())
|
||||
assert rec["kind"] == "mcp_query"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# #1797 — query log is opt-in (default OFF)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _clear_log_env(monkeypatch):
|
||||
for k in ("GRAPHIFY_QUERY_LOG", "GRAPHIFY_QUERY_LOG_ENABLE", "GRAPHIFY_QUERY_LOG_DISABLE"):
|
||||
monkeypatch.delenv(k, raising=False)
|
||||
|
||||
|
||||
def test_query_log_off_by_default(monkeypatch):
|
||||
from graphify.querylog import _log_path
|
||||
_clear_log_env(monkeypatch)
|
||||
assert _log_path() is None
|
||||
|
||||
|
||||
def test_query_log_enabled_by_explicit_flag(monkeypatch):
|
||||
from graphify.querylog import _log_path
|
||||
_clear_log_env(monkeypatch)
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG_ENABLE", "1")
|
||||
assert str(_log_path()).endswith("graphify-queries.log")
|
||||
|
||||
|
||||
def test_query_log_enabled_by_explicit_path(monkeypatch, tmp_path):
|
||||
from graphify.querylog import _log_path
|
||||
_clear_log_env(monkeypatch)
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG", str(tmp_path / "q.log"))
|
||||
assert _log_path() == tmp_path / "q.log"
|
||||
|
||||
|
||||
def test_query_log_disable_wins(monkeypatch):
|
||||
from graphify.querylog import _log_path
|
||||
_clear_log_env(monkeypatch)
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG_ENABLE", "1")
|
||||
monkeypatch.setenv("GRAPHIFY_QUERY_LOG_DISABLE", "1")
|
||||
assert _log_path() is None
|
||||
|
||||
|
||||
def test_log_query_writes_nothing_by_default(monkeypatch, tmp_path):
|
||||
"""End-to-end: with no opt-in, log_query must not create the default log."""
|
||||
_clear_log_env(monkeypatch)
|
||||
monkeypatch.setattr(Path, "home", classmethod(lambda cls: tmp_path))
|
||||
log_query(kind="query", question="secret internal ticket TICKET-123", corpus=".", result="1 node found")
|
||||
assert not (tmp_path / ".cache" / "graphify-queries.log").exists()
|
||||
Reference in New Issue
Block a user