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
386 lines
12 KiB
Python
386 lines
12 KiB
Python
"""
|
|
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)
|