Skip to content

Commit 6910f3e

Browse files
committed
Add pytest suite and CI workflow
1 parent b129ba0 commit 6910f3e

File tree

5 files changed

+173
-4
lines changed

5 files changed

+173
-4
lines changed

.github/workflows/tests.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: Tests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
pytest:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- uses: actions/checkout@v4
13+
- name: Set up uv
14+
uses: astral-sh/setup-uv@v3
15+
with:
16+
python-version: "3.13"
17+
- name: Install dependencies
18+
run: uv sync --dev
19+
- name: Run tests
20+
run: uv run --dev pytest

Makefile

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# Create/refresh a local .venv and install deps from pyproject/uv.lock
44
install:
5-
uv sync
5+
uv sync --dev
66

77
# Optional: create venv explicitly (uv sync will also create one if missing)
88
venv:
@@ -13,11 +13,15 @@ ingest:
1313
uv run ingest.py
1414

1515
chat:
16-
uv run chat.py
16+
uv run chat.py
17+
18+
# Run the automated test suite
19+
test:
20+
uv run --dev pytest
1721

1822
# Create/update a lockfile explicitly (optional; uv sync also updates it)
1923
lock:
20-
uv lock
24+
uv lock
2125

2226
clean:
2327
rm -rf .rag

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
A fully local RAG pipeline using LlamaIndex + Ollama + Chroma to query your Logseq notes.
44

55
## Prereqs
6-
- Python 3.10+
6+
- Python 3.13+
77
- Ollama running (https://ollama.com)
88
- Pull a chat and embedding model:
99
```bash
@@ -36,6 +36,11 @@ make ingest
3636
make chat
3737
```
3838

39+
## Tests
40+
```bash
41+
make test
42+
```
43+
3944
### Example questions
4045
- Summarize tasks tagged #home in October 2025.
4146
- Find notes referencing [[Team Topologies]] and list my pros/cons.

pyproject.toml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,15 @@ dependencies = [
1212
"llama-index-vector-stores-chroma>=0.5.3",
1313
"pyyaml>=6.0.3",
1414
]
15+
16+
[tool.uv]
17+
dev-dependencies = [
18+
"pytest>=8.3.3",
19+
]
20+
21+
[tool.pytest.ini_options]
22+
minversion = "8.0"
23+
addopts = "-ra"
24+
testpaths = [
25+
"tests",
26+
]

tests/test_ingest.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import importlib
2+
import sys
3+
from pathlib import Path
4+
import textwrap
5+
6+
import pytest
7+
8+
9+
@pytest.fixture(scope="session")
10+
def ingest_module():
11+
project_root = Path(__file__).resolve().parents[1]
12+
config_path = project_root / "config.yaml"
13+
created = False
14+
15+
if not config_path.exists():
16+
config_path.write_text(
17+
textwrap.dedent(
18+
"""
19+
logseq_root: /tmp
20+
include_dirs: []
21+
file_exts: []
22+
exclude_globs: []
23+
models:
24+
llm: llama3.1
25+
embedding: nomic-embed-text
26+
storage:
27+
chroma_path: /tmp/chroma
28+
retrieval:
29+
top_k: 5
30+
mmr: false
31+
chunk:
32+
chunk_size: 512
33+
chunk_overlap: 50
34+
"""
35+
).strip()
36+
)
37+
created = True
38+
39+
try:
40+
if "ingest" in sys.modules:
41+
module = sys.modules["ingest"]
42+
else:
43+
module = importlib.import_module("ingest")
44+
yield module
45+
finally:
46+
if created and config_path.exists():
47+
config_path.unlink()
48+
49+
50+
def test_normalize_logseq_links(ingest_module):
51+
text = "Follow [[Page Name]] then see ((abc123))."
52+
result = ingest_module.normalize_logseq_links(text)
53+
assert result == "Follow Page Name then see [ref:abc123]."
54+
55+
56+
def test_parse_tags_combines_sources(ingest_module):
57+
text = """
58+
#alpha introduces the topic
59+
Another line with #beta and #alpha
60+
tags:: gamma, beta , delta
61+
"""
62+
result = ingest_module.parse_tags(text)
63+
assert result == ["alpha", "beta", "delta", "gamma"]
64+
65+
66+
def test_page_title_from_path(ingest_module):
67+
path = "/tmp/logseq/pages/project_notes.md"
68+
assert ingest_module.page_title_from_path(path) == "project-notes"
69+
70+
71+
def test_collect_files_respects_ext_and_excludes(tmp_path, ingest_module):
72+
pages = tmp_path / "pages"
73+
journals = tmp_path / "journals"
74+
archive = pages / "archive"
75+
pages.mkdir()
76+
journals.mkdir()
77+
archive.mkdir()
78+
79+
keep_pages = pages / "alpha.md"
80+
keep_journal = journals / "2025-01-01.md"
81+
ignore_ext = pages / "ignore.txt"
82+
excluded = archive / "old.md"
83+
84+
keep_pages.write_text("alpha")
85+
keep_journal.write_text("journal")
86+
ignore_ext.write_text("nope")
87+
excluded.write_text("archive")
88+
89+
found = ingest_module.collect_files(
90+
str(tmp_path),
91+
["pages", "journals"],
92+
[".md"],
93+
["pages/archive/*"],
94+
)
95+
96+
assert set(found) == {str(keep_pages), str(keep_journal)}
97+
98+
99+
def test_load_documents_applies_metadata(monkeypatch, tmp_path, ingest_module):
100+
docs_dir = tmp_path / "pages"
101+
docs_dir.mkdir()
102+
doc_path = docs_dir / "demo_page.md"
103+
doc_path.write_text(
104+
"""
105+
#alpha tag at the top
106+
tags:: beta, alpha
107+
Content referencing [[Other Page]] and ((xyz789)).
108+
"""
109+
)
110+
111+
class DummyDocument:
112+
def __init__(self, text, metadata):
113+
self.text = text
114+
self.metadata = metadata
115+
116+
monkeypatch.setattr(ingest_module, "Document", DummyDocument)
117+
118+
docs = ingest_module.load_documents([str(doc_path)])
119+
120+
assert len(docs) == 1
121+
doc = docs[0]
122+
assert doc.text.strip().startswith("#alpha tag at the top")
123+
assert "[[" not in doc.text and "((" not in doc.text
124+
assert doc.metadata["source"] == str(doc_path)
125+
assert doc.metadata["title"] == "demo-page"
126+
assert doc.metadata["tags"] == "alpha, beta"
127+
assert doc.metadata["basename"] == "demo_page.md"
128+
assert doc.metadata["dir"] == "pages"

0 commit comments

Comments
 (0)