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
527 lines
16 KiB
Python
527 lines
16 KiB
Python
"""
|
|
Tests for advanced_search_system/constraints/constraint_analyzer.py
|
|
|
|
Tests cover:
|
|
- ConstraintAnalyzer initialization
|
|
- extract_constraints method
|
|
- _parse_constraint_type method
|
|
- _parse_weight method
|
|
"""
|
|
|
|
from unittest.mock import Mock
|
|
|
|
|
|
class TestConstraintAnalyzerInit:
|
|
"""Tests for ConstraintAnalyzer initialization."""
|
|
|
|
def test_stores_model(self):
|
|
"""Test that model is stored."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
mock_model = Mock()
|
|
analyzer = ConstraintAnalyzer(mock_model)
|
|
|
|
assert analyzer.model is mock_model
|
|
|
|
|
|
class TestParseConstraintType:
|
|
"""Tests for ConstraintAnalyzer._parse_constraint_type method."""
|
|
|
|
def test_parses_property(self):
|
|
"""Test parsing property type."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
from local_deep_research.advanced_search_system.constraints.base_constraint import (
|
|
ConstraintType,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_constraint_type("property")
|
|
|
|
assert result == ConstraintType.PROPERTY
|
|
|
|
def test_parses_name_pattern(self):
|
|
"""Test parsing name_pattern type."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
from local_deep_research.advanced_search_system.constraints.base_constraint import (
|
|
ConstraintType,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_constraint_type("name_pattern")
|
|
|
|
assert result == ConstraintType.NAME_PATTERN
|
|
|
|
def test_parses_event(self):
|
|
"""Test parsing event type."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
from local_deep_research.advanced_search_system.constraints.base_constraint import (
|
|
ConstraintType,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_constraint_type("event")
|
|
|
|
assert result == ConstraintType.EVENT
|
|
|
|
def test_parses_statistic(self):
|
|
"""Test parsing statistic type."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
from local_deep_research.advanced_search_system.constraints.base_constraint import (
|
|
ConstraintType,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_constraint_type("statistic")
|
|
|
|
assert result == ConstraintType.STATISTIC
|
|
|
|
def test_parses_temporal(self):
|
|
"""Test parsing temporal type."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
from local_deep_research.advanced_search_system.constraints.base_constraint import (
|
|
ConstraintType,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_constraint_type("temporal")
|
|
|
|
assert result == ConstraintType.TEMPORAL
|
|
|
|
def test_parses_location(self):
|
|
"""Test parsing location type."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
from local_deep_research.advanced_search_system.constraints.base_constraint import (
|
|
ConstraintType,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_constraint_type("location")
|
|
|
|
assert result == ConstraintType.LOCATION
|
|
|
|
def test_parses_comparison(self):
|
|
"""Test parsing comparison type."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
from local_deep_research.advanced_search_system.constraints.base_constraint import (
|
|
ConstraintType,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_constraint_type("comparison")
|
|
|
|
assert result == ConstraintType.COMPARISON
|
|
|
|
def test_parses_existence(self):
|
|
"""Test parsing existence type."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
from local_deep_research.advanced_search_system.constraints.base_constraint import (
|
|
ConstraintType,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_constraint_type("existence")
|
|
|
|
assert result == ConstraintType.EXISTENCE
|
|
|
|
def test_case_insensitive(self):
|
|
"""Test that parsing is case-insensitive."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
from local_deep_research.advanced_search_system.constraints.base_constraint import (
|
|
ConstraintType,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
assert (
|
|
analyzer._parse_constraint_type("PROPERTY")
|
|
== ConstraintType.PROPERTY
|
|
)
|
|
assert (
|
|
analyzer._parse_constraint_type("Property")
|
|
== ConstraintType.PROPERTY
|
|
)
|
|
assert (
|
|
analyzer._parse_constraint_type("TEMPORAL")
|
|
== ConstraintType.TEMPORAL
|
|
)
|
|
|
|
def test_unknown_type_defaults_to_property(self):
|
|
"""Test that unknown type defaults to PROPERTY."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
from local_deep_research.advanced_search_system.constraints.base_constraint import (
|
|
ConstraintType,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_constraint_type("unknown_type")
|
|
|
|
assert result == ConstraintType.PROPERTY
|
|
|
|
|
|
class TestParseWeight:
|
|
"""Tests for ConstraintAnalyzer._parse_weight method."""
|
|
|
|
def test_parses_int(self):
|
|
"""Test parsing integer weight."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_weight(1)
|
|
|
|
assert result == 1.0
|
|
|
|
def test_parses_float(self):
|
|
"""Test parsing float weight."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_weight(0.75)
|
|
|
|
assert result == 0.75
|
|
|
|
def test_parses_numeric_string(self):
|
|
"""Test parsing numeric string weight."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_weight("0.8")
|
|
|
|
assert result == 0.8
|
|
|
|
def test_parses_string_with_text_annotation(self):
|
|
"""Test parsing string with text annotation."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_weight("0.9 (high importance)")
|
|
|
|
assert result == 0.9
|
|
|
|
def test_extracts_first_number_from_string(self):
|
|
"""Test extracting first number from string."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_weight("Weight: 0.7 out of 1.0")
|
|
|
|
assert result == 0.7
|
|
|
|
def test_invalid_string_defaults_to_one(self):
|
|
"""Test that invalid string defaults to 1.0."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_weight("no numbers here")
|
|
|
|
assert result == 1.0
|
|
|
|
def test_empty_string_defaults_to_one(self):
|
|
"""Test that empty string defaults to 1.0."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(Mock())
|
|
|
|
result = analyzer._parse_weight("")
|
|
|
|
assert result == 1.0
|
|
|
|
|
|
class TestExtractConstraints:
|
|
"""Tests for ConstraintAnalyzer.extract_constraints method."""
|
|
|
|
def test_extracts_single_constraint(self):
|
|
"""Test extracting a single constraint."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
mock_model = Mock()
|
|
mock_model.invoke.return_value = Mock(
|
|
content="""
|
|
CONSTRAINT_1:
|
|
Type: property
|
|
Description: The person is an actor
|
|
Value: must be an actor
|
|
Weight: 0.8
|
|
"""
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(mock_model)
|
|
constraints = analyzer.extract_constraints("Who is the actor?")
|
|
|
|
assert len(constraints) == 1
|
|
assert constraints[0].description == "The person is an actor"
|
|
assert constraints[0].value == "must be an actor"
|
|
assert constraints[0].weight == 0.8
|
|
|
|
def test_extracts_multiple_constraints(self):
|
|
"""Test extracting multiple constraints."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
mock_model = Mock()
|
|
mock_model.invoke.return_value = Mock(
|
|
content="""
|
|
CONSTRAINT_1:
|
|
Type: property
|
|
Description: First constraint
|
|
Value: first value
|
|
Weight: 0.9
|
|
|
|
CONSTRAINT_2:
|
|
Type: temporal
|
|
Description: Second constraint
|
|
Value: second value
|
|
Weight: 0.7
|
|
"""
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(mock_model)
|
|
constraints = analyzer.extract_constraints("Test query")
|
|
|
|
assert len(constraints) == 2
|
|
assert constraints[0].description == "First constraint"
|
|
assert constraints[1].description == "Second constraint"
|
|
|
|
def test_generates_unique_ids(self):
|
|
"""Test that constraints get unique IDs."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
mock_model = Mock()
|
|
mock_model.invoke.return_value = Mock(
|
|
content="""
|
|
CONSTRAINT_1:
|
|
Type: property
|
|
Description: First
|
|
Value: first
|
|
Weight: 1.0
|
|
|
|
CONSTRAINT_2:
|
|
Type: property
|
|
Description: Second
|
|
Value: second
|
|
Weight: 1.0
|
|
"""
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(mock_model)
|
|
constraints = analyzer.extract_constraints("Test")
|
|
|
|
assert constraints[0].id == "c1"
|
|
assert constraints[1].id == "c2"
|
|
|
|
def test_handles_missing_weight(self):
|
|
"""Test handling constraint without weight."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
mock_model = Mock()
|
|
mock_model.invoke.return_value = Mock(
|
|
content="""
|
|
CONSTRAINT_1:
|
|
Type: property
|
|
Description: No weight specified
|
|
Value: some value
|
|
"""
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(mock_model)
|
|
constraints = analyzer.extract_constraints("Test")
|
|
|
|
assert len(constraints) == 1
|
|
assert constraints[0].weight == 1.0 # Default
|
|
|
|
def test_skips_incomplete_constraints(self):
|
|
"""Test that incomplete constraints are skipped."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
mock_model = Mock()
|
|
mock_model.invoke.return_value = Mock(
|
|
content="""
|
|
CONSTRAINT_1:
|
|
Type: property
|
|
Description: Missing value
|
|
|
|
CONSTRAINT_2:
|
|
Type: property
|
|
Description: Complete constraint
|
|
Value: has value
|
|
"""
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(mock_model)
|
|
constraints = analyzer.extract_constraints("Test")
|
|
|
|
# Only the complete constraint should be included
|
|
assert len(constraints) == 1
|
|
assert constraints[0].description == "Complete constraint"
|
|
|
|
def test_handles_think_tags_in_response(self):
|
|
"""Test that think tags are removed from response."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
mock_model = Mock()
|
|
mock_model.invoke.return_value = Mock(
|
|
content="""<think>Let me analyze this query...</think>
|
|
CONSTRAINT_1:
|
|
Type: property
|
|
Description: Test constraint
|
|
Value: test value
|
|
Weight: 0.5
|
|
"""
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(mock_model)
|
|
constraints = analyzer.extract_constraints("Test")
|
|
|
|
assert len(constraints) == 1
|
|
assert constraints[0].description == "Test constraint"
|
|
|
|
def test_handles_empty_response(self):
|
|
"""Test handling empty model response."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
mock_model = Mock()
|
|
mock_model.invoke.return_value = Mock(content="")
|
|
|
|
analyzer = ConstraintAnalyzer(mock_model)
|
|
constraints = analyzer.extract_constraints("Test")
|
|
|
|
assert constraints == []
|
|
|
|
def test_parses_constraint_type_correctly(self):
|
|
"""Test that constraint types are parsed correctly."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
from local_deep_research.advanced_search_system.constraints.base_constraint import (
|
|
ConstraintType,
|
|
)
|
|
|
|
mock_model = Mock()
|
|
mock_model.invoke.return_value = Mock(
|
|
content="""
|
|
CONSTRAINT_1:
|
|
Type: temporal
|
|
Description: Time constraint
|
|
Value: in 2024
|
|
Weight: 0.8
|
|
"""
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(mock_model)
|
|
constraints = analyzer.extract_constraints("Test")
|
|
|
|
assert constraints[0].type == ConstraintType.TEMPORAL
|
|
|
|
def test_handles_extra_whitespace(self):
|
|
"""Test handling of extra whitespace in response."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
mock_model = Mock()
|
|
mock_model.invoke.return_value = Mock(
|
|
content="""
|
|
CONSTRAINT_1:
|
|
Type: property
|
|
Description: Test with spaces
|
|
Value: value with spaces
|
|
Weight: 0.7
|
|
"""
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(mock_model)
|
|
constraints = analyzer.extract_constraints("Test")
|
|
|
|
assert len(constraints) == 1
|
|
assert constraints[0].description == "Test with spaces"
|
|
|
|
def test_processes_last_constraint(self):
|
|
"""Test that the last constraint is processed (not forgotten)."""
|
|
from local_deep_research.advanced_search_system.constraints.constraint_analyzer import (
|
|
ConstraintAnalyzer,
|
|
)
|
|
|
|
mock_model = Mock()
|
|
# No trailing CONSTRAINT_ marker, so last one must be processed
|
|
mock_model.invoke.return_value = Mock(
|
|
content="""
|
|
CONSTRAINT_1:
|
|
Type: property
|
|
Description: First constraint
|
|
Value: first
|
|
|
|
CONSTRAINT_2:
|
|
Type: property
|
|
Description: Last constraint
|
|
Value: last"""
|
|
)
|
|
|
|
analyzer = ConstraintAnalyzer(mock_model)
|
|
constraints = analyzer.extract_constraints("Test")
|
|
|
|
assert len(constraints) == 2
|
|
assert constraints[1].description == "Last constraint"
|