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
6 changes: 5 additions & 1 deletion backend/indexer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
class CompassIndexer:
def __init__(self, model: str, workspace: str):
self.client = PageIndexClient(model=model, workspace=workspace)
self._structure_cache: dict[str, str] = {}

def index_document(self, filepath: str) -> str | None:
path = Path(filepath)
Expand All @@ -33,10 +34,13 @@ def index_document(self, filepath: str) -> str | None:
logger.info(f"Indexing: {path.name}")
doc_id = self.client.index(str(path))
logger.info(f"Done: {path.name} → {doc_id}")
self._structure_cache.pop(doc_id, None) # invalidate on re-index
return doc_id

def get_structure(self, doc_id: str) -> str:
return self.client.get_document_structure(doc_id)
if doc_id not in self._structure_cache:
self._structure_cache[doc_id] = self.client.get_document_structure(doc_id)
return self._structure_cache[doc_id]

def get_page_content(self, doc_id: str, pages: str) -> str:
return self.client.get_page_content(doc_id, pages)
Expand Down
87 changes: 87 additions & 0 deletions tests/test_indexer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""
Unit tests for CompassIndexer — focuses on caching and deduplication logic.
PageIndexClient is mocked so no disk I/O or LLM calls are made.
"""

import pytest
from unittest.mock import MagicMock, patch, call


def _make_client(doc_name="doc", structure='[{"title": "T", "nodes": []}]'):
client = MagicMock()
client.documents = {
"doc-001": {"doc_name": doc_name, "type": "md", "page_count": None, "doc_description": ""}
}
client.get_document_structure.return_value = structure
client.index.return_value = "doc-002"
return client


@pytest.fixture
def indexer():
with patch("backend.indexer.PageIndexClient") as MockClient:
MockClient.return_value = _make_client()
from backend.indexer import CompassIndexer
idx = CompassIndexer(model="test-model", workspace="/tmp/ws")
yield idx


class TestStructureCache:
def test_first_call_hits_client(self, indexer):
indexer.get_structure("doc-001")
indexer.client.get_document_structure.assert_called_once_with("doc-001")

def test_second_call_uses_cache(self, indexer):
indexer.get_structure("doc-001")
indexer.get_structure("doc-001")
# client should only be called once
indexer.client.get_document_structure.assert_called_once()

def test_cache_returns_same_value(self, indexer):
first = indexer.get_structure("doc-001")
second = indexer.get_structure("doc-001")
assert first == second

def test_cache_invalidated_on_reindex(self, indexer):
# Pre-populate cache
indexer._structure_cache["doc-001"] = "cached-structure"

# Simulate what index_document does after a successful index call
indexer._structure_cache.pop("doc-001", None)

assert "doc-001" not in indexer._structure_cache

def test_cache_pop_is_called_after_index(self, indexer):
indexer._structure_cache["doc-002"] = "stale"
indexer.client.index.return_value = "doc-002"
# doc-002 is not in existing documents, so it will be indexed
indexer.client.documents = {}

indexer.index_document("newfile.md")

assert "doc-002" not in indexer._structure_cache

def test_different_docs_cached_independently(self, indexer):
indexer.client.documents["doc-002"] = {
"doc_name": "other", "type": "md", "page_count": None, "doc_description": ""
}
indexer.client.get_document_structure.side_effect = lambda doc_id: f"structure-{doc_id}"

s1 = indexer.get_structure("doc-001")
s2 = indexer.get_structure("doc-002")

assert s1 == "structure-doc-001"
assert s2 == "structure-doc-002"
assert indexer.client.get_document_structure.call_count == 2


class TestDeduplication:
def test_does_not_reindex_existing_doc(self, indexer):
result = indexer.index_document("doc.md")
assert result == "doc-001"
indexer.client.index.assert_not_called()

def test_indexes_new_doc(self, indexer):
result = indexer.index_document("new-file.md")
assert result == "doc-002"
indexer.client.index.assert_called_once()
Loading