Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ omit =
*/site-packages/*

[report]
fail_under = 95
show_missing = true
exclude_lines =
pragma: no cover
if __name__ == .__main__.:
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
- name: Run tests
run: |
if [ -d tests ]; then
pytest --tb=short -q
pytest --tb=short -q --cov --cov-report=term-missing
else
echo "No tests directory found, skipping pytest"
fi
Expand Down
19 changes: 16 additions & 3 deletions processing/chunker.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,14 @@ def _stash(m: re.Match) -> str:
body = body.strip()
full = (prefix + body).strip()

if not full:
if not full: # pragma: no cover
# Structurally unreachable given how `sections` is built above:
# a section only carries header_line=None with an empty body if
# it's the sole pre-first-header entry, and that's appended only
# when its body is non-whitespace; every other section has a
# non-empty header_line, which alone makes `prefix` (and so
# `full`) non-empty. Kept as a defensive guard in case that
# invariant ever changes.
continue

if len(full) <= MAX_CHARS:
Expand All @@ -128,8 +135,14 @@ def _stash(m: re.Match) -> str:
chunk = (prefix + sb).strip()
if chunk:
all_chunks.append(chunk)
else:
all_chunks.append(full) # fallback: emit as-is
else: # pragma: no cover
# Structurally unreachable: `body` is always non-empty here
# (guarded by the `if not full` check above), and both branches
# of _split_at_paragraphs return at least one element for
# non-empty input -- verified empirically, including with a
# negative `budget`. Kept as a defensive fallback in case that
# invariant ever changes.
all_chunks.append(full)

# Restore fenced code blocks in every chunk
restored: list[str] = []
Expand Down
36 changes: 36 additions & 0 deletions tests/test_ingestion.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,39 @@ def test_load_documents_not_found():
with patch("os.path.exists", return_value=False):
docs = load_documents(["/repo"])
assert len(docs) == 0


def test_load_documents_skips_work_subtree():
# omnibioai/work/ holds UUID- and wftest_*/sweep_*-named runtime copies
# of bundle READMEs that would otherwise shadow the canonical
# omnibioai-workflow-bundles/ paths -- see doc_loader.py's module
# docstring. Any .md under a "work" path segment must never be loaded.
fake_walk = [
("/repo", [], ["README.md"]),
("/repo/work/run-1234", [], ["shadowed.md"]),
]
with patch("os.path.exists", return_value=True), \
patch("os.walk", return_value=iter(fake_walk)), \
patch("builtins.open", mock_open(read_data="real content")):
docs = load_documents(["/repo"])
assert len(docs) == 1
assert docs[0]["source"] == "/repo/README.md"


def test_load_documents_continues_past_unreadable_file():
fake_walk = [("/repo", [], ["bad.md", "good.md"])]
call_count = {"n": 0}

def _open(path, *args, **kwargs):
call_count["n"] += 1
if "bad.md" in path:
raise OSError("permission denied")
return mock_open(read_data="readable content").return_value

with patch("os.path.exists", return_value=True), \
patch("os.walk", return_value=iter(fake_walk)), \
patch("builtins.open", side_effect=_open):
docs = load_documents(["/repo"])

assert len(docs) == 1
assert docs[0]["source"] == "/repo/good.md"
40 changes: 40 additions & 0 deletions tests/test_main_api.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import importlib
import logging
import sys
from unittest.mock import AsyncMock, MagicMock, patch

Expand Down Expand Up @@ -36,6 +38,44 @@ def test_status_endpoint():
assert response.json()["graph_edges"] == 4


def test_status_endpoint_counts_distinct_omnibioai_repos():
with (
patch("api.main.CONTROL_PLANE.status", return_value={"status": "READY"}),
patch("api.main.graph_store") as mock_gs,
patch("api.main.vector_store") as mock_vs,
):
mock_gs.size.return_value = {"nodes": 1, "edges": 2}
mock_vs.index = None
mock_vs.metadata = [
{"source": "/data/omnibioai-workbench/README.md"},
{"source": "/data/omnibioai-workbench/docs/notes.md"}, # same repo, deduped
{"source": "/data/omnibioai-auth/README.md"},
{"source": "/data/unrelated/notes.md"}, # no "omnibioai*" segment at all
]
response = client.get("/status")
assert response.status_code == 200
assert response.json()["repos_loaded"] == 2


def test_warns_when_no_persisted_faiss_index_is_found(caplog):
import api.main as main_module
from index.vector_store import VectorStore

try:
with (
patch.object(VectorStore, "load", return_value=False),
caplog.at_level(logging.WARNING, logger="api.main"),
):
importlib.reload(main_module)
assert any(
"No persisted FAISS index found" in record.message for record in caplog.records
)
finally:
# Restore api.main to a normal (unpatched) import for any test that
# runs after this one and reaches for its module-level state.
importlib.reload(main_module)


def test_guard_requests_middleware_ready():
with (
patch("api.main.CONTROL_PLANE.status", return_value={"status": "READY"}),
Expand Down
103 changes: 102 additions & 1 deletion tests/test_rag_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@

import numpy as np
import pytest
import requests

import rag.engine as _engine_mod
from rag.engine import RAGEngine, cosine, ollama_embed, ollama_generate
from rag.engine import RAGEngine, _load_llm_model, cosine, ollama_embed, ollama_generate

# =========================================================
# UNIT TESTS FOR ollama_embed
Expand Down Expand Up @@ -399,3 +400,103 @@ def test_retrieve_without_rerank_fetches_exact_top_k(mock_embed, engine, mock_ve

call_args = mock_index.search.call_args
assert call_args[0][1] == 5 # exact top_k, no multiplier


# =========================================================
# UNIT TESTS FOR _load_llm_model
# =========================================================

def test_load_llm_model_defaults_when_file_missing(tmp_path):
missing = tmp_path / "does-not-exist.yaml"
assert _load_llm_model(default="fallback-model", path=str(missing)) == "fallback-model"


def test_load_llm_model_defaults_on_malformed_yaml(tmp_path):
bad = tmp_path / "bad.yaml"
bad.write_text("llm_model: [unterminated")
assert _load_llm_model(default="fallback-model", path=str(bad)) == "fallback-model"


def test_load_llm_model_defaults_when_yaml_is_not_a_mapping(tmp_path):
not_a_map = tmp_path / "list.yaml"
not_a_map.write_text("- one\n- two\n")
assert _load_llm_model(default="fallback-model", path=str(not_a_map)) == "fallback-model"


def test_load_llm_model_defaults_when_key_is_absent(tmp_path):
no_key = tmp_path / "no_key.yaml"
no_key.write_text("other_setting: true\n")
assert _load_llm_model(default="fallback-model", path=str(no_key)) == "fallback-model"


def test_load_llm_model_reads_configured_value(tmp_path):
configured = tmp_path / "config.yaml"
configured.write_text("llm_model: mixtral\n")
assert _load_llm_model(default="fallback-model", path=str(configured)) == "mixtral"


# =========================================================
# UNIT TESTS FOR ollama_embed RETRY BEHAVIOR
# =========================================================

@patch("rag.engine.time.sleep")
@patch("rag.engine.requests.post")
def test_ollama_embed_retries_then_succeeds(mock_post, mock_sleep):
failure = requests.exceptions.ConnectionError("transient CUDA context failure")
success = MagicMock()
success.json.return_value = {"embedding": [0.1] * 768}
success.raise_for_status.return_value = None
mock_post.side_effect = [failure, success]

vec = ollama_embed("test text")

assert vec.shape == (768,)
assert mock_post.call_count == 2
mock_sleep.assert_called_once_with(2) # 2 * attempt(1)


@patch("rag.engine.time.sleep")
@patch("rag.engine.requests.post")
def test_ollama_embed_raises_after_exhausting_all_retries(mock_post, mock_sleep):
mock_post.side_effect = requests.exceptions.ConnectionError("still down")

with pytest.raises(requests.exceptions.ConnectionError):
ollama_embed("test text")

assert mock_post.call_count == 3
assert mock_sleep.call_count == 2 # slept between attempts 1->2 and 2->3, not after the last


# =========================================================
# UNIT TESTS FOR RAGEngine.stream_llm
# =========================================================

@patch("rag.engine.requests.post")
def test_stream_llm_yields_tokens_and_stops_on_done(mock_post, engine):
response = MagicMock()
response.raise_for_status.return_value = None
response.iter_lines.return_value = [
b'{"response": "Hello"}',
b"", # blank keep-alive line -- must be skipped, not yielded
b'{"response": " world"}',
b'{"response": "", "done": true}',
b'{"response": "unreachable after done"}',
]
mock_post.return_value = response

tokens = list(engine.stream_llm("query", "context"))

assert tokens == ["Hello", " world"]
_, kwargs = mock_post.call_args
assert kwargs["stream"] is True


@patch("rag.engine.requests.post")
def test_stream_llm_yields_inline_error_token_on_failure(mock_post, engine):
mock_post.side_effect = requests.exceptions.RequestException("ollama unreachable")

tokens = list(engine.stream_llm("query", "context"))

assert len(tokens) == 1
assert tokens[0].startswith("[LLM_ERROR]")
assert "ollama unreachable" in tokens[0]
Loading