""" Behavioral tests for base_candidate module. Tests Candidate dataclass with evidence management and scoring logic. """ import pytest class TestCandidateInit: """Tests for Candidate dataclass initialization.""" def test_basic_construction(self): from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) c = Candidate(name="Lake X") assert c.name == "Lake X" def test_evidence_defaults_empty_dict(self): from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) c = Candidate(name="Lake X") assert c.evidence == {} def test_score_defaults_zero(self): from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) c = Candidate(name="Lake X") assert c.score == 0.0 def test_metadata_defaults_empty_dict(self): from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) c = Candidate(name="Lake X") assert c.metadata == {} class TestCandidateAddEvidence: """Tests for Candidate.add_evidence() method.""" def test_add_single_evidence(self): 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, ) c = Candidate(name="Lake X") e = Evidence( claim="formed in ice age", type=EvidenceType.RESEARCH_FINDING, source="wiki", ) c.add_evidence("c1", e) assert "c1" in c.evidence assert c.evidence["c1"] is e def test_add_multiple_evidence(self): 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, ) c = Candidate(name="Lake X") e1 = Evidence( claim="claim1", type=EvidenceType.DIRECT_STATEMENT, source="s1" ) e2 = Evidence( claim="claim2", type=EvidenceType.NEWS_REPORT, source="s2" ) c.add_evidence("c1", e1) c.add_evidence("c2", e2) assert len(c.evidence) == 2 def test_overwrite_existing_evidence(self): 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, ) c = Candidate(name="Lake X") e1 = Evidence(claim="old", type=EvidenceType.INFERENCE, source="s1") e2 = Evidence( claim="new", type=EvidenceType.DIRECT_STATEMENT, source="s2" ) c.add_evidence("c1", e1) c.add_evidence("c1", e2) assert c.evidence["c1"].claim == "new" class TestCandidateCalculateScore: """Tests for Candidate.calculate_score() method.""" def test_empty_constraints_returns_zero(self): from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) c = Candidate(name="Lake X") assert c.calculate_score([]) == 0.0 def test_single_constraint_with_evidence(self): 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, ) c = Candidate(name="Lake X") constraint = Constraint( id="c1", type=ConstraintType.PROPERTY, description="d", value="v", weight=1.0, ) evidence = Evidence( claim="supports", type=EvidenceType.DIRECT_STATEMENT, source="s", confidence=0.8, ) c.add_evidence("c1", evidence) score = c.calculate_score([constraint]) assert score == pytest.approx(0.8) def test_score_considers_weight(self): 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, ) c = Candidate(name="Lake X") c1 = Constraint( id="c1", type=ConstraintType.PROPERTY, description="d", value="v", weight=2.0, ) c2 = Constraint( id="c2", type=ConstraintType.EVENT, description="d", value="v", weight=1.0, ) e1 = Evidence( claim="e1", type=EvidenceType.DIRECT_STATEMENT, source="s", confidence=0.9, ) e2 = Evidence( claim="e2", type=EvidenceType.NEWS_REPORT, source="s", confidence=0.3, ) c.add_evidence("c1", e1) c.add_evidence("c2", e2) score = c.calculate_score([c1, c2]) # (0.9*2.0 + 0.3*1.0) / (2.0 + 1.0) = 2.1 / 3.0 = 0.7 assert score == pytest.approx(0.7) def test_missing_evidence_contributes_zero(self): 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, ) c = Candidate(name="Lake X") constraint = Constraint( id="c1", type=ConstraintType.PROPERTY, description="d", value="v", weight=1.0, ) score = c.calculate_score([constraint]) assert score == 0.0 def test_score_stored_on_candidate(self): 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, ) c = Candidate(name="Lake X") constraint = Constraint( id="c1", type=ConstraintType.PROPERTY, description="d", value="v" ) evidence = Evidence( claim="c", type=EvidenceType.DIRECT_STATEMENT, source="s", confidence=0.7, ) c.add_evidence("c1", evidence) c.calculate_score([constraint]) assert c.score == pytest.approx(0.7) class TestCandidateGetUnverifiedConstraints: """Tests for Candidate.get_unverified_constraints() method.""" def test_all_unverified_when_no_evidence(self): 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, ) c = Candidate(name="Lake X") constraints = [ Constraint( id="c1", type=ConstraintType.PROPERTY, description="d", value="v", ), Constraint( id="c2", type=ConstraintType.EVENT, description="d", value="v" ), ] unverified = c.get_unverified_constraints(constraints) assert len(unverified) == 2 def test_none_unverified_when_all_have_evidence(self): 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, ) c = Candidate(name="Lake X") constraint = Constraint( id="c1", type=ConstraintType.PROPERTY, description="d", value="v" ) c.add_evidence( "c1", Evidence(claim="c", type=EvidenceType.INFERENCE, source="s") ) assert len(c.get_unverified_constraints([constraint])) == 0 def test_partial_evidence_returns_missing(self): 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, ) c = Candidate(name="Lake X") c1 = Constraint( id="c1", type=ConstraintType.PROPERTY, description="d", value="v" ) c2 = Constraint( id="c2", type=ConstraintType.EVENT, description="d", value="v" ) c.add_evidence( "c1", Evidence(claim="c", type=EvidenceType.INFERENCE, source="s") ) unverified = c.get_unverified_constraints([c1, c2]) assert len(unverified) == 1 assert unverified[0].id == "c2" class TestCandidateGetWeakEvidence: """Tests for Candidate.get_weak_evidence() method.""" def test_empty_evidence_returns_empty(self): from local_deep_research.advanced_search_system.candidates.base_candidate import ( Candidate, ) c = Candidate(name="Lake X") assert c.get_weak_evidence() == [] def test_returns_low_confidence_ids(self): 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, ) c = Candidate(name="Lake X") c.add_evidence( "c1", Evidence( claim="weak", type=EvidenceType.SPECULATION, source="s", confidence=0.1, ), ) c.add_evidence( "c2", Evidence( claim="strong", type=EvidenceType.DIRECT_STATEMENT, source="s", confidence=0.9, ), ) weak = c.get_weak_evidence(threshold=0.5) assert "c1" in weak assert "c2" not in weak def test_custom_threshold(self): 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, ) c = Candidate(name="Lake X") c.add_evidence( "c1", Evidence( claim="c", type=EvidenceType.NEWS_REPORT, source="s", confidence=0.75, ), ) # Default threshold is 0.5 - should not be weak assert "c1" not in c.get_weak_evidence(threshold=0.5) # Higher threshold - should be weak assert "c1" in c.get_weak_evidence(threshold=0.8)