import pytest from apps.blog.models import ArticleIndexPage, ArticlePage from apps.blog.tests.factories import AuthorFactory from apps.blog.views import MAX_QUERY_LENGTH @pytest.fixture def search_articles(home_page): """Create an article index with searchable articles.""" index = ArticleIndexPage(title="Articles", slug="articles") home_page.add_child(instance=index) author = AuthorFactory() articles = [] for title, summary in [ ("Understanding LLM Benchmarks", "A deep dive into how language models are evaluated"), ("Local Models on Apple Silicon", "Running open-source models on your MacBook"), ("Agent Frameworks Compared", "Comparing LangChain, CrewAI, and AutoGen"), ]: a = ArticlePage( title=title, slug=title.lower().replace(" ", "-"), author=author, summary=summary, body=[("rich_text", f"

{summary} in detail.

")], ) index.add_child(instance=a) a.save_revision().publish() articles.append(a) return articles @pytest.mark.django_db class TestSearchView: def test_empty_query_returns_no_results(self, client, home_page): resp = client.get("/search/") assert resp.status_code == 200 assert resp.context["query"] == "" assert resp.context["results"] is None def test_whitespace_query_returns_no_results(self, client, home_page): resp = client.get("/search/?q= ") assert resp.status_code == 200 assert resp.context["query"] == "" assert resp.context["results"] is None def test_search_returns_matching_articles(self, client, search_articles): resp = client.get("/search/?q=benchmarks") assert resp.status_code == 200 assert resp.context["query"] == "benchmarks" assert resp.context["results"] is not None def test_search_no_match_returns_empty_page(self, client, search_articles): resp = client.get("/search/?q=zzzznonexistent") assert resp.status_code == 200 assert resp.context["query"] == "zzzznonexistent" # Either None or empty page object results = resp.context["results"] if results is not None: assert len(list(results)) == 0 def test_query_is_truncated_to_max_length(self, client, home_page): long_query = "a" * 500 resp = client.get(f"/search/?q={long_query}") assert resp.status_code == 200 assert len(resp.context["query"]) <= MAX_QUERY_LENGTH def test_query_preserved_in_template(self, client, search_articles): resp = client.get("/search/?q=LLM") html = resp.content.decode() assert 'value="LLM"' in html def test_search_results_page_renders(self, client, search_articles): resp = client.get("/search/?q=models") assert resp.status_code == 200 html = resp.content.decode() assert "Search" in html def test_search_url_resolves(self, client, home_page): from django.urls import reverse assert reverse("search") == "/search/" @pytest.mark.django_db class TestSearchFields: def test_search_fields_include_summary(self): field_names = [ f.field_name for f in ArticlePage.search_fields if hasattr(f, "field_name") ] assert "summary" in field_names def test_search_fields_include_body_text(self): field_names = [ f.field_name for f in ArticlePage.search_fields if hasattr(f, "field_name") ] assert "body_text" in field_names def test_search_fields_include_autocomplete_title(self): from wagtail.search.index import AutocompleteField autocomplete_fields = [ f for f in ArticlePage.search_fields if isinstance(f, AutocompleteField) ] assert any(f.field_name == "title" for f in autocomplete_fields) def test_search_fields_include_related_tags(self): from wagtail.search.index import RelatedFields related = [f for f in ArticlePage.search_fields if isinstance(f, RelatedFields)] assert any(f.field_name == "tags" for f in related) def test_body_text_excludes_code_blocks(self): author = AuthorFactory() article = ArticlePage( title="Test", slug="test", author=author, summary="summary", body=[ ("rich_text", "

prose content here

"), ("code", {"language": "python", "filename": "", "raw_code": "def secret(): pass"}), ], ) assert "prose content here" in article.body_text assert "secret" not in article.body_text @pytest.mark.django_db class TestSearchNavIntegration: def test_nav_contains_search_form(self, client, home_page): resp = client.get("/") html = resp.content.decode() assert 'role="search"' in html assert 'name="q"' in html assert 'placeholder="Search articles..."' in html def test_article_index_contains_search_form(self, client, home_page): index = ArticleIndexPage(title="Articles", slug="articles") home_page.add_child(instance=index) resp = client.get("/articles/") html = resp.content.decode() assert 'name="q"' in html