"""High-value pure logic tests for DomainClassifier and DOMAIN_CATEGORIES.""" import pytest from local_deep_research.domain_classifier.classifier import ( DOMAIN_CATEGORIES, DomainClassifier, ) # --------------------------------------------------------------------------- # DOMAIN_CATEGORIES dict tests # --------------------------------------------------------------------------- class TestDomainCategories: """Tests for the DOMAIN_CATEGORIES constant.""" EXPECTED_MAIN_CATEGORIES = [ "Academic & Research", "News & Media", "Reference & Documentation", "Social & Community", "Business & Commerce", "Technology", "Government & Organization", "Entertainment & Lifestyle", "Professional & Industry", "Other", ] def test_has_exactly_10_main_categories(self): assert len(DOMAIN_CATEGORIES) == 10 @pytest.mark.parametrize("category", EXPECTED_MAIN_CATEGORIES) def test_main_category_exists(self, category): assert category in DOMAIN_CATEGORIES def test_every_category_has_subcategories(self): for cat, subcats in DOMAIN_CATEGORIES.items(): assert isinstance(subcats, list), ( f"{cat} subcategories should be a list" ) assert len(subcats) > 0, ( f"{cat} should have at least one subcategory" ) def test_tech_news_in_news_and_media(self): assert "Tech News" in DOMAIN_CATEGORIES["News & Media"] def test_other_has_unknown_subcategory(self): assert "Unknown" in DOMAIN_CATEGORIES["Other"] def test_other_has_personal_website(self): assert "Personal Website" in DOMAIN_CATEGORIES["Other"] def test_academic_has_scientific_journal(self): assert "Scientific Journal" in DOMAIN_CATEGORIES["Academic & Research"] def test_technology_has_open_source_project(self): assert "Open Source Project" in DOMAIN_CATEGORIES["Technology"] def test_all_subcategories_are_strings(self): for cat, subcats in DOMAIN_CATEGORIES.items(): for sub in subcats: assert isinstance(sub, str), ( f"Subcategory in {cat} must be a string" ) # --------------------------------------------------------------------------- # Helper to build a DomainClassifier without touching DB/LLM # --------------------------------------------------------------------------- def _make_classifier(): """Create a DomainClassifier instance (no real DB or LLM needed).""" return DomainClassifier(username="test_user", settings_snapshot={}) # --------------------------------------------------------------------------- # _build_classification_prompt tests # --------------------------------------------------------------------------- class TestBuildClassificationPrompt: """Tests for _build_classification_prompt pure-logic method.""" def test_prompt_contains_domain_name(self): clf = _make_classifier() prompt = clf._build_classification_prompt("example.com", []) assert "example.com" in prompt def test_prompt_contains_all_main_categories(self): clf = _make_classifier() prompt = clf._build_classification_prompt("example.com", []) for category in DOMAIN_CATEGORIES: assert category in prompt def test_prompt_says_no_samples_when_empty(self): clf = _make_classifier() prompt = clf._build_classification_prompt("example.com", []) assert "No samples available" in prompt def test_prompt_includes_sample_title(self): clf = _make_classifier() samples = [ {"title": "My Great Article", "preview": "Some preview text"} ] prompt = clf._build_classification_prompt("example.com", samples) assert "My Great Article" in prompt def test_prompt_includes_sample_preview(self): clf = _make_classifier() samples = [{"title": "Title", "preview": "A short preview of content"}] prompt = clf._build_classification_prompt("example.com", samples) assert "A short preview of content" in prompt def test_prompt_omits_preview_when_none(self): clf = _make_classifier() samples = [{"title": "Title Only"}] prompt = clf._build_classification_prompt("example.com", samples) assert "Title Only" in prompt assert "Preview:" not in prompt def test_prompt_omits_preview_when_empty_string(self): clf = _make_classifier() samples = [{"title": "Title", "preview": ""}] prompt = clf._build_classification_prompt("example.com", samples) # Empty string is falsy, so no preview line assert "Preview:" not in prompt def test_only_first_5_samples_used(self): clf = _make_classifier() samples = [ {"title": f"Sample {idx}", "preview": f"Preview {idx}"} for idx in range(8) ] prompt = clf._build_classification_prompt("example.com", samples) # Samples 0-4 should appear, samples 5-7 should not assert "Sample 4" in prompt assert "Sample 5" not in prompt def test_preview_truncated_to_100_chars(self): clf = _make_classifier() long_preview = "A" * 200 samples = [{"title": "Title", "preview": long_preview}] prompt = clf._build_classification_prompt("example.com", samples) # The prompt should contain exactly 100 'A' characters followed by "..." assert "A" * 100 + "..." in prompt assert "A" * 101 not in prompt def test_prompt_requests_json_response(self): clf = _make_classifier() prompt = clf._build_classification_prompt("example.com", []) assert "JSON" in prompt def test_prompt_mentions_confidence(self): clf = _make_classifier() prompt = clf._build_classification_prompt("example.com", []) assert "confidence" in prompt def test_prompt_with_multiple_samples_numbered(self): clf = _make_classifier() samples = [ {"title": "First", "preview": "p1"}, {"title": "Second", "preview": "p2"}, {"title": "Third", "preview": "p3"}, ] prompt = clf._build_classification_prompt("example.com", samples) assert "1. Title: First" in prompt assert "2. Title: Second" in prompt assert "3. Title: Third" in prompt def test_prompt_with_missing_preview_key(self): """Sample dict without 'preview' key at all should still work.""" clf = _make_classifier() samples = [{"title": "No Preview Key"}] prompt = clf._build_classification_prompt("example.com", samples) assert "No Preview Key" in prompt assert "Preview:" not in prompt def test_prompt_subcategories_listed(self): """Subcategories should appear in the prompt's category listing.""" clf = _make_classifier() prompt = clf._build_classification_prompt("example.com", []) assert "Q&A Platform" in prompt assert "Developer Tools" in prompt def test_prompt_does_not_say_no_samples_when_samples_present(self): clf = _make_classifier() samples = [{"title": "Exists", "preview": "content"}] prompt = clf._build_classification_prompt("example.com", samples) assert "No samples available" not in prompt class TestDomainClassifierInit: """Tests for DomainClassifier initialisation (no side effects).""" def test_stores_username(self): clf = DomainClassifier(username="alice") assert clf.username == "alice" def test_stores_settings_snapshot(self): snap = {"model": "gpt-4"} clf = DomainClassifier(username="bob", settings_snapshot=snap) assert clf.settings_snapshot is snap def test_llm_starts_as_none(self): clf = DomainClassifier(username="charlie") assert clf.llm is None def test_default_settings_snapshot_is_none(self): clf = DomainClassifier(username="dave") assert clf.settings_snapshot is None