"""Unit tests for content.archive.indexer.""" from __future__ import annotations from unittest.mock import patch import pytest from content.archive.indexer import ( EpisodeDocument, IndexResult, _meilisearch_available, index_episode, search_episodes, ) # ── _meilisearch_available ──────────────────────────────────────────────────── class TestMeilisearchAvailable: def test_returns_bool(self): assert isinstance(_meilisearch_available(), bool) def test_false_when_spec_missing(self): with patch("importlib.util.find_spec", return_value=None): assert _meilisearch_available() is False # ── EpisodeDocument ─────────────────────────────────────────────────────────── class TestEpisodeDocument: def test_to_dict_contains_id(self): doc = EpisodeDocument(id="ep-001", title="Test") d = doc.to_dict() assert d["id"] == "ep-001" def test_to_dict_contains_title(self): doc = EpisodeDocument(id="ep-001", title="My Episode") assert doc.to_dict()["title"] == "My Episode" def test_to_dict_defaults(self): doc = EpisodeDocument(id="ep-001", title="T") d = doc.to_dict() assert d["tags"] == [] assert d["highlight_ids"] == [] assert d["duration"] == 0.0 assert d["clip_count"] == 0 def test_to_dict_preserves_tags(self): doc = EpisodeDocument(id="ep-001", title="T", tags=["gaming", "highlights"]) assert doc.to_dict()["tags"] == ["gaming", "highlights"] def test_to_dict_all_fields(self): doc = EpisodeDocument( id="ep-002", title="Full", description="Desc", tags=["t"], published_at="2026-03-23T00:00:00Z", youtube_url="https://yt.com/x", blossom_url="https://blossom.io/x", duration=180.0, clip_count=5, highlight_ids=["h1", "h2"], ) d = doc.to_dict() assert d["description"] == "Desc" assert d["youtube_url"] == "https://yt.com/x" assert d["duration"] == 180.0 assert d["highlight_ids"] == ["h1", "h2"] # ── index_episode ───────────────────────────────────────────────────────────── class TestIndexEpisode: @pytest.mark.asyncio async def test_empty_id_returns_failure(self): result = await index_episode("", "Title") assert result.success is False assert "episode_id" in result.error @pytest.mark.asyncio async def test_whitespace_id_returns_failure(self): result = await index_episode(" ", "Title") assert result.success is False @pytest.mark.asyncio async def test_returns_failure_when_meilisearch_missing(self): with patch("content.archive.indexer._meilisearch_available", return_value=False): result = await index_episode("ep-001", "Title") assert result.success is False assert "meilisearch" in result.error.lower() @pytest.mark.asyncio async def test_successful_indexing(self): fake_result = IndexResult(success=True, document_id="ep-001") with ( patch("content.archive.indexer._meilisearch_available", return_value=True), patch("asyncio.to_thread", return_value=fake_result), ): result = await index_episode( "ep-001", "Test Episode", description="A test", tags=["gaming"], published_at="2026-03-23T00:00:00Z", youtube_url="https://yt.com/abc", duration=120.0, clip_count=3, highlight_ids=["h1", "h2", "h3"], ) assert result.success is True assert result.document_id == "ep-001" @pytest.mark.asyncio async def test_exception_from_thread_returns_failure(self): with ( patch("content.archive.indexer._meilisearch_available", return_value=True), patch("asyncio.to_thread", side_effect=RuntimeError("connection refused")), ): result = await index_episode("ep-001", "Title") assert result.success is False assert "connection refused" in result.error # ── search_episodes ─────────────────────────────────────────────────────────── class TestSearchEpisodes: @pytest.mark.asyncio async def test_returns_empty_when_library_missing(self): with patch("content.archive.indexer._meilisearch_available", return_value=False): results = await search_episodes("highlights") assert results == [] @pytest.mark.asyncio async def test_returns_hits_on_success(self): fake_hits = [{"id": "ep-001", "title": "Gaming Highlights"}] with ( patch("content.archive.indexer._meilisearch_available", return_value=True), patch("asyncio.to_thread", return_value=fake_hits), ): results = await search_episodes("gaming") assert len(results) == 1 assert results[0]["id"] == "ep-001" @pytest.mark.asyncio async def test_returns_empty_on_exception(self): with ( patch("content.archive.indexer._meilisearch_available", return_value=True), patch("asyncio.to_thread", side_effect=RuntimeError("timeout")), ): results = await search_episodes("query") assert results == [] @pytest.mark.asyncio async def test_empty_list_when_no_results(self): with ( patch("content.archive.indexer._meilisearch_available", return_value=True), patch("asyncio.to_thread", return_value=[]), ): results = await search_episodes("nothing matches") assert results == []