generated from coulomb/repo-seed
Optional JSONPath query/extract support, FTS5 section/block search, mkt cache query and search. Local SQLite backend now supports parsed snapshot persistence, incremental refresh, cached querying, and ranked full-text search
This commit is contained in:
@@ -57,6 +57,23 @@ def test_local_snapshot_store_deletes_removed_files(tmp_path: Path):
|
||||
assert store.load_state() == []
|
||||
|
||||
|
||||
def test_local_snapshot_store_searches_sections_and_blocks(tmp_path: Path):
|
||||
source = tmp_path / "doc.md"
|
||||
source.write_text(
|
||||
"# Doc\n\n## Decision\n\nWe choose a local SQLite index for repeated queries.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
store = LocalSnapshotStore(local_index_path_for(tmp_path))
|
||||
store.build([tmp_path], root=tmp_path)
|
||||
|
||||
results = store.search("SQLite")
|
||||
|
||||
assert results
|
||||
assert results[0].path == "doc.md"
|
||||
assert {result.unit_kind for result in results} <= {"section", "block"}
|
||||
assert any("SQLite index" in result.text for result in results)
|
||||
|
||||
|
||||
def test_mkt_ast_show_and_stats(tmp_path: Path):
|
||||
source = tmp_path / "doc.md"
|
||||
source.write_text("# Doc\n\nBody.\n", encoding="utf-8")
|
||||
@@ -87,3 +104,44 @@ def test_mkt_cache_init_and_index(tmp_path: Path):
|
||||
assert "parsed: 1" in indexed.output
|
||||
assert clean.exit_code == 0
|
||||
assert "clean" in clean.output
|
||||
|
||||
|
||||
def test_mkt_search_uses_local_index(tmp_path: Path):
|
||||
source = tmp_path / "doc.md"
|
||||
source.write_text("# Doc\n\nSearchable local index content.\n", encoding="utf-8")
|
||||
runner = CliRunner()
|
||||
indexed = runner.invoke(main, ["cache", "index", str(tmp_path), "--root", str(tmp_path)])
|
||||
|
||||
result = runner.invoke(main, ["search", "Searchable", "--root", str(tmp_path)])
|
||||
|
||||
assert indexed.exit_code == 0
|
||||
assert result.exit_code == 0
|
||||
assert "match(es)" in result.output
|
||||
assert "doc.md" in result.output
|
||||
|
||||
|
||||
def test_mkt_cache_query_uses_indexed_snapshots(tmp_path: Path):
|
||||
one = tmp_path / "one.md"
|
||||
two = tmp_path / "two.md"
|
||||
one.write_text("# One\n\n## Decision\n\nUse SQLite.\n", encoding="utf-8")
|
||||
two.write_text("# Two\n\n## Context\n\nOther material.\n", encoding="utf-8")
|
||||
runner = CliRunner()
|
||||
indexed = runner.invoke(main, ["cache", "index", str(tmp_path), "--root", str(tmp_path)])
|
||||
|
||||
result = runner.invoke(
|
||||
main,
|
||||
[
|
||||
"cache",
|
||||
"query",
|
||||
"sections[heading=Decision]",
|
||||
"--root",
|
||||
str(tmp_path),
|
||||
"--format",
|
||||
"json",
|
||||
],
|
||||
)
|
||||
|
||||
assert indexed.exit_code == 0
|
||||
assert result.exit_code == 0
|
||||
assert '"count": 1' in result.output
|
||||
assert '"source_path": "one.md"' in result.output
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
from pathlib import Path
|
||||
import importlib.util
|
||||
|
||||
import pytest
|
||||
from click.testing import CliRunner
|
||||
|
||||
from markitect_tool.cli import main
|
||||
from markitect_tool.core import parse_markdown
|
||||
from markitect_tool.query import InvalidQueryError, extract_document, query_document
|
||||
from markitect_tool.query import (
|
||||
InvalidQueryError,
|
||||
extract_document,
|
||||
query_document,
|
||||
query_document_jsonpath,
|
||||
)
|
||||
|
||||
|
||||
QUERY_DOC = """---
|
||||
@@ -110,6 +116,41 @@ def test_invalid_query_reports_error():
|
||||
query_document(document, "sections[heading")
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
importlib.util.find_spec("jsonpath_ng") is None,
|
||||
reason="jsonpath-ng optional dependency is not installed",
|
||||
)
|
||||
def test_query_document_jsonpath_returns_shared_match_envelope():
|
||||
document = parse_markdown(QUERY_DOC)
|
||||
|
||||
matches = query_document_jsonpath(document, "$.headings[?(@.level == 2)].text")
|
||||
|
||||
assert [match.value for match in matches] == [
|
||||
"Context",
|
||||
"Decision",
|
||||
"Consequences",
|
||||
]
|
||||
assert all(match.kind == "heading_value" for match in matches)
|
||||
|
||||
|
||||
def test_query_document_jsonpath_reports_missing_optional_dependency(monkeypatch):
|
||||
document = parse_markdown(QUERY_DOC)
|
||||
|
||||
import builtins
|
||||
|
||||
real_import = builtins.__import__
|
||||
|
||||
def fake_import(name, *args, **kwargs):
|
||||
if name.startswith("jsonpath_ng"):
|
||||
raise ImportError("blocked")
|
||||
return real_import(name, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(builtins, "__import__", fake_import)
|
||||
|
||||
with pytest.raises(InvalidQueryError, match="optional `jsonpath-ng`"):
|
||||
query_document_jsonpath(document, "$.headings[*].text")
|
||||
|
||||
|
||||
def test_mkt_query_outputs_json(tmp_path: Path):
|
||||
source = tmp_path / "doc.md"
|
||||
source.write_text(QUERY_DOC, encoding="utf-8")
|
||||
@@ -136,6 +177,24 @@ def test_mkt_query_outputs_text(tmp_path: Path):
|
||||
assert "## Context" in result.output
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
importlib.util.find_spec("jsonpath_ng") is None,
|
||||
reason="jsonpath-ng optional dependency is not installed",
|
||||
)
|
||||
def test_mkt_query_jsonpath_outputs_json(tmp_path: Path):
|
||||
source = tmp_path / "doc.md"
|
||||
source.write_text(QUERY_DOC, encoding="utf-8")
|
||||
|
||||
result = CliRunner().invoke(
|
||||
main,
|
||||
["query", str(source), "$.frontmatter.status", "--engine", "jsonpath"],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert '"engine": "jsonpath"' in result.output
|
||||
assert '"value": "accepted"' in result.output
|
||||
|
||||
|
||||
def test_mkt_extract_outputs_text(tmp_path: Path):
|
||||
source = tmp_path / "doc.md"
|
||||
source.write_text(QUERY_DOC, encoding="utf-8")
|
||||
|
||||
Reference in New Issue
Block a user