"""
Tests for the SearXNGSearchEngine class.

Tests cover:
- Initialization and configuration
- Safe search settings
- Rate limiting
- Search result parsing
- Preview generation
- Full content retrieval
- Error handling
"""

from unittest.mock import Mock, patch


class TestSafeSearchSetting:
    """Tests for SafeSearchSetting enum."""

    def test_safe_search_values(self):
        """SafeSearchSetting has correct values."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SafeSearchSetting,
        )

        assert SafeSearchSetting.OFF.value == 0
        assert SafeSearchSetting.MODERATE.value == 1
        assert SafeSearchSetting.STRICT.value == 2

    def test_safe_search_names(self):
        """SafeSearchSetting has correct names."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SafeSearchSetting,
        )

        assert SafeSearchSetting.OFF.name == "OFF"
        assert SafeSearchSetting.MODERATE.name == "MODERATE"
        assert SafeSearchSetting.STRICT.name == "STRICT"


class TestSearXNGSearchEngineInit:
    """Tests for SearXNGSearchEngine initialization."""

    def test_init_with_accessible_instance(self):
        """Initialize with accessible SearXNG instance."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
                max_results=10,
            )

            assert engine.is_available is True
            assert engine.instance_url == "http://localhost:8080"
            assert engine.max_results == 10

    def test_init_with_inaccessible_instance(self):
        """Initialize with inaccessible SearXNG instance."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 500
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            assert engine.is_available is False

    def test_init_with_connection_error(self):
        """Initialize handles connection errors gracefully."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )
        import requests

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_get.side_effect = requests.RequestException(
                "Connection refused"
            )

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            assert engine.is_available is False

    def test_init_strips_trailing_slash(self):
        """Initialize strips trailing slash from instance URL."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080/",
            )

            assert engine.instance_url == "http://localhost:8080"

    def test_init_with_custom_categories(self):
        """Initialize with custom categories."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
                categories=["news", "science"],
            )

            assert engine.categories == ["news", "science"]

    def test_init_with_custom_engines(self):
        """Initialize with custom engines."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
                engines=["google", "bing"],
            )

            assert engine.engines == ["google", "bing"]

    def test_init_default_categories(self):
        """Initialize uses default categories."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            assert engine.categories == ["general"]


class TestSafeSearchParsing:
    """Tests for safe search setting parsing."""

    def test_safe_search_string_name(self):
        """Parse safe search from string name."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
            SafeSearchSetting,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
                safe_search="STRICT",
            )

            assert engine.safe_search == SafeSearchSetting.STRICT

    def test_safe_search_integer(self):
        """Parse safe search from integer."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
            SafeSearchSetting,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
                safe_search=1,
            )

            assert engine.safe_search == SafeSearchSetting.MODERATE

    def test_safe_search_string_integer(self):
        """Parse safe search from string integer."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
            SafeSearchSetting,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
                safe_search="2",
            )

            assert engine.safe_search == SafeSearchSetting.STRICT

    def test_safe_search_invalid_defaults_to_off(self):
        """Invalid safe search value defaults to OFF."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
            SafeSearchSetting,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
                safe_search="INVALID_VALUE",
            )

            assert engine.safe_search == SafeSearchSetting.OFF


class TestIsValidSearchResult:
    """Tests for _is_valid_search_result method."""

    def test_valid_http_url(self):
        """Accept valid HTTP URL."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            assert engine._is_valid_search_result("http://example.com") is True

    def test_valid_https_url(self):
        """Accept valid HTTPS URL."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            assert engine._is_valid_search_result("https://example.com") is True

    def test_reject_relative_url(self):
        """Reject relative URL."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            assert (
                engine._is_valid_search_result("/stats?engine=google") is False
            )

    def test_reject_instance_url(self):
        """Reject URLs pointing to SearXNG instance itself."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            assert (
                engine._is_valid_search_result(
                    "http://localhost:8080/stats?engine=google"
                )
                is False
            )

    def test_reject_empty_url(self):
        """Reject empty URL."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            assert engine._is_valid_search_result("") is False
            assert engine._is_valid_search_result(None) is False


