from pathlib import Path from click.testing import CliRunner from markitect_tool.backend import LocalSnapshotStore, local_index_path_for from markitect_tool.cli import main def test_local_snapshot_store_persists_state_and_document(tmp_path: Path): source = tmp_path / "doc.md" source.write_text("---\ntitle: Example\n---\n# Doc\n\nBody.\n", encoding="utf-8") store = LocalSnapshotStore(tmp_path / ".markitect" / "cache" / "index.sqlite3") state = store.put_file(source, root=tmp_path) loaded = store.load_state() document = store.get_document("doc.md") assert state.path == "doc.md" assert state.snapshot_id.startswith("snapshot:") assert loaded[0].path == "doc.md" assert loaded[0].content_hash == state.content_hash assert document["frontmatter"]["title"] == "Example" assert document["headings"][0]["text"] == "Doc" def test_local_snapshot_store_build_is_incremental(tmp_path: Path): source = tmp_path / "doc.md" source.write_text("# Doc\n", encoding="utf-8") store = LocalSnapshotStore(local_index_path_for(tmp_path)) first = store.build([tmp_path], root=tmp_path) second = store.build([tmp_path], root=tmp_path) assert first.parsed == ["doc.md"] assert first.indexed == ["doc.md"] assert second.parsed == [] assert second.indexed == [] assert not second.dirty source.write_text("# Doc\n\nChanged.\n", encoding="utf-8") changed = store.build([tmp_path], root=tmp_path) assert changed.parsed == ["doc.md"] assert changed.indexed == ["doc.md"] def test_local_snapshot_store_deletes_removed_files(tmp_path: Path): source = tmp_path / "doc.md" source.write_text("# Doc\n", encoding="utf-8") store = LocalSnapshotStore(local_index_path_for(tmp_path)) store.build([tmp_path], root=tmp_path) source.unlink() result = store.build([tmp_path], root=tmp_path) assert result.deleted == ["doc.md"] 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") runner = CliRunner() shown = runner.invoke(main, ["ast", "show", str(source), "--format", "tree"]) stats = runner.invoke(main, ["ast", "stats", str(source)]) assert shown.exit_code == 0 assert "# Doc" in shown.output assert stats.exit_code == 0 assert "headings: 1" in stats.output assert "paragraph_open" in stats.output def test_mkt_cache_init_and_index(tmp_path: Path): source = tmp_path / "doc.md" source.write_text("# Doc\n", encoding="utf-8") runner = CliRunner() initialized = runner.invoke(main, ["cache", "init", "--root", str(tmp_path)]) indexed = runner.invoke(main, ["cache", "index", str(tmp_path), "--root", str(tmp_path)]) clean = runner.invoke(main, ["cache", "index", str(tmp_path), "--root", str(tmp_path)]) assert initialized.exit_code == 0 assert "schema_version: 1" in initialized.output assert indexed.exit_code == 0 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