diff --git a/api/services/auth/firecrawl/firecrawl.py b/api/services/auth/firecrawl/firecrawl.py index bfced19128cb1d..38d2832e274305 100644 --- a/api/services/auth/firecrawl/firecrawl.py +++ b/api/services/auth/firecrawl/firecrawl.py @@ -5,6 +5,8 @@ from services.auth.api_key_auth_base import ApiKeyAuthBase, AuthCredentials +_CREDENTIAL_VALIDATION_TIMEOUT = httpx.Timeout(10.0, connect=3.0) + class FirecrawlAuth(ApiKeyAuthBase): def __init__(self, credentials: AuthCredentials): @@ -42,7 +44,7 @@ def _build_url(self, path: str) -> str: return f"{self.base_url.rstrip('/')}/{path.lstrip('/')}" def _post_request(self, url, data, headers): - return httpx.post(url, headers=headers, json=data) + return httpx.post(url, headers=headers, json=data, timeout=_CREDENTIAL_VALIDATION_TIMEOUT) def _handle_error(self, response): try: diff --git a/api/tests/unit_tests/services/auth/test_firecrawl_auth.py b/api/tests/unit_tests/services/auth/test_firecrawl_auth.py index 1458180570d27a..19bd753c01b6ea 100644 --- a/api/tests/unit_tests/services/auth/test_firecrawl_auth.py +++ b/api/tests/unit_tests/services/auth/test_firecrawl_auth.py @@ -4,7 +4,7 @@ import httpx import pytest -from services.auth.firecrawl.firecrawl import FirecrawlAuth +from services.auth.firecrawl.firecrawl import _CREDENTIAL_VALIDATION_TIMEOUT, FirecrawlAuth class TestFirecrawlAuth: @@ -86,6 +86,7 @@ def test_should_validate_valid_credentials_successfully(self, mock_post, auth_in "https://api.firecrawl.dev/v1/crawl", headers={"Content-Type": "application/json", "Authorization": "Bearer test_api_key_123"}, json=expected_data, + timeout=_CREDENTIAL_VALIDATION_TIMEOUT, ) @pytest.mark.parametrize( @@ -195,3 +196,19 @@ def test_should_handle_timeout_with_retry_suggestion(self, mock_post, auth_insta # Verify the timeout exception is raised with original message assert "timed out" in str(exc_info.value) + + @patch("services.auth.firecrawl.firecrawl.httpx.post", autospec=True) + def test_should_pass_bounded_timeout_to_credential_validation(self, mock_post, auth_instance): + """Credential validation must not use the default unbounded timeout.""" + mock_response = MagicMock() + mock_response.status_code = 200 + mock_post.return_value = mock_response + + auth_instance.validate_credentials() + + call_kwargs = mock_post.call_args + assert "timeout" in call_kwargs.kwargs, "timeout keyword is missing from _post_request" + timeout = call_kwargs.kwargs["timeout"] + assert isinstance(timeout, httpx.Timeout) + assert timeout.connect == _CREDENTIAL_VALIDATION_TIMEOUT.connect + assert timeout.read == _CREDENTIAL_VALIDATION_TIMEOUT.read