import json import os from argparse import Namespace from pathlib import Path from unittest.mock import patch import pytest os.environ.setdefault("OPENAI_BASE_URL", "http://test/v1") from src.memory.index import _extract_text, run_index class TestExtractText: def test_with_frontmatter(self) -> None: md = "---\ntitle: test\n---\n\nbody content" assert _extract_text(md) == "body content" def test_no_frontmatter(self) -> None: md = "just content" assert _extract_text(md) == "just content" def test_empty(self) -> None: assert _extract_text("") == "" class TestRunIndex: def test_index_creates_json(self, tmp_path: Path) -> None: memory_dir = tmp_path / "memory" index_dir = tmp_path / ".rag" memory_dir.mkdir(parents=True) (memory_dir / "test.md").write_text("---\ntitle: test\n---\n\nhello world") def fake_embed_texts(texts): return [[0.1, 0.2, 0.3]] * len(texts) with patch("src.memory.index.embed_texts", fake_embed_texts): args = Namespace(memory_dir=str(memory_dir), output=str(index_dir)) run_index(args) assert (index_dir / "index.json").exists() index = json.loads((index_dir / "index.json").read_text()) assert len(index["files"]) == 1 assert index["files"][0]["source"] == "test.md" assert index["files"][0]["embedding"] == [0.1, 0.2, 0.3] assert index["files"][0]["chunk_idx"] == 0 assert index["files"][0]["offset"] == 0 def test_no_md_files(self, tmp_path: Path, capsys) -> None: memory_dir = tmp_path / "empty" index_dir = tmp_path / ".rag" memory_dir.mkdir(parents=True) args = Namespace(memory_dir=str(memory_dir), output=str(index_dir)) run_index(args) captured = capsys.readouterr() assert "No .md files found" in captured.out def test_chunking_long_file(self, tmp_path: Path) -> None: memory_dir = tmp_path / "memory" index_dir = tmp_path / ".rag" memory_dir.mkdir(parents=True) long_body = "a" * 1500 (memory_dir / "long.md").write_text(long_body) def fake_embed_texts(texts): return [[0.1, 0.2, 0.3]] * len(texts) with patch("src.memory.index.embed_texts", fake_embed_texts): args = Namespace(memory_dir=str(memory_dir), output=str(index_dir)) run_index(args) index = json.loads((index_dir / "index.json").read_text()) assert len(index["files"]) >= 3 chunk_idxs = [f["chunk_idx"] for f in index["files"]] assert chunk_idxs == sorted(chunk_idxs) assert chunk_idxs[0] == 0 assert index["files"][0]["offset"] == 0 offsets = [f["offset"] for f in index["files"]] assert offsets == sorted(offsets) def test_chunking_size_env_override( self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: memory_dir = tmp_path / "memory" index_dir = tmp_path / ".rag" memory_dir.mkdir(parents=True) monkeypatch.setenv("MEMORY_CHUNK_SIZE", "256") monkeypatch.setenv("MEMORY_CHUNK_OVERLAP", "0") (memory_dir / "f.md").write_text("a" * 600) def fake_embed_texts(texts): return [[0.1]] * len(texts) with patch("src.memory.index.embed_texts", fake_embed_texts): args = Namespace(memory_dir=str(memory_dir), output=str(index_dir)) run_index(args) index = json.loads((index_dir / "index.json").read_text()) assert len(index["files"]) == 3 for i, f in enumerate(index["files"]): assert f["chunk_idx"] == i assert f["offset"] == i * 256 def test_chunking_skip_rag_dir(self, tmp_path: Path) -> None: memory_dir = tmp_path / "memory" index_dir = memory_dir / ".rag" memory_dir.mkdir(parents=True) (memory_dir / "real.md").write_text("real content") (index_dir).mkdir() (index_dir / "index.json").write_text('{"files": []}') (index_dir / "ignore.md").write_text("should be ignored") def fake_embed_texts(texts): return [[0.1]] * len(texts) output_dir = tmp_path / "out" with patch("src.memory.index.embed_texts", fake_embed_texts): args = Namespace(memory_dir=str(memory_dir), output=str(output_dir)) run_index(args) index = json.loads((output_dir / "index.json").read_text()) sources = [f["source"] for f in index["files"]] assert "real.md" in sources assert all(".rag" not in s for s in sources)