""" Tests for advanced_search_system base classes. Tests cover: - Constraint and ConstraintType - Candidate class - Evidence and EvidenceType - BaseFilter abstract class """ import pytest from unittest.mock import Mock class TestConstraintType: """Tests for ConstraintType enum.""" def test_constraint_type_values(self): """Test all constraint type enum values exist.""" from local_deep_research.advanced_search_system.constraints.base_constraint import ( ConstraintType, ) assert ConstraintType.PROPERTY.value == "property" assert ConstraintType.NAME_PATTERN.value == "name_pattern" assert ConstraintType.EVENT.value == "event" assert ConstraintType.STATISTIC.value == "statistic" assert ConstraintType.TEMPORAL.value == "temporal" assert ConstraintType.LOCATION.value == "location" assert ConstraintType.COMPARISON.value == "comparison" assert ConstraintType.EXISTENCE.value == "existence" def test_constraint_type_count(self): """Test that all constraint types are defined.""" from local_deep_research.advanced_search_system.constraints.base_constraint import ( ConstraintType, ) assert len(ConstraintType) == 8 class TestConstraint: """Tests for Constraint dataclass.""" def test_constraint_creation_minimal(self): """Test creating constraint with minimal required fields.""" from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) constraint = Constraint( id="c1", type=ConstraintType.PROPERTY, description="Test constraint", value="test value", ) assert constraint.id == "c1" assert constraint.type == ConstraintType.PROPERTY assert constraint.description == "Test constraint" assert constraint.value == "test value" assert constraint.weight == 1.0 assert constraint.metadata == {} def test_constraint_creation_full(self): """Test creating constraint with all fields.""" from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) metadata = {"source": "test"} constraint = Constraint( id="c2", type=ConstraintType.TEMPORAL, description="Time constraint", value="2024", weight=0.5, metadata=metadata, ) assert constraint.id == "c2" assert constraint.weight == 0.5 assert constraint.metadata == metadata def test_constraint_post_init_creates_metadata(self): """Test that __post_init__ creates empty metadata dict.""" from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) constraint = Constraint( id="c3", type=ConstraintType.LOCATION, description="Location", value="Colorado", ) assert constraint.metadata is not None assert isinstance(constraint.metadata, dict) def test_constraint_to_search_terms_property(self): """Test to_search_terms for PROPERTY type.""" from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) constraint = Constraint( id="c1", type=ConstraintType.PROPERTY, description="Test", value="formed during ice age", ) result = constraint.to_search_terms() assert result == "formed during ice age" def test_constraint_to_search_terms_name_pattern(self): """Test to_search_terms for NAME_PATTERN type.""" from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) constraint = Constraint( id="c1", type=ConstraintType.NAME_PATTERN, description="Test", value="body part", ) result = constraint.to_search_terms() assert "body part" in result assert "name" in result assert "trail" in result def test_constraint_to_search_terms_event(self): """Test to_search_terms for EVENT type.""" from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) constraint = Constraint( id="c1", type=ConstraintType.EVENT, description="Test", value="rockslide 2020", ) result = constraint.to_search_terms() assert "rockslide 2020" in result assert "accident" in result or "incident" in result def test_constraint_to_search_terms_statistic(self): """Test to_search_terms for STATISTIC type.""" from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) constraint = Constraint( id="c1", type=ConstraintType.STATISTIC, description="Test", value="84.5x ratio", ) result = constraint.to_search_terms() assert "84.5x ratio" in result assert "statistics" in result or "data" in result def test_constraint_to_search_terms_other(self): """Test to_search_terms for other types (e.g., TEMPORAL).""" from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) constraint = Constraint( id="c1", type=ConstraintType.TEMPORAL, description="Test", value="2024", ) result = constraint.to_search_terms() assert result == "2024" def test_constraint_is_critical_name_pattern(self): """Test is_critical returns True for NAME_PATTERN type.""" from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) constraint = Constraint( id="c1", type=ConstraintType.NAME_PATTERN, description="Test", value="test", weight=0.5, # Low weight but still critical ) assert constraint.is_critical() is True def test_constraint_is_critical_high_weight(self): """Test is_critical returns True for high weight constraints.""" from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) constraint = Constraint( id="c1", type=ConstraintType.PROPERTY, description="Test", value="test", weight=0.9, ) assert constraint.is_critical() is True def test_constraint_is_critical_low_weight(self): """Test is_critical returns False for low weight non-NAME_PATTERN.""" from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) constraint = Constraint( id="c1", type=ConstraintType.PROPERTY, description="Test", value="test", weight=0.5, ) assert constraint.is_critical() is False class TestEvidenceType: """Tests for EvidenceType enum.""" def test_evidence_type_values(self): """Test all evidence type enum values exist.""" from local_deep_research.advanced_search_system.evidence.base_evidence import ( EvidenceType, ) assert EvidenceType.DIRECT_STATEMENT.value == "direct_statement" assert EvidenceType.OFFICIAL_RECORD.value == "official_record" assert EvidenceType.RESEARCH_FINDING.value == "research_finding" assert EvidenceType.NEWS_REPORT.value == "news_report" assert EvidenceType.STATISTICAL_DATA.value == "statistical_data" assert EvidenceType.INFERENCE.value == "inference" assert EvidenceType.CORRELATION.value == "correlation" assert EvidenceType.SPECULATION.value == "speculation" def test_evidence_type_base_confidence(self): """Test base confidence values for evidence types.""" from local_deep_research.advanced_search_system.evidence.base_evidence import ( EvidenceType, ) assert EvidenceType.DIRECT_STATEMENT.base_confidence == 0.95 assert EvidenceType.OFFICIAL_RECORD.base_confidence == 0.90 assert EvidenceType.RESEARCH_FINDING.base_confidence == 0.85 assert EvidenceType.STATISTICAL_DATA.base_confidence == 0.85 assert EvidenceType.NEWS_REPORT.base_confidence == 0.75 assert EvidenceType.INFERENCE.base_confidence == 0.50 assert EvidenceType.CORRELATION.base_confidence == 0.30 assert EvidenceType.SPECULATION.base_confidence == 0.10 class TestEvidence: """Tests for Evidence dataclass.""" def test_evidence_creation_minimal(self): """Test creating evidence with minimal fields.""" from local_deep_research.advanced_search_system.evidence.base_evidence import ( Evidence, EvidenceType, ) evidence = Evidence( claim="Test claim", type=EvidenceType.NEWS_REPORT, source="https://example.com", ) assert evidence.claim == "Test claim" assert evidence.type == EvidenceType.NEWS_REPORT assert evidence.source == "https://example.com" # Post-init should set confidence based on type assert evidence.confidence == 0.75 def test_evidence_creation_with_confidence(self): """Test creating evidence with explicit confidence.""" from local_deep_research.advanced_search_system.evidence.base_evidence import ( Evidence, EvidenceType, ) evidence = Evidence( claim="Test claim", type=EvidenceType.NEWS_REPORT, source="https://example.com", confidence=0.9, ) # Should keep provided confidence, not override assert evidence.confidence == 0.9 def test_evidence_creation_full(self): """Test creating evidence with all fields.""" from local_deep_research.advanced_search_system.evidence.base_evidence import ( Evidence, EvidenceType, ) evidence = Evidence( claim="Test claim", type=EvidenceType.OFFICIAL_RECORD, source="https://official.gov", confidence=0.95, reasoning="Found in official database", raw_text="The official record states...", metadata={"page": 1}, ) assert evidence.claim == "Test claim" assert evidence.reasoning == "Found in official database" assert evidence.raw_text == "The official record states..." assert evidence.metadata == {"page": 1} def test_evidence_timestamp_auto_generated(self): """Test that timestamp is automatically generated.""" from local_deep_research.advanced_search_system.evidence.base_evidence import ( Evidence, EvidenceType, ) evidence = Evidence( claim="Test", type=EvidenceType.INFERENCE, source="test", ) assert evidence.timestamp is not None assert len(evidence.timestamp) > 0 def test_evidence_post_init_sets_confidence(self): """Test __post_init__ sets confidence based on type when 0.""" from local_deep_research.advanced_search_system.evidence.base_evidence import ( Evidence, EvidenceType, ) evidence = Evidence( claim="Test", type=EvidenceType.DIRECT_STATEMENT, source="test", confidence=0.0, # Will be replaced ) assert evidence.confidence == 0.95 class TestCandidate: """Tests for Candidate dataclass.""" def test_candidate_creation_minimal(self): """Test creating candidate with minimal fields.""" from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) candidate = Candidate(name="Test Candidate") assert candidate.name == "Test Candidate" assert candidate.evidence == {} assert candidate.score == 0.0 assert candidate.metadata == {} def test_candidate_add_evidence(self): """Test adding evidence to a candidate.""" from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) from local_deep_research.advanced_search_system.evidence.base_evidence import ( Evidence, EvidenceType, ) candidate = Candidate(name="Test") evidence = Evidence( claim="Test claim", type=EvidenceType.NEWS_REPORT, source="https://example.com", ) candidate.add_evidence("constraint_1", evidence) assert "constraint_1" in candidate.evidence assert candidate.evidence["constraint_1"] == evidence def test_candidate_calculate_score_empty(self): """Test score calculation with no constraints.""" from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) candidate = Candidate(name="Test") score = candidate.calculate_score([]) assert score == 0.0 def test_candidate_calculate_score_with_evidence(self): """Test score calculation with evidence.""" from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) from local_deep_research.advanced_search_system.evidence.base_evidence import ( Evidence, EvidenceType, ) candidate = Candidate(name="Test") # Create constraints c1 = Constraint( id="c1", type=ConstraintType.PROPERTY, description="Test 1", value="test", weight=1.0, ) c2 = Constraint( id="c2", type=ConstraintType.PROPERTY, description="Test 2", value="test", weight=1.0, ) # Add evidence with 0.8 confidence evidence = Evidence( claim="Test", type=EvidenceType.NEWS_REPORT, source="test", confidence=0.8, ) candidate.add_evidence("c1", evidence) score = candidate.calculate_score([c1, c2]) # Score = (0.8 * 1.0) / (1.0 + 1.0) = 0.4 assert score == 0.4 assert candidate.score == 0.4 def test_candidate_calculate_score_weighted(self): """Test score calculation with weighted constraints.""" from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) from local_deep_research.advanced_search_system.evidence.base_evidence import ( Evidence, EvidenceType, ) candidate = Candidate(name="Test") # Create constraints with different weights c1 = Constraint( id="c1", type=ConstraintType.PROPERTY, description="Critical", value="test", weight=2.0, ) c2 = Constraint( id="c2", type=ConstraintType.PROPERTY, description="Minor", value="test", weight=0.5, ) # Add evidence for c1 only evidence = Evidence( claim="Test", type=EvidenceType.DIRECT_STATEMENT, source="test", confidence=1.0, ) candidate.add_evidence("c1", evidence) score = candidate.calculate_score([c1, c2]) # Score = (1.0 * 2.0) / (2.0 + 0.5) = 2.0 / 2.5 = 0.8 assert score == 0.8 def test_candidate_get_unverified_constraints(self): """Test getting unverified constraints.""" from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) from local_deep_research.advanced_search_system.constraints.base_constraint import ( Constraint, ConstraintType, ) from local_deep_research.advanced_search_system.evidence.base_evidence import ( Evidence, EvidenceType, ) candidate = Candidate(name="Test") c1 = Constraint( id="c1", type=ConstraintType.PROPERTY, description="Test 1", value="test", ) c2 = Constraint( id="c2", type=ConstraintType.PROPERTY, description="Test 2", value="test", ) c3 = Constraint( id="c3", type=ConstraintType.PROPERTY, description="Test 3", value="test", ) # Add evidence for c1 only evidence = Evidence( claim="Test", type=EvidenceType.NEWS_REPORT, source="test", ) candidate.add_evidence("c1", evidence) unverified = candidate.get_unverified_constraints([c1, c2, c3]) assert len(unverified) == 2 assert c2 in unverified assert c3 in unverified assert c1 not in unverified def test_candidate_get_weak_evidence(self): """Test getting constraints with weak evidence.""" from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) from local_deep_research.advanced_search_system.evidence.base_evidence import ( Evidence, EvidenceType, ) candidate = Candidate(name="Test") # Add strong evidence strong = Evidence( claim="Strong", type=EvidenceType.DIRECT_STATEMENT, source="test", confidence=0.9, ) candidate.add_evidence("c1", strong) # Add weak evidence weak = Evidence( claim="Weak", type=EvidenceType.SPECULATION, source="test", confidence=0.3, ) candidate.add_evidence("c2", weak) weak_constraints = candidate.get_weak_evidence(threshold=0.5) assert len(weak_constraints) == 1 assert "c2" in weak_constraints assert "c1" not in weak_constraints def test_candidate_get_weak_evidence_custom_threshold(self): """Test getting weak evidence with custom threshold.""" from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) from local_deep_research.advanced_search_system.evidence.base_evidence import ( Evidence, EvidenceType, ) candidate = Candidate(name="Test") evidence = Evidence( claim="Medium", type=EvidenceType.NEWS_REPORT, source="test", confidence=0.7, ) candidate.add_evidence("c1", evidence) # With default threshold of 0.5, should not be weak weak_default = candidate.get_weak_evidence() assert "c1" not in weak_default # With higher threshold, should be weak weak_high = candidate.get_weak_evidence(threshold=0.8) assert "c1" in weak_high class TestBaseFilter: """Tests for BaseFilter abstract class.""" def test_base_filter_is_abstract(self): """Test that BaseFilter is abstract and cannot be instantiated.""" from local_deep_research.advanced_search_system.filters.base_filter import ( BaseFilter, ) with pytest.raises(TypeError): BaseFilter() def test_base_filter_with_model(self): """Test BaseFilter subclass with model.""" from local_deep_research.advanced_search_system.filters.base_filter import ( BaseFilter, ) class ConcreteFilter(BaseFilter): def filter_results(self, results, query, **kwargs): return results mock_model = Mock() filter_instance = ConcreteFilter(model=mock_model) assert filter_instance.model == mock_model def test_base_filter_without_model(self): """Test BaseFilter subclass without model.""" from local_deep_research.advanced_search_system.filters.base_filter import ( BaseFilter, ) class ConcreteFilter(BaseFilter): def filter_results(self, results, query, **kwargs): return results filter_instance = ConcreteFilter() assert filter_instance.model is None def test_base_filter_filter_results_abstract(self): """Test that filter_results must be implemented.""" from local_deep_research.advanced_search_system.filters.base_filter import ( BaseFilter, ) class IncompleteFilter(BaseFilter): pass with pytest.raises(TypeError): IncompleteFilter() def test_base_filter_concrete_implementation(self): """Test a concrete filter implementation.""" from local_deep_research.advanced_search_system.filters.base_filter import ( BaseFilter, ) class RelevanceFilter(BaseFilter): def filter_results(self, results, query, **kwargs): threshold = kwargs.get("threshold", 0.5) return [r for r in results if r.get("score", 0) >= threshold] filter_instance = RelevanceFilter() results = [ {"title": "A", "score": 0.9}, {"title": "B", "score": 0.3}, {"title": "C", "score": 0.7}, ] filtered = filter_instance.filter_results(results, "test query") assert len(filtered) == 2 assert {"title": "A", "score": 0.9} in filtered assert {"title": "C", "score": 0.7} in filtered