Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>tags/1.9.0
| @@ -5,7 +5,7 @@ import logging | |||
| import os | |||
| import time | |||
| import requests | |||
| import httpx | |||
| logger = logging.getLogger(__name__) | |||
| @@ -30,10 +30,10 @@ class NacosHttpClient: | |||
| params = {} | |||
| try: | |||
| self._inject_auth_info(headers, params) | |||
| response = requests.request(method, url="http://" + self.server + url, headers=headers, params=params) | |||
| response = httpx.request(method, url="http://" + self.server + url, headers=headers, params=params) | |||
| response.raise_for_status() | |||
| return response.text | |||
| except requests.RequestException as e: | |||
| except httpx.RequestError as e: | |||
| return f"Request to Nacos failed: {e}" | |||
| def _inject_auth_info(self, headers: dict[str, str], params: dict[str, str], module: str = "config") -> None: | |||
| @@ -78,7 +78,7 @@ class NacosHttpClient: | |||
| params = {"username": self.username, "password": self.password} | |||
| url = "http://" + self.server + "/nacos/v1/auth/login" | |||
| try: | |||
| resp = requests.request("POST", url, headers=None, params=params) | |||
| resp = httpx.request("POST", url, headers=None, params=params) | |||
| resp.raise_for_status() | |||
| response_data = resp.json() | |||
| self.token = response_data.get("accessToken") | |||
| @@ -1,6 +1,6 @@ | |||
| import logging | |||
| import requests | |||
| import httpx | |||
| from flask import current_app, redirect, request | |||
| from flask_login import current_user | |||
| from flask_restx import Resource, fields | |||
| @@ -119,7 +119,7 @@ class OAuthDataSourceBinding(Resource): | |||
| return {"error": "Invalid code"}, 400 | |||
| try: | |||
| oauth_provider.get_access_token(code) | |||
| except requests.HTTPError as e: | |||
| except httpx.HTTPStatusError as e: | |||
| logger.exception( | |||
| "An error occurred during the OAuthCallback process with %s: %s", provider, e.response.text | |||
| ) | |||
| @@ -152,7 +152,7 @@ class OAuthDataSourceSync(Resource): | |||
| return {"error": "Invalid provider"}, 400 | |||
| try: | |||
| oauth_provider.sync_data_source(binding_id) | |||
| except requests.HTTPError as e: | |||
| except httpx.HTTPStatusError as e: | |||
| logger.exception( | |||
| "An error occurred during the OAuthCallback process with %s: %s", provider, e.response.text | |||
| ) | |||
| @@ -1,6 +1,6 @@ | |||
| import logging | |||
| import requests | |||
| import httpx | |||
| from flask import current_app, redirect, request | |||
| from flask_restx import Resource | |||
| from sqlalchemy import select | |||
| @@ -101,8 +101,10 @@ class OAuthCallback(Resource): | |||
| try: | |||
| token = oauth_provider.get_access_token(code) | |||
| user_info = oauth_provider.get_user_info(token) | |||
| except requests.RequestException as e: | |||
| error_text = e.response.text if e.response else str(e) | |||
| except httpx.RequestError as e: | |||
| error_text = str(e) | |||
| if isinstance(e, httpx.HTTPStatusError): | |||
| error_text = e.response.text | |||
| logger.exception("An error occurred during the OAuth process with %s: %s", provider, error_text) | |||
| return {"error": "OAuth process failed"}, 400 | |||
| @@ -1,7 +1,7 @@ | |||
| import json | |||
| import logging | |||
| import requests | |||
| import httpx | |||
| from flask_restx import Resource, fields, reqparse | |||
| from packaging import version | |||
| @@ -57,7 +57,11 @@ class VersionApi(Resource): | |||
| return result | |||
| try: | |||
| response = requests.get(check_update_url, {"current_version": args["current_version"]}, timeout=(3, 10)) | |||
| response = httpx.get( | |||
| check_update_url, | |||
| params={"current_version": args["current_version"]}, | |||
| timeout=httpx.Timeout(connect=3, read=10), | |||
| ) | |||
| except Exception as error: | |||
| logger.warning("Check update version error: %s.", str(error)) | |||
| result["version"] = args["current_version"] | |||
| @@ -8,7 +8,7 @@ from collections import deque | |||
| from collections.abc import Sequence | |||
| from datetime import datetime | |||
| import requests | |||
| import httpx | |||
| from opentelemetry import trace as trace_api | |||
| from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter | |||
| from opentelemetry.sdk.resources import Resource | |||
| @@ -65,13 +65,13 @@ class TraceClient: | |||
| def api_check(self): | |||
| try: | |||
| response = requests.head(self.endpoint, timeout=5) | |||
| response = httpx.head(self.endpoint, timeout=5) | |||
| if response.status_code == 405: | |||
| return True | |||
| else: | |||
| logger.debug("AliyunTrace API check failed: Unexpected status code: %s", response.status_code) | |||
| return False | |||
| except requests.RequestException as e: | |||
| except httpx.RequestError as e: | |||
| logger.debug("AliyunTrace API check failed: %s", str(e)) | |||
| raise ValueError(f"AliyunTrace API check failed: {str(e)}") | |||
| @@ -1,7 +1,7 @@ | |||
| import urllib.parse | |||
| from dataclasses import dataclass | |||
| import requests | |||
| import httpx | |||
| @dataclass | |||
| @@ -58,7 +58,7 @@ class GitHubOAuth(OAuth): | |||
| "redirect_uri": self.redirect_uri, | |||
| } | |||
| headers = {"Accept": "application/json"} | |||
| response = requests.post(self._TOKEN_URL, data=data, headers=headers) | |||
| response = httpx.post(self._TOKEN_URL, data=data, headers=headers) | |||
| response_json = response.json() | |||
| access_token = response_json.get("access_token") | |||
| @@ -70,11 +70,11 @@ class GitHubOAuth(OAuth): | |||
| def get_raw_user_info(self, token: str): | |||
| headers = {"Authorization": f"token {token}"} | |||
| response = requests.get(self._USER_INFO_URL, headers=headers) | |||
| response = httpx.get(self._USER_INFO_URL, headers=headers) | |||
| response.raise_for_status() | |||
| user_info = response.json() | |||
| email_response = requests.get(self._EMAIL_INFO_URL, headers=headers) | |||
| email_response = httpx.get(self._EMAIL_INFO_URL, headers=headers) | |||
| email_info = email_response.json() | |||
| primary_email: dict = next((email for email in email_info if email["primary"] == True), {}) | |||
| @@ -112,7 +112,7 @@ class GoogleOAuth(OAuth): | |||
| "redirect_uri": self.redirect_uri, | |||
| } | |||
| headers = {"Accept": "application/json"} | |||
| response = requests.post(self._TOKEN_URL, data=data, headers=headers) | |||
| response = httpx.post(self._TOKEN_URL, data=data, headers=headers) | |||
| response_json = response.json() | |||
| access_token = response_json.get("access_token") | |||
| @@ -124,7 +124,7 @@ class GoogleOAuth(OAuth): | |||
| def get_raw_user_info(self, token: str): | |||
| headers = {"Authorization": f"Bearer {token}"} | |||
| response = requests.get(self._USER_INFO_URL, headers=headers) | |||
| response = httpx.get(self._USER_INFO_URL, headers=headers) | |||
| response.raise_for_status() | |||
| return response.json() | |||
| @@ -1,7 +1,7 @@ | |||
| import urllib.parse | |||
| from typing import Any | |||
| import requests | |||
| import httpx | |||
| from flask_login import current_user | |||
| from sqlalchemy import select | |||
| @@ -43,7 +43,7 @@ class NotionOAuth(OAuthDataSource): | |||
| data = {"code": code, "grant_type": "authorization_code", "redirect_uri": self.redirect_uri} | |||
| headers = {"Accept": "application/json"} | |||
| auth = (self.client_id, self.client_secret) | |||
| response = requests.post(self._TOKEN_URL, data=data, auth=auth, headers=headers) | |||
| response = httpx.post(self._TOKEN_URL, data=data, auth=auth, headers=headers) | |||
| response_json = response.json() | |||
| access_token = response_json.get("access_token") | |||
| @@ -239,7 +239,7 @@ class NotionOAuth(OAuthDataSource): | |||
| "Notion-Version": "2022-06-28", | |||
| } | |||
| response = requests.post(url=self._NOTION_PAGE_SEARCH, json=data, headers=headers) | |||
| response = httpx.post(url=self._NOTION_PAGE_SEARCH, json=data, headers=headers) | |||
| response_json = response.json() | |||
| results.extend(response_json.get("results", [])) | |||
| @@ -254,7 +254,7 @@ class NotionOAuth(OAuthDataSource): | |||
| "Authorization": f"Bearer {access_token}", | |||
| "Notion-Version": "2022-06-28", | |||
| } | |||
| response = requests.get(url=f"{self._NOTION_BLOCK_SEARCH}/{block_id}", headers=headers) | |||
| response = httpx.get(url=f"{self._NOTION_BLOCK_SEARCH}/{block_id}", headers=headers) | |||
| response_json = response.json() | |||
| if response.status_code != 200: | |||
| message = response_json.get("message", "unknown error") | |||
| @@ -270,7 +270,7 @@ class NotionOAuth(OAuthDataSource): | |||
| "Authorization": f"Bearer {access_token}", | |||
| "Notion-Version": "2022-06-28", | |||
| } | |||
| response = requests.get(url=self._NOTION_BOT_USER, headers=headers) | |||
| response = httpx.get(url=self._NOTION_BOT_USER, headers=headers) | |||
| response_json = response.json() | |||
| if "object" in response_json and response_json["object"] == "user": | |||
| user_type = response_json["type"] | |||
| @@ -294,7 +294,7 @@ class NotionOAuth(OAuthDataSource): | |||
| "Authorization": f"Bearer {access_token}", | |||
| "Notion-Version": "2022-06-28", | |||
| } | |||
| response = requests.post(url=self._NOTION_PAGE_SEARCH, json=data, headers=headers) | |||
| response = httpx.post(url=self._NOTION_PAGE_SEARCH, json=data, headers=headers) | |||
| response_json = response.json() | |||
| results.extend(response_json.get("results", [])) | |||
| @@ -1,6 +1,6 @@ | |||
| import json | |||
| import requests | |||
| import httpx | |||
| from services.auth.api_key_auth_base import ApiKeyAuthBase | |||
| @@ -36,7 +36,7 @@ class FirecrawlAuth(ApiKeyAuthBase): | |||
| return {"Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}"} | |||
| def _post_request(self, url, data, headers): | |||
| return requests.post(url, headers=headers, json=data) | |||
| return httpx.post(url, headers=headers, json=data) | |||
| def _handle_error(self, response): | |||
| if response.status_code in {402, 409, 500}: | |||
| @@ -1,6 +1,6 @@ | |||
| import json | |||
| import requests | |||
| import httpx | |||
| from services.auth.api_key_auth_base import ApiKeyAuthBase | |||
| @@ -31,7 +31,7 @@ class JinaAuth(ApiKeyAuthBase): | |||
| return {"Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}"} | |||
| def _post_request(self, url, data, headers): | |||
| return requests.post(url, headers=headers, json=data) | |||
| return httpx.post(url, headers=headers, json=data) | |||
| def _handle_error(self, response): | |||
| if response.status_code in {402, 409, 500}: | |||
| @@ -1,6 +1,6 @@ | |||
| import json | |||
| import requests | |||
| import httpx | |||
| from services.auth.api_key_auth_base import ApiKeyAuthBase | |||
| @@ -31,7 +31,7 @@ class JinaAuth(ApiKeyAuthBase): | |||
| return {"Content-Type": "application/json", "Authorization": f"Bearer {self.api_key}"} | |||
| def _post_request(self, url, data, headers): | |||
| return requests.post(url, headers=headers, json=data) | |||
| return httpx.post(url, headers=headers, json=data) | |||
| def _handle_error(self, response): | |||
| if response.status_code in {402, 409, 500}: | |||
| @@ -1,7 +1,7 @@ | |||
| import json | |||
| from urllib.parse import urljoin | |||
| import requests | |||
| import httpx | |||
| from services.auth.api_key_auth_base import ApiKeyAuthBase | |||
| @@ -31,7 +31,7 @@ class WatercrawlAuth(ApiKeyAuthBase): | |||
| return {"Content-Type": "application/json", "X-API-KEY": self.api_key} | |||
| def _get_request(self, url, headers): | |||
| return requests.get(url, headers=headers) | |||
| return httpx.get(url, headers=headers) | |||
| def _handle_error(self, response): | |||
| if response.status_code in {402, 409, 500}: | |||
| @@ -1,6 +1,6 @@ | |||
| import os | |||
| import requests | |||
| import httpx | |||
| class OperationService: | |||
| @@ -12,7 +12,7 @@ class OperationService: | |||
| headers = {"Content-Type": "application/json", "Billing-Api-Secret-Key": cls.secret_key} | |||
| url = f"{cls.base_url}{endpoint}" | |||
| response = requests.request(method, url, json=json, params=params, headers=headers) | |||
| response = httpx.request(method, url, json=json, params=params, headers=headers) | |||
| return response.json() | |||
| @@ -3,7 +3,7 @@ import json | |||
| from dataclasses import dataclass | |||
| from typing import Any | |||
| import requests | |||
| import httpx | |||
| from flask_login import current_user | |||
| from core.helper import encrypter | |||
| @@ -216,7 +216,7 @@ class WebsiteService: | |||
| @classmethod | |||
| def _crawl_with_jinareader(cls, request: CrawlRequest, api_key: str) -> dict[str, Any]: | |||
| if not request.options.crawl_sub_pages: | |||
| response = requests.get( | |||
| response = httpx.get( | |||
| f"https://r.jina.ai/{request.url}", | |||
| headers={"Accept": "application/json", "Authorization": f"Bearer {api_key}"}, | |||
| ) | |||
| @@ -224,7 +224,7 @@ class WebsiteService: | |||
| raise ValueError("Failed to crawl:") | |||
| return {"status": "active", "data": response.json().get("data")} | |||
| else: | |||
| response = requests.post( | |||
| response = httpx.post( | |||
| "https://adaptivecrawl-kir3wx7b3a-uc.a.run.app", | |||
| json={ | |||
| "url": request.url, | |||
| @@ -287,7 +287,7 @@ class WebsiteService: | |||
| @classmethod | |||
| def _get_jinareader_status(cls, job_id: str, api_key: str) -> dict[str, Any]: | |||
| response = requests.post( | |||
| response = httpx.post( | |||
| "https://adaptivecrawlstatus-kir3wx7b3a-uc.a.run.app", | |||
| headers={"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}, | |||
| json={"taskId": job_id}, | |||
| @@ -303,7 +303,7 @@ class WebsiteService: | |||
| } | |||
| if crawl_status_data["status"] == "completed": | |||
| response = requests.post( | |||
| response = httpx.post( | |||
| "https://adaptivecrawlstatus-kir3wx7b3a-uc.a.run.app", | |||
| headers={"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}, | |||
| json={"taskId": job_id, "urls": list(data.get("processed", {}).keys())}, | |||
| @@ -362,7 +362,7 @@ class WebsiteService: | |||
| @classmethod | |||
| def _get_jinareader_url_data(cls, job_id: str, url: str, api_key: str) -> dict[str, Any] | None: | |||
| if not job_id: | |||
| response = requests.get( | |||
| response = httpx.get( | |||
| f"https://r.jina.ai/{url}", | |||
| headers={"Accept": "application/json", "Authorization": f"Bearer {api_key}"}, | |||
| ) | |||
| @@ -371,7 +371,7 @@ class WebsiteService: | |||
| return dict(response.json().get("data", {})) | |||
| else: | |||
| # Get crawl status first | |||
| status_response = requests.post( | |||
| status_response = httpx.post( | |||
| "https://adaptivecrawlstatus-kir3wx7b3a-uc.a.run.app", | |||
| headers={"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}, | |||
| json={"taskId": job_id}, | |||
| @@ -381,7 +381,7 @@ class WebsiteService: | |||
| raise ValueError("Crawl job is not completed") | |||
| # Get processed data | |||
| data_response = requests.post( | |||
| data_response = httpx.post( | |||
| "https://adaptivecrawlstatus-kir3wx7b3a-uc.a.run.app", | |||
| headers={"Content-Type": "application/json", "Authorization": f"Bearer {api_key}"}, | |||
| json={"taskId": job_id, "urls": list(status_data.get("processed", {}).keys())}, | |||
| @@ -1,8 +1,8 @@ | |||
| import os | |||
| from typing import Literal | |||
| import httpx | |||
| import pytest | |||
| import requests | |||
| from core.plugin.entities.plugin_daemon import PluginDaemonBasicResponse | |||
| from core.tools.entities.common_entities import I18nObject | |||
| @@ -27,13 +27,11 @@ class MockedHttp: | |||
| @classmethod | |||
| def requests_request( | |||
| cls, method: Literal["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD"], url: str, **kwargs | |||
| ) -> requests.Response: | |||
| ) -> httpx.Response: | |||
| """ | |||
| Mocked requests.request | |||
| Mocked httpx.request | |||
| """ | |||
| request = requests.PreparedRequest() | |||
| request.method = method | |||
| request.url = url | |||
| request = httpx.Request(method, url) | |||
| if url.endswith("/tools"): | |||
| content = PluginDaemonBasicResponse[list[ToolProviderEntity]]( | |||
| code=0, message="success", data=cls.list_tools() | |||
| @@ -41,8 +39,7 @@ class MockedHttp: | |||
| else: | |||
| raise ValueError("") | |||
| response = requests.Response() | |||
| response.status_code = 200 | |||
| response = httpx.Response(status_code=200) | |||
| response.request = request | |||
| response._content = content.encode("utf-8") | |||
| return response | |||
| @@ -54,7 +51,7 @@ MOCK_SWITCH = os.getenv("MOCK_SWITCH", "false").lower() == "true" | |||
| @pytest.fixture | |||
| def setup_http_mock(request, monkeypatch: pytest.MonkeyPatch): | |||
| if MOCK_SWITCH: | |||
| monkeypatch.setattr(requests, "request", MockedHttp.requests_request) | |||
| monkeypatch.setattr(httpx, "request", MockedHttp.requests_request) | |||
| def unpatch(): | |||
| monkeypatch.undo() | |||
| @@ -6,7 +6,7 @@ Test Clickzetta integration in Docker environment | |||
| import os | |||
| import time | |||
| import requests | |||
| import httpx | |||
| from clickzetta import connect | |||
| @@ -66,7 +66,7 @@ def test_dify_api(): | |||
| max_retries = 30 | |||
| for i in range(max_retries): | |||
| try: | |||
| response = requests.get(f"{base_url}/console/api/health") | |||
| response = httpx.get(f"{base_url}/console/api/health") | |||
| if response.status_code == 200: | |||
| print("✓ Dify API is ready") | |||
| break | |||
| @@ -201,9 +201,9 @@ class TestOAuthCallback: | |||
| mock_db.session.rollback = MagicMock() | |||
| # Import the real requests module to create a proper exception | |||
| import requests | |||
| import httpx | |||
| request_exception = requests.exceptions.RequestException("OAuth error") | |||
| request_exception = httpx.RequestError("OAuth error") | |||
| request_exception.response = MagicMock() | |||
| request_exception.response.text = str(exception) | |||
| @@ -1,8 +1,8 @@ | |||
| import urllib.parse | |||
| from unittest.mock import MagicMock, patch | |||
| import httpx | |||
| import pytest | |||
| import requests | |||
| from libs.oauth import GitHubOAuth, GoogleOAuth, OAuthUserInfo | |||
| @@ -68,7 +68,7 @@ class TestGitHubOAuth(BaseOAuthTest): | |||
| ({}, None, True), | |||
| ], | |||
| ) | |||
| @patch("requests.post") | |||
| @patch("httpx.post") | |||
| def test_should_retrieve_access_token( | |||
| self, mock_post, oauth, mock_response, response_data, expected_token, should_raise | |||
| ): | |||
| @@ -105,7 +105,7 @@ class TestGitHubOAuth(BaseOAuthTest): | |||
| ), | |||
| ], | |||
| ) | |||
| @patch("requests.get") | |||
| @patch("httpx.get") | |||
| def test_should_retrieve_user_info_correctly(self, mock_get, oauth, user_data, email_data, expected_email): | |||
| user_response = MagicMock() | |||
| user_response.json.return_value = user_data | |||
| @@ -121,11 +121,11 @@ class TestGitHubOAuth(BaseOAuthTest): | |||
| assert user_info.name == user_data["name"] | |||
| assert user_info.email == expected_email | |||
| @patch("requests.get") | |||
| @patch("httpx.get") | |||
| def test_should_handle_network_errors(self, mock_get, oauth): | |||
| mock_get.side_effect = requests.exceptions.RequestException("Network error") | |||
| mock_get.side_effect = httpx.RequestError("Network error") | |||
| with pytest.raises(requests.exceptions.RequestException): | |||
| with pytest.raises(httpx.RequestError): | |||
| oauth.get_raw_user_info("test_token") | |||
| @@ -167,7 +167,7 @@ class TestGoogleOAuth(BaseOAuthTest): | |||
| ({}, None, True), | |||
| ], | |||
| ) | |||
| @patch("requests.post") | |||
| @patch("httpx.post") | |||
| def test_should_retrieve_access_token( | |||
| self, mock_post, oauth, oauth_config, mock_response, response_data, expected_token, should_raise | |||
| ): | |||
| @@ -201,7 +201,7 @@ class TestGoogleOAuth(BaseOAuthTest): | |||
| ({"sub": "123", "email": "test@example.com", "name": "Test User"}, ""), # Always returns empty string | |||
| ], | |||
| ) | |||
| @patch("requests.get") | |||
| @patch("httpx.get") | |||
| def test_should_retrieve_user_info_correctly(self, mock_get, oauth, mock_response, user_data, expected_name): | |||
| mock_response.json.return_value = user_data | |||
| mock_get.return_value = mock_response | |||
| @@ -217,12 +217,12 @@ class TestGoogleOAuth(BaseOAuthTest): | |||
| @pytest.mark.parametrize( | |||
| "exception_type", | |||
| [ | |||
| requests.exceptions.HTTPError, | |||
| requests.exceptions.ConnectionError, | |||
| requests.exceptions.Timeout, | |||
| httpx.HTTPError, | |||
| httpx.ConnectError, | |||
| httpx.TimeoutException, | |||
| ], | |||
| ) | |||
| @patch("requests.get") | |||
| @patch("httpx.get") | |||
| def test_should_handle_http_errors(self, mock_get, oauth, exception_type): | |||
| mock_response = MagicMock() | |||
| mock_response.raise_for_status.side_effect = exception_type("Error") | |||
| @@ -6,8 +6,8 @@ import json | |||
| from concurrent.futures import ThreadPoolExecutor | |||
| from unittest.mock import Mock, patch | |||
| import httpx | |||
| import pytest | |||
| import requests | |||
| from services.auth.api_key_auth_factory import ApiKeyAuthFactory | |||
| from services.auth.api_key_auth_service import ApiKeyAuthService | |||
| @@ -26,7 +26,7 @@ class TestAuthIntegration: | |||
| self.watercrawl_credentials = {"auth_type": "x-api-key", "config": {"api_key": "wc_test_key_789"}} | |||
| @patch("services.auth.api_key_auth_service.db.session") | |||
| @patch("services.auth.firecrawl.firecrawl.requests.post") | |||
| @patch("services.auth.firecrawl.firecrawl.httpx.post") | |||
| @patch("services.auth.api_key_auth_service.encrypter.encrypt_token") | |||
| def test_end_to_end_auth_flow(self, mock_encrypt, mock_http, mock_session): | |||
| """Test complete authentication flow: request → validation → encryption → storage""" | |||
| @@ -47,7 +47,7 @@ class TestAuthIntegration: | |||
| mock_session.add.assert_called_once() | |||
| mock_session.commit.assert_called_once() | |||
| @patch("services.auth.firecrawl.firecrawl.requests.post") | |||
| @patch("services.auth.firecrawl.firecrawl.httpx.post") | |||
| def test_cross_component_integration(self, mock_http): | |||
| """Test factory → provider → HTTP call integration""" | |||
| mock_http.return_value = self._create_success_response() | |||
| @@ -97,7 +97,7 @@ class TestAuthIntegration: | |||
| assert "another_secret" not in factory_str | |||
| @patch("services.auth.api_key_auth_service.db.session") | |||
| @patch("services.auth.firecrawl.firecrawl.requests.post") | |||
| @patch("services.auth.firecrawl.firecrawl.httpx.post") | |||
| @patch("services.auth.api_key_auth_service.encrypter.encrypt_token") | |||
| def test_concurrent_creation_safety(self, mock_encrypt, mock_http, mock_session): | |||
| """Test concurrent authentication creation safety""" | |||
| @@ -142,31 +142,31 @@ class TestAuthIntegration: | |||
| with pytest.raises((ValueError, KeyError, TypeError, AttributeError)): | |||
| ApiKeyAuthFactory(AuthType.FIRECRAWL, invalid_input) | |||
| @patch("services.auth.firecrawl.firecrawl.requests.post") | |||
| @patch("services.auth.firecrawl.firecrawl.httpx.post") | |||
| def test_http_error_handling(self, mock_http): | |||
| """Test proper HTTP error handling""" | |||
| mock_response = Mock() | |||
| mock_response.status_code = 401 | |||
| mock_response.text = '{"error": "Unauthorized"}' | |||
| mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError("Unauthorized") | |||
| mock_response.raise_for_status.side_effect = httpx.HTTPError("Unauthorized") | |||
| mock_http.return_value = mock_response | |||
| # PT012: Split into single statement for pytest.raises | |||
| factory = ApiKeyAuthFactory(AuthType.FIRECRAWL, self.firecrawl_credentials) | |||
| with pytest.raises((requests.exceptions.HTTPError, Exception)): | |||
| with pytest.raises((httpx.HTTPError, Exception)): | |||
| factory.validate_credentials() | |||
| @patch("services.auth.api_key_auth_service.db.session") | |||
| @patch("services.auth.firecrawl.firecrawl.requests.post") | |||
| @patch("services.auth.firecrawl.firecrawl.httpx.post") | |||
| def test_network_failure_recovery(self, mock_http, mock_session): | |||
| """Test system recovery from network failures""" | |||
| mock_http.side_effect = requests.exceptions.RequestException("Network timeout") | |||
| mock_http.side_effect = httpx.RequestError("Network timeout") | |||
| mock_session.add = Mock() | |||
| mock_session.commit = Mock() | |||
| args = {"category": self.category, "provider": AuthType.FIRECRAWL, "credentials": self.firecrawl_credentials} | |||
| with pytest.raises(requests.exceptions.RequestException): | |||
| with pytest.raises(httpx.RequestError): | |||
| ApiKeyAuthService.create_provider_auth(self.tenant_id_1, args) | |||
| mock_session.commit.assert_not_called() | |||
| @@ -1,7 +1,7 @@ | |||
| from unittest.mock import MagicMock, patch | |||
| import httpx | |||
| import pytest | |||
| import requests | |||
| from services.auth.firecrawl.firecrawl import FirecrawlAuth | |||
| @@ -64,7 +64,7 @@ class TestFirecrawlAuth: | |||
| FirecrawlAuth(credentials) | |||
| assert str(exc_info.value) == expected_error | |||
| @patch("services.auth.firecrawl.firecrawl.requests.post") | |||
| @patch("services.auth.firecrawl.firecrawl.httpx.post") | |||
| def test_should_validate_valid_credentials_successfully(self, mock_post, auth_instance): | |||
| """Test successful credential validation""" | |||
| mock_response = MagicMock() | |||
| @@ -95,7 +95,7 @@ class TestFirecrawlAuth: | |||
| (500, "Internal server error"), | |||
| ], | |||
| ) | |||
| @patch("services.auth.firecrawl.firecrawl.requests.post") | |||
| @patch("services.auth.firecrawl.firecrawl.httpx.post") | |||
| def test_should_handle_http_errors(self, mock_post, status_code, error_message, auth_instance): | |||
| """Test handling of various HTTP error codes""" | |||
| mock_response = MagicMock() | |||
| @@ -115,7 +115,7 @@ class TestFirecrawlAuth: | |||
| (401, "Not JSON", True, "Expecting value"), # JSON decode error | |||
| ], | |||
| ) | |||
| @patch("services.auth.firecrawl.firecrawl.requests.post") | |||
| @patch("services.auth.firecrawl.firecrawl.httpx.post") | |||
| def test_should_handle_unexpected_errors( | |||
| self, mock_post, status_code, response_text, has_json_error, expected_error_contains, auth_instance | |||
| ): | |||
| @@ -134,13 +134,13 @@ class TestFirecrawlAuth: | |||
| @pytest.mark.parametrize( | |||
| ("exception_type", "exception_message"), | |||
| [ | |||
| (requests.ConnectionError, "Network error"), | |||
| (requests.Timeout, "Request timeout"), | |||
| (requests.ReadTimeout, "Read timeout"), | |||
| (requests.ConnectTimeout, "Connection timeout"), | |||
| (httpx.ConnectError, "Network error"), | |||
| (httpx.TimeoutException, "Request timeout"), | |||
| (httpx.ReadTimeout, "Read timeout"), | |||
| (httpx.ConnectTimeout, "Connection timeout"), | |||
| ], | |||
| ) | |||
| @patch("services.auth.firecrawl.firecrawl.requests.post") | |||
| @patch("services.auth.firecrawl.firecrawl.httpx.post") | |||
| def test_should_handle_network_errors(self, mock_post, exception_type, exception_message, auth_instance): | |||
| """Test handling of various network-related errors including timeouts""" | |||
| mock_post.side_effect = exception_type(exception_message) | |||
| @@ -162,7 +162,7 @@ class TestFirecrawlAuth: | |||
| FirecrawlAuth({"auth_type": "basic", "config": {"api_key": "super_secret_key_12345"}}) | |||
| assert "super_secret_key_12345" not in str(exc_info.value) | |||
| @patch("services.auth.firecrawl.firecrawl.requests.post") | |||
| @patch("services.auth.firecrawl.firecrawl.httpx.post") | |||
| def test_should_use_custom_base_url_in_validation(self, mock_post): | |||
| """Test that custom base URL is used in validation""" | |||
| mock_response = MagicMock() | |||
| @@ -179,12 +179,12 @@ class TestFirecrawlAuth: | |||
| assert result is True | |||
| assert mock_post.call_args[0][0] == "https://custom.firecrawl.dev/v1/crawl" | |||
| @patch("services.auth.firecrawl.firecrawl.requests.post") | |||
| @patch("services.auth.firecrawl.firecrawl.httpx.post") | |||
| def test_should_handle_timeout_with_retry_suggestion(self, mock_post, auth_instance): | |||
| """Test that timeout errors are handled gracefully with appropriate error message""" | |||
| mock_post.side_effect = requests.Timeout("The request timed out after 30 seconds") | |||
| mock_post.side_effect = httpx.TimeoutException("The request timed out after 30 seconds") | |||
| with pytest.raises(requests.Timeout) as exc_info: | |||
| with pytest.raises(httpx.TimeoutException) as exc_info: | |||
| auth_instance.validate_credentials() | |||
| # Verify the timeout exception is raised with original message | |||
| @@ -1,7 +1,7 @@ | |||
| from unittest.mock import MagicMock, patch | |||
| import httpx | |||
| import pytest | |||
| import requests | |||
| from services.auth.jina.jina import JinaAuth | |||
| @@ -35,7 +35,7 @@ class TestJinaAuth: | |||
| JinaAuth(credentials) | |||
| assert str(exc_info.value) == "No API key provided" | |||
| @patch("services.auth.jina.jina.requests.post") | |||
| @patch("services.auth.jina.jina.httpx.post") | |||
| def test_should_validate_valid_credentials_successfully(self, mock_post): | |||
| """Test successful credential validation""" | |||
| mock_response = MagicMock() | |||
| @@ -53,7 +53,7 @@ class TestJinaAuth: | |||
| json={"url": "https://example.com"}, | |||
| ) | |||
| @patch("services.auth.jina.jina.requests.post") | |||
| @patch("services.auth.jina.jina.httpx.post") | |||
| def test_should_handle_http_402_error(self, mock_post): | |||
| """Test handling of 402 Payment Required error""" | |||
| mock_response = MagicMock() | |||
| @@ -68,7 +68,7 @@ class TestJinaAuth: | |||
| auth.validate_credentials() | |||
| assert str(exc_info.value) == "Failed to authorize. Status code: 402. Error: Payment required" | |||
| @patch("services.auth.jina.jina.requests.post") | |||
| @patch("services.auth.jina.jina.httpx.post") | |||
| def test_should_handle_http_409_error(self, mock_post): | |||
| """Test handling of 409 Conflict error""" | |||
| mock_response = MagicMock() | |||
| @@ -83,7 +83,7 @@ class TestJinaAuth: | |||
| auth.validate_credentials() | |||
| assert str(exc_info.value) == "Failed to authorize. Status code: 409. Error: Conflict error" | |||
| @patch("services.auth.jina.jina.requests.post") | |||
| @patch("services.auth.jina.jina.httpx.post") | |||
| def test_should_handle_http_500_error(self, mock_post): | |||
| """Test handling of 500 Internal Server Error""" | |||
| mock_response = MagicMock() | |||
| @@ -98,7 +98,7 @@ class TestJinaAuth: | |||
| auth.validate_credentials() | |||
| assert str(exc_info.value) == "Failed to authorize. Status code: 500. Error: Internal server error" | |||
| @patch("services.auth.jina.jina.requests.post") | |||
| @patch("services.auth.jina.jina.httpx.post") | |||
| def test_should_handle_unexpected_error_with_text_response(self, mock_post): | |||
| """Test handling of unexpected errors with text response""" | |||
| mock_response = MagicMock() | |||
| @@ -114,7 +114,7 @@ class TestJinaAuth: | |||
| auth.validate_credentials() | |||
| assert str(exc_info.value) == "Failed to authorize. Status code: 403. Error: Forbidden" | |||
| @patch("services.auth.jina.jina.requests.post") | |||
| @patch("services.auth.jina.jina.httpx.post") | |||
| def test_should_handle_unexpected_error_without_text(self, mock_post): | |||
| """Test handling of unexpected errors without text response""" | |||
| mock_response = MagicMock() | |||
| @@ -130,15 +130,15 @@ class TestJinaAuth: | |||
| auth.validate_credentials() | |||
| assert str(exc_info.value) == "Unexpected error occurred while trying to authorize. Status code: 404" | |||
| @patch("services.auth.jina.jina.requests.post") | |||
| @patch("services.auth.jina.jina.httpx.post") | |||
| def test_should_handle_network_errors(self, mock_post): | |||
| """Test handling of network connection errors""" | |||
| mock_post.side_effect = requests.ConnectionError("Network error") | |||
| mock_post.side_effect = httpx.ConnectError("Network error") | |||
| credentials = {"auth_type": "bearer", "config": {"api_key": "test_api_key_123"}} | |||
| auth = JinaAuth(credentials) | |||
| with pytest.raises(requests.ConnectionError): | |||
| with pytest.raises(httpx.ConnectError): | |||
| auth.validate_credentials() | |||
| def test_should_not_expose_api_key_in_error_messages(self): | |||
| @@ -1,7 +1,7 @@ | |||
| from unittest.mock import MagicMock, patch | |||
| import httpx | |||
| import pytest | |||
| import requests | |||
| from services.auth.watercrawl.watercrawl import WatercrawlAuth | |||
| @@ -64,7 +64,7 @@ class TestWatercrawlAuth: | |||
| WatercrawlAuth(credentials) | |||
| assert str(exc_info.value) == expected_error | |||
| @patch("services.auth.watercrawl.watercrawl.requests.get") | |||
| @patch("services.auth.watercrawl.watercrawl.httpx.get") | |||
| def test_should_validate_valid_credentials_successfully(self, mock_get, auth_instance): | |||
| """Test successful credential validation""" | |||
| mock_response = MagicMock() | |||
| @@ -87,7 +87,7 @@ class TestWatercrawlAuth: | |||
| (500, "Internal server error"), | |||
| ], | |||
| ) | |||
| @patch("services.auth.watercrawl.watercrawl.requests.get") | |||
| @patch("services.auth.watercrawl.watercrawl.httpx.get") | |||
| def test_should_handle_http_errors(self, mock_get, status_code, error_message, auth_instance): | |||
| """Test handling of various HTTP error codes""" | |||
| mock_response = MagicMock() | |||
| @@ -107,7 +107,7 @@ class TestWatercrawlAuth: | |||
| (401, "Not JSON", True, "Expecting value"), # JSON decode error | |||
| ], | |||
| ) | |||
| @patch("services.auth.watercrawl.watercrawl.requests.get") | |||
| @patch("services.auth.watercrawl.watercrawl.httpx.get") | |||
| def test_should_handle_unexpected_errors( | |||
| self, mock_get, status_code, response_text, has_json_error, expected_error_contains, auth_instance | |||
| ): | |||
| @@ -126,13 +126,13 @@ class TestWatercrawlAuth: | |||
| @pytest.mark.parametrize( | |||
| ("exception_type", "exception_message"), | |||
| [ | |||
| (requests.ConnectionError, "Network error"), | |||
| (requests.Timeout, "Request timeout"), | |||
| (requests.ReadTimeout, "Read timeout"), | |||
| (requests.ConnectTimeout, "Connection timeout"), | |||
| (httpx.ConnectError, "Network error"), | |||
| (httpx.TimeoutException, "Request timeout"), | |||
| (httpx.ReadTimeout, "Read timeout"), | |||
| (httpx.ConnectTimeout, "Connection timeout"), | |||
| ], | |||
| ) | |||
| @patch("services.auth.watercrawl.watercrawl.requests.get") | |||
| @patch("services.auth.watercrawl.watercrawl.httpx.get") | |||
| def test_should_handle_network_errors(self, mock_get, exception_type, exception_message, auth_instance): | |||
| """Test handling of various network-related errors including timeouts""" | |||
| mock_get.side_effect = exception_type(exception_message) | |||
| @@ -154,7 +154,7 @@ class TestWatercrawlAuth: | |||
| WatercrawlAuth({"auth_type": "bearer", "config": {"api_key": "super_secret_key_12345"}}) | |||
| assert "super_secret_key_12345" not in str(exc_info.value) | |||
| @patch("services.auth.watercrawl.watercrawl.requests.get") | |||
| @patch("services.auth.watercrawl.watercrawl.httpx.get") | |||
| def test_should_use_custom_base_url_in_validation(self, mock_get): | |||
| """Test that custom base URL is used in validation""" | |||
| mock_response = MagicMock() | |||
| @@ -179,7 +179,7 @@ class TestWatercrawlAuth: | |||
| ("https://app.watercrawl.dev//", "https://app.watercrawl.dev/api/v1/core/crawl-requests/"), | |||
| ], | |||
| ) | |||
| @patch("services.auth.watercrawl.watercrawl.requests.get") | |||
| @patch("services.auth.watercrawl.watercrawl.httpx.get") | |||
| def test_should_use_urljoin_for_url_construction(self, mock_get, base_url, expected_url): | |||
| """Test that urljoin is used correctly for URL construction with various base URLs""" | |||
| mock_response = MagicMock() | |||
| @@ -193,12 +193,12 @@ class TestWatercrawlAuth: | |||
| # Verify the correct URL was called | |||
| assert mock_get.call_args[0][0] == expected_url | |||
| @patch("services.auth.watercrawl.watercrawl.requests.get") | |||
| @patch("services.auth.watercrawl.watercrawl.httpx.get") | |||
| def test_should_handle_timeout_with_retry_suggestion(self, mock_get, auth_instance): | |||
| """Test that timeout errors are handled gracefully with appropriate error message""" | |||
| mock_get.side_effect = requests.Timeout("The request timed out after 30 seconds") | |||
| mock_get.side_effect = httpx.TimeoutException("The request timed out after 30 seconds") | |||
| with pytest.raises(requests.Timeout) as exc_info: | |||
| with pytest.raises(httpx.TimeoutException) as exc_info: | |||
| auth_instance.validate_credentials() | |||
| # Verify the timeout exception is raised with original message | |||