""" 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="""Let me analyze this query... 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"