class TestRateLimiting:
    """Tests for rate limiting functionality."""

    def test_rate_limit_applied(self):
        """Rate limiting is applied between requests."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )
        import time

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
                delay_between_requests=0.1,
            )

            engine.last_request_time = time.time()

            with patch("time.sleep"):
                engine._respect_rate_limit()

                # Should have been called to wait
                # (may or may not depending on timing)

    def test_no_rate_limit_first_request(self):
        """No rate limiting on first request."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
                delay_between_requests=1.0,
            )

            # last_request_time is 0, so no wait needed
            engine.last_request_time = 0

            with patch("time.sleep"):
                engine._respect_rate_limit()
                # Should not sleep on first request (time since last > delay)


class TestGetSearchResults:
    """Tests for _get_search_results method."""

    def test_returns_empty_when_unavailable(self):
        """Return empty list when engine is unavailable."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 500
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            results = engine._get_search_results("test query")

            assert results == []

    def test_parses_html_results(self):
        """Parse HTML search results."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        html_content = """
        <html>
        <body>
            <article class="result">
                <h3><a href="https://example.com/page1">Result 1</a></h3>
                <p class="content">This is the first result content.</p>
            </article>
            <article class="result">
                <h3><a href="https://example.com/page2">Result 2</a></h3>
                <p class="content">This is the second result content.</p>
            </article>
        </body>
        </html>
        """

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            # First call for availability check
            mock_response_init = Mock()
            mock_response_init.status_code = 200
            mock_response_init.cookies = {}

            # Second call for search
            mock_response_search = Mock()
            mock_response_search.status_code = 200
            mock_response_search.text = html_content
            mock_response_search.cookies = {}

            mock_get.side_effect = [
                mock_response_init,
                mock_response_init,
                mock_response_search,
            ]

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            results = engine._get_search_results("test query")

            assert len(results) == 2
            assert results[0]["title"] == "Result 1"
            assert results[0]["url"] == "https://example.com/page1"


class TestGetPreviews:
    """Tests for _get_previews method."""

    def test_returns_empty_when_unavailable(self):
        """Return empty list when engine is unavailable."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 500
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            previews = engine._get_previews("test query")

            assert previews == []

    def test_formats_previews_correctly(self):
        """Format previews with correct fields."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            # Mock _get_search_results
            with patch.object(
                engine,
                "_get_search_results",
                return_value=[
                    {
                        "title": "Test Title",
                        "url": "https://example.com",
                        "content": "Test content",
                        "engine": "google",
                        "category": "general",
                    }
                ],
            ):
                previews = engine._get_previews("test query")

                assert len(previews) == 1
                assert previews[0]["title"] == "Test Title"
                assert previews[0]["link"] == "https://example.com"
                assert previews[0]["snippet"] == "Test content"
                assert previews[0]["engine"] == "google"


class TestGetFullContent:
    """Tests for _get_full_content method."""

    def test_returns_items_when_unavailable(self):
        """Return items as-is when engine is unavailable."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 500
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            items = [{"title": "Test", "link": "https://example.com"}]
            result = engine._get_full_content(items)

            assert result == items


class TestRun:
    """Tests for run method."""

    def test_run_returns_empty_when_unavailable(self):
        """Run returns empty list when engine is unavailable."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 500
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            results = engine.run("test query")

            assert results == []

    def test_run_handles_exceptions(self):
        """Run handles exceptions gracefully."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            with patch.object(
                engine, "_get_previews", side_effect=Exception("Search failed")
            ):
                results = engine.run("test query")

                assert results == []


class TestResults:
    """Tests for results method."""

    def test_results_returns_empty_when_unavailable(self):
        """Results returns empty list when engine is unavailable."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 500
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            results = engine.results("test query")

            assert results == []

    def test_results_formats_correctly(self):
        """Results formats output correctly."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            with patch.object(
                engine,
                "_get_search_results",
                return_value=[
                    {
                        "title": "Test",
                        "url": "https://example.com",
                        "content": "Content",
                    }
                ],
            ):
                results = engine.results("test query")

                assert len(results) == 1
                assert results[0]["title"] == "Test"
                assert results[0]["link"] == "https://example.com"
                assert results[0]["snippet"] == "Content"

    def test_results_with_custom_max_results(self):
        """Results respects custom max_results."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
                max_results=10,
            )

            with patch.object(engine, "_get_search_results", return_value=[]):
                engine.results("test query", max_results=5)

                # max_results should be temporarily changed to 5
                assert engine.max_results == 10  # Restored after call


class TestClassAttributes:
    """Tests for class attributes."""

    def test_is_public(self):
        """SearXNGSearchEngine is marked as public."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        assert SearXNGSearchEngine.is_public is True

    def test_is_generic(self):
        """SearXNGSearchEngine is marked as generic."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        assert SearXNGSearchEngine.is_generic is True


class TestGetSelfHostingInstructions:
    """Tests for get_self_hosting_instructions static method."""

    def test_returns_instructions(self):
        """Returns self-hosting instructions."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        instructions = SearXNGSearchEngine.get_self_hosting_instructions()

        assert "SearXNG Self-Hosting Instructions" in instructions
        assert "docker" in instructions.lower()
        assert "8080" in instructions


class TestInvoke:
    """Tests for invoke method."""

    def test_invoke_calls_run(self):
        """Invoke calls run method."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
            )

            with patch.object(
                engine, "run", return_value=[{"title": "Result"}]
            ) as mock_run:
                result = engine.invoke("test query")

                mock_run.assert_called_once_with("test query")
                assert result == [{"title": "Result"}]


class TestNormalizeList:
    """Tests for _normalize_list static method."""

    def test_none_returns_none(self):
        """None input returns None."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        assert SearXNGSearchEngine._normalize_list(None) is None

    def test_list_returned_unchanged(self):
        """Already-parsed list is returned as-is."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        original = ["general", "news"]
        assert SearXNGSearchEngine._normalize_list(original) is original

    def test_json_array_string_parsed(self):
        """JSON array string is parsed into a list."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        assert SearXNGSearchEngine._normalize_list('["general", "news"]') == [
            "general",
            "news",
        ]

    def test_json_array_with_crlf_parsed(self):
        """JSON array with \\r\\n (issue #1030 exact input) is parsed correctly."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        raw = '[\r\n  "general"\r\n]'
        assert SearXNGSearchEngine._normalize_list(raw) == ["general"]

    def test_comma_separated_string_parsed(self):
        """Comma-separated string is split into a list."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        assert SearXNGSearchEngine._normalize_list(
            "general, news, science"
        ) == [
            "general",
            "news",
            "science",
        ]

    def test_empty_string_returns_none(self):
        """Empty string returns None."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        assert SearXNGSearchEngine._normalize_list("") is None

    def test_whitespace_only_returns_none(self):
        """Whitespace-only string returns None."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        assert SearXNGSearchEngine._normalize_list("   ") is None

    def test_bare_string_returns_single_element_list(self):
        """A bare string without JSON brackets or commas becomes a single-element list."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        assert SearXNGSearchEngine._normalize_list("general") == ["general"]


class TestCategoriesNormalization:
    """Tests that categories are normalized in __init__."""

    def test_string_categories_normalized_to_list(self):
        """String categories (issue #1030) are parsed into a proper list."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
                categories='[\r\n  "general"\r\n]',
            )

            assert engine.categories == ["general"]

    def test_none_categories_default_to_general(self):
        """None categories default to ['general']."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
                categories=None,
            )

            assert engine.categories == ["general"]

    def test_string_engines_normalized_to_list(self):
        """String engines are parsed into a proper list."""
        from local_deep_research.web_search_engines.engines.search_engine_searxng import (
            SearXNGSearchEngine,
        )

        with patch(
            "local_deep_research.web_search_engines.engines.search_engine_searxng.safe_get"
        ) as mock_get:
            mock_response = Mock()
            mock_response.status_code = 200
            mock_get.return_value = mock_response

            engine = SearXNGSearchEngine(
                instance_url="http://localhost:8080",
                engines='["google", "bing"]',
            )

            assert engine.engines == ["google", "bing"]
