Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

216 lines
8.0 KiB
Python

"""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