7a0da7932b
Backwards Compatibility / Verify Encryption Constants (push) Waiting to run
Backwards Compatibility / PyPI Version Compatibility (push) Waiting to run
Backwards Compatibility / Database Migration Tests (push) Waiting to run
CodeQL Advanced / Analyze (javascript-typescript) (push) Waiting to run
CodeQL Advanced / Analyze (python) (push) Waiting to run
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Blocked by required conditions
Docker Tests (Consolidated) / detect-changes (push) Waiting to run
Docker Tests (Consolidated) / Build Test Image (push) Waiting to run
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Blocked by required conditions
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Blocked by required conditions
Docker Tests (Consolidated) / Accessibility Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Unit Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / LLM Example Tests (push) Blocked by required conditions
Docker Tests (Consolidated) / Production Image Smoke Test (push) Blocked by required conditions
Docker Tests (Consolidated) / Infrastructure Tests (push) Blocked by required conditions
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Waiting to run
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
475 lines
15 KiB
Python
475 lines
15 KiB
Python
"""
|
|
Behavioral tests for findings/topic module.
|
|
|
|
Tests Topic and TopicGraph dataclasses with source management and graph logic.
|
|
"""
|
|
|
|
|
|
class TestTopicInit:
|
|
"""Tests for Topic dataclass initialization."""
|
|
|
|
def test_basic_construction(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
t = Topic(
|
|
id="t1",
|
|
title="Climate Change",
|
|
lead_source={"url": "http://example.com"},
|
|
)
|
|
assert t.id == "t1"
|
|
assert t.title == "Climate Change"
|
|
assert t.lead_source == {"url": "http://example.com"}
|
|
|
|
def test_supporting_sources_default_empty(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
t = Topic(id="t1", title="t", lead_source={"url": "u"})
|
|
assert t.supporting_sources == []
|
|
|
|
def test_rejected_sources_default_empty(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
t = Topic(id="t1", title="t", lead_source={"url": "u"})
|
|
assert t.rejected_sources == []
|
|
|
|
def test_related_topic_ids_default_empty(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
t = Topic(id="t1", title="t", lead_source={"url": "u"})
|
|
assert t.related_topic_ids == []
|
|
|
|
def test_parent_topic_id_default_none(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
t = Topic(id="t1", title="t", lead_source={"url": "u"})
|
|
assert t.parent_topic_id is None
|
|
|
|
def test_child_topic_ids_default_empty(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
t = Topic(id="t1", title="t", lead_source={"url": "u"})
|
|
assert t.child_topic_ids == []
|
|
|
|
|
|
class TestTopicAddSupportingSource:
|
|
"""Tests for Topic.add_supporting_source() method."""
|
|
|
|
def test_add_one_source(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
t = Topic(id="t1", title="t", lead_source={"url": "u"})
|
|
t.add_supporting_source({"url": "http://support.com"})
|
|
assert len(t.supporting_sources) == 1
|
|
|
|
def test_no_duplicate_sources(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
t = Topic(id="t1", title="t", lead_source={"url": "u"})
|
|
source = {"url": "http://support.com"}
|
|
t.add_supporting_source(source)
|
|
t.add_supporting_source(source)
|
|
assert len(t.supporting_sources) == 1
|
|
|
|
|
|
class TestTopicRejectSource:
|
|
"""Tests for Topic.reject_source() method."""
|
|
|
|
def test_reject_supporting_source(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
t = Topic(id="t1", title="t", lead_source={"url": "u"})
|
|
source = {"url": "http://bad.com"}
|
|
t.add_supporting_source(source)
|
|
t.reject_source(source)
|
|
assert source not in t.supporting_sources
|
|
assert source in t.rejected_sources
|
|
|
|
def test_reject_non_supporting_source(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
t = Topic(id="t1", title="t", lead_source={"url": "u"})
|
|
source = {"url": "http://other.com"}
|
|
t.reject_source(source)
|
|
assert source in t.rejected_sources
|
|
|
|
def test_no_duplicate_rejected(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
t = Topic(id="t1", title="t", lead_source={"url": "u"})
|
|
source = {"url": "http://bad.com"}
|
|
t.reject_source(source)
|
|
t.reject_source(source)
|
|
assert t.rejected_sources.count(source) == 1
|
|
|
|
|
|
class TestTopicUpdateLeadSource:
|
|
"""Tests for Topic.update_lead_source() method."""
|
|
|
|
def test_old_lead_becomes_supporting(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
old_lead = {"url": "http://old.com"}
|
|
new_lead = {"url": "http://new.com"}
|
|
t = Topic(id="t1", title="t", lead_source=old_lead)
|
|
t.update_lead_source(new_lead)
|
|
assert t.lead_source == new_lead
|
|
assert old_lead in t.supporting_sources
|
|
|
|
def test_new_lead_removed_from_supporting(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
lead = {"url": "http://lead.com"}
|
|
candidate = {"url": "http://candidate.com"}
|
|
t = Topic(id="t1", title="t", lead_source=lead)
|
|
t.add_supporting_source(candidate)
|
|
t.update_lead_source(candidate)
|
|
assert t.lead_source == candidate
|
|
# candidate should not be in supporting after being promoted
|
|
assert candidate not in t.supporting_sources
|
|
|
|
|
|
class TestTopicGetAllSources:
|
|
"""Tests for Topic.get_all_sources() method."""
|
|
|
|
def test_lead_only(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
lead = {"url": "http://lead.com"}
|
|
t = Topic(id="t1", title="t", lead_source=lead)
|
|
all_sources = t.get_all_sources()
|
|
assert len(all_sources) == 1
|
|
assert all_sources[0] == lead
|
|
|
|
def test_lead_plus_supporting(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
lead = {"url": "http://lead.com"}
|
|
t = Topic(id="t1", title="t", lead_source=lead)
|
|
t.add_supporting_source({"url": "http://s1.com"})
|
|
t.add_supporting_source({"url": "http://s2.com"})
|
|
assert len(t.get_all_sources()) == 3
|
|
|
|
|
|
class TestTopicToDict:
|
|
"""Tests for Topic.to_dict() method."""
|
|
|
|
def test_contains_all_fields(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
t = Topic(id="t1", title="Climate", lead_source={"url": "u"})
|
|
d = t.to_dict()
|
|
assert d["id"] == "t1"
|
|
assert d["title"] == "Climate"
|
|
assert d["lead_source"] == {"url": "u"}
|
|
assert "supporting_sources" in d
|
|
assert "rejected_sources" in d
|
|
assert "created_at" in d
|
|
assert "related_topic_ids" in d
|
|
assert "parent_topic_id" in d
|
|
assert "child_topic_ids" in d
|
|
|
|
def test_created_at_is_isoformat(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
)
|
|
|
|
t = Topic(id="t1", title="t", lead_source={"url": "u"})
|
|
d = t.to_dict()
|
|
assert isinstance(d["created_at"], str)
|
|
assert "T" in d["created_at"]
|
|
|
|
|
|
class TestTopicGraphInit:
|
|
"""Tests for TopicGraph initialization."""
|
|
|
|
def test_empty_graph(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
assert g.topics == {}
|
|
|
|
|
|
class TestTopicGraphAddAndGet:
|
|
"""Tests for TopicGraph.add_topic() and get_topic()."""
|
|
|
|
def test_add_and_retrieve(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
t = Topic(id="t1", title="Climate", lead_source={"url": "u"})
|
|
g.add_topic(t)
|
|
assert g.get_topic("t1") is t
|
|
|
|
def test_get_nonexistent_returns_none(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
assert g.get_topic("nonexistent") is None
|
|
|
|
|
|
class TestTopicGraphLinkTopics:
|
|
"""Tests for TopicGraph.link_topics() method."""
|
|
|
|
def test_bidirectional_link(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
|
|
t2 = Topic(id="t2", title="B", lead_source={"url": "u2"})
|
|
g.add_topic(t1)
|
|
g.add_topic(t2)
|
|
g.link_topics("t1", "t2")
|
|
assert "t2" in t1.related_topic_ids
|
|
assert "t1" in t2.related_topic_ids
|
|
|
|
def test_no_duplicate_links(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
|
|
t2 = Topic(id="t2", title="B", lead_source={"url": "u2"})
|
|
g.add_topic(t1)
|
|
g.add_topic(t2)
|
|
g.link_topics("t1", "t2")
|
|
g.link_topics("t1", "t2")
|
|
assert t1.related_topic_ids.count("t2") == 1
|
|
|
|
def test_link_nonexistent_topic_no_error(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
|
|
g.add_topic(t1)
|
|
g.link_topics("t1", "nonexistent")
|
|
assert t1.related_topic_ids == []
|
|
|
|
|
|
class TestTopicGraphParentChild:
|
|
"""Tests for TopicGraph.set_parent_child() method."""
|
|
|
|
def test_set_parent_child(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
parent = Topic(id="p", title="Parent", lead_source={"url": "u"})
|
|
child = Topic(id="c", title="Child", lead_source={"url": "u"})
|
|
g.add_topic(parent)
|
|
g.add_topic(child)
|
|
g.set_parent_child("p", "c")
|
|
assert "c" in parent.child_topic_ids
|
|
assert child.parent_topic_id == "p"
|
|
|
|
|
|
class TestTopicGraphGetRootTopics:
|
|
"""Tests for TopicGraph.get_root_topics() method."""
|
|
|
|
def test_all_roots_when_no_parents(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
g.add_topic(Topic(id="t1", title="A", lead_source={"url": "u"}))
|
|
g.add_topic(Topic(id="t2", title="B", lead_source={"url": "u"}))
|
|
roots = g.get_root_topics()
|
|
assert len(roots) == 2
|
|
|
|
def test_excludes_children(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
parent = Topic(id="p", title="P", lead_source={"url": "u"})
|
|
child = Topic(id="c", title="C", lead_source={"url": "u"})
|
|
g.add_topic(parent)
|
|
g.add_topic(child)
|
|
g.set_parent_child("p", "c")
|
|
roots = g.get_root_topics()
|
|
assert len(roots) == 1
|
|
assert roots[0].id == "p"
|
|
|
|
|
|
class TestTopicGraphGetRelatedTopics:
|
|
"""Tests for TopicGraph.get_related_topics() method."""
|
|
|
|
def test_returns_related(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
t1 = Topic(id="t1", title="A", lead_source={"url": "u"})
|
|
t2 = Topic(id="t2", title="B", lead_source={"url": "u"})
|
|
g.add_topic(t1)
|
|
g.add_topic(t2)
|
|
g.link_topics("t1", "t2")
|
|
related = g.get_related_topics("t1")
|
|
assert len(related) == 1
|
|
assert related[0].id == "t2"
|
|
|
|
def test_nonexistent_topic_returns_empty(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
assert g.get_related_topics("nonexistent") == []
|
|
|
|
|
|
class TestTopicGraphMergeTopics:
|
|
"""Tests for TopicGraph.merge_topics() method."""
|
|
|
|
def test_merge_combines_sources(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
|
|
t2 = Topic(id="t2", title="B", lead_source={"url": "u2"})
|
|
t2.add_supporting_source({"url": "u3"})
|
|
g.add_topic(t1)
|
|
g.add_topic(t2)
|
|
merged = g.merge_topics("t1", "t2")
|
|
assert merged is not None
|
|
# t2's supporting source should be in t1's supporting
|
|
assert {"url": "u3"} in merged.supporting_sources
|
|
|
|
def test_merge_removes_second_topic(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
|
|
t2 = Topic(id="t2", title="B", lead_source={"url": "u2"})
|
|
g.add_topic(t1)
|
|
g.add_topic(t2)
|
|
g.merge_topics("t1", "t2")
|
|
assert g.get_topic("t2") is None
|
|
|
|
def test_merge_updates_title(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
|
|
t2 = Topic(id="t2", title="B", lead_source={"url": "u2"})
|
|
g.add_topic(t1)
|
|
g.add_topic(t2)
|
|
merged = g.merge_topics("t1", "t2", new_title="Combined")
|
|
assert merged.title == "Combined"
|
|
|
|
def test_merge_nonexistent_returns_none(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
|
|
g.add_topic(t1)
|
|
assert g.merge_topics("t1", "nonexistent") is None
|
|
|
|
def test_merge_updates_references_in_other_topics(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
t1 = Topic(id="t1", title="A", lead_source={"url": "u1"})
|
|
t2 = Topic(id="t2", title="B", lead_source={"url": "u2"})
|
|
t3 = Topic(id="t3", title="C", lead_source={"url": "u3"})
|
|
g.add_topic(t1)
|
|
g.add_topic(t2)
|
|
g.add_topic(t3)
|
|
g.link_topics("t2", "t3")
|
|
g.merge_topics("t1", "t2")
|
|
# t3 should now reference t1 instead of t2
|
|
assert "t1" in t3.related_topic_ids
|
|
assert "t2" not in t3.related_topic_ids
|
|
|
|
|
|
class TestTopicGraphToDict:
|
|
"""Tests for TopicGraph.to_dict() method."""
|
|
|
|
def test_empty_graph_to_dict(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
assert g.to_dict() == {}
|
|
|
|
def test_graph_with_topics(self):
|
|
from local_deep_research.advanced_search_system.findings.topic import (
|
|
Topic,
|
|
TopicGraph,
|
|
)
|
|
|
|
g = TopicGraph()
|
|
g.add_topic(Topic(id="t1", title="A", lead_source={"url": "u"}))
|
|
d = g.to_dict()
|
|
assert "t1" in d
|
|
assert d["t1"]["title"] == "A"
|