""" Unit tests for cleverswarm_client module. Copyright (c) 2016 - present Syncleus, Inc. Copyright (c) 2016 - present CleverThis, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ import tempfile from pathlib import Path from unittest.mock import Mock, patch import pytest import json from httmock import HTTMock, urlmatch from cleverswarm_python_client.libs.cleverswarm_client import CleverSwarmClient from cleverswarm_python_client.libs.exceptions import ( InvalidFileException, JobNotFoundException, UnauthorizedException, InvalidUsernameOrPasswordException, RequestErrorException, ConnectionErrorException, ConnectionTimeoutException, UnexpectedConditionException, ) from cleverswarm_python_client.libs.job_enums import JobStatus from cleverswarm_python_client.libs.file_type_enum import FileTypeAPI from cleverswarm_python_client.libs.response_type_enum import ResponseTypeAPI class TestCleverSwarmClient: """Test cases for CleverSwarmClient.""" def setup_method(self): """Set up test client.""" self.client = CleverSwarmClient("http://localhost:8000/api/v0/") def test_init_with_base_url(self): """Test initialization with base URL.""" client = CleverSwarmClient("http://localhost:8000/api/v0/") assert client._base_url == "http://localhost:8000/api/v0/" assert client._username is None assert client._token is None def test_init_with_username_and_token(self): """Test initialization with username and token.""" client = CleverSwarmClient( "http://localhost:8000/api/v0/", username="testuser", token="testtoken" ) assert client._base_url == "http://localhost:8000/api/v0/" assert client._username == "testuser" assert client._token == "testtoken" def test_init_with_empty_base_url(self): """Test initialization with empty base URL.""" client = CleverSwarmClient("") assert client._base_url == "" def test_init_with_none_base_url(self): """Test initialization with None base URL.""" client = CleverSwarmClient(None) assert client._base_url is None def test_client_initialization_attributes(self): """Test client initialization attributes.""" client = CleverSwarmClient("http://localhost:8000/api/v0/") # Test all default attributes assert client._base_url == "http://localhost:8000/api/v0/" assert client._username is None assert client._token is None assert client._max_retries == 2 assert client._timeout == 10 assert client._triplets_template == "{}_output.jsonl" def test_client_initialization_with_credentials(self): """Test client initialization with various credential combinations.""" # Test with all parameters client = CleverSwarmClient( "http://localhost:8000/api/v0/", username="testuser", token="testtoken" ) assert client._username == "testuser" assert client._token == "testtoken" def test_client_with_custom_attributes(self): """Test client with custom attributes.""" client = CleverSwarmClient("http://localhost:8000/api/v0/") # Test that we can modify attributes client._max_retries = 5 client._timeout = 30 client._triplets_template = "custom_{}_output.jsonl" assert client._max_retries == 5 assert client._timeout == 30 assert client._triplets_template == "custom_{}_output.jsonl" def test_file_validation_with_missing_files(self): """Test file validation with missing files.""" # Test with non-existent files - this should raise InvalidFileException before calling update_token # We need to mock the update_token method to avoid the input() call with patch.object(self.client, 'update_token'): with pytest.raises(InvalidFileException): self.client.create_benchmark_job( Path("nonexistent1.txt"), Path("nonexistent2.json"), Path("nonexistent3.jsonl"), True ) def test_file_validation_with_existing_files(self): """Test file validation with existing files.""" with tempfile.TemporaryDirectory() as temp_dir: # Create valid test files text_file = Path(temp_dir) / "test.txt" json_file = Path(temp_dir) / "test.json" jsonl_file = Path(temp_dir) / "test.jsonl" text_file.write_text("Test content") json_file.write_text('{"test": "data"}') jsonl_file.write_text('{"test": "data"}') # Test that files exist and are valid files assert text_file.exists() assert text_file.is_file() assert json_file.exists() assert json_file.is_file() assert jsonl_file.exists() assert jsonl_file.is_file() # Test that the client can validate these files by calling create_benchmark_job # We need to mock the update_token and the HTTP request to avoid actual API calls with patch.object(self.client, 'update_token'): with patch.object(self.client, '_generic_http_request_executor') as mock_executor: # Mock the executor to return the job_id directly mock_executor.return_value = "test_job_123" # This should not raise InvalidFileException result = self.client.create_benchmark_job( text_file, json_file, jsonl_file, True ) assert result == "test_job_123" def test_client_state_management(self): """Test client state management.""" client = CleverSwarmClient("http://localhost:8000/api/v0/") # Test initial state assert client._token is None assert client._username is None # Test setting token client._token = "test_token" assert client._token == "test_token" # Test setting username client._username = "test_user" assert client._username == "test_user" def test_client_configuration(self): """Test client configuration options.""" client = CleverSwarmClient("http://localhost:8000/api/v0/") # Test default configuration assert client._max_retries == 2 assert client._timeout == 10 # Test modifying configuration client._max_retries = 5 client._timeout = 30 assert client._max_retries == 5 assert client._timeout == 30 def test_triplets_template(self): """Test triplets template functionality.""" client = CleverSwarmClient("http://localhost:8000/api/v0/") # Test default template assert client._triplets_template == "{}_output.jsonl" # Test template formatting formatted = client._triplets_template.format("test_job") assert formatted == "test_job_output.jsonl" # Test custom template client._triplets_template = "custom_{}_result.jsonl" formatted = client._triplets_template.format("test_job") assert formatted == "custom_test_job_result.jsonl" def test_poll_job_ready_or_failed_completed(self): """Test polling job that is already completed.""" with patch.object(self.client, 'get_job_status') as mock_status: mock_status.return_value = JobStatus.Completed result = self.client.poll_job_ready_or_failed("job123") assert result == JobStatus.Completed mock_status.assert_called_once_with("job123") def test_poll_job_ready_or_failed_failed(self): """Test polling job that has failed.""" with patch.object(self.client, 'get_job_status') as mock_status: mock_status.return_value = JobStatus.Failed result = self.client.poll_job_ready_or_failed("job123") assert result == JobStatus.Failed mock_status.assert_called_once_with("job123") def test_poll_job_ready_or_failed_processing(self): """Test polling job that is still processing.""" with patch.object(self.client, 'get_job_status') as mock_status: # First call returns Processing, second call returns Completed mock_status.side_effect = [JobStatus.Processing, JobStatus.Completed] with patch('time.sleep'): # Mock sleep to speed up test result = self.client.poll_job_ready_or_failed("job123") assert result == JobStatus.Completed assert mock_status.call_count == 2 def test_generic_http_request_executor_success(self): """Test generic HTTP request executor with successful response.""" def test_func(): mock_response = Mock() mock_response.status_code = 200 return mock_response result = self.client._generic_http_request_executor(test_func) assert result.status_code == 200 def test_generic_http_request_executor_max_retries_exceeded(self): """Test generic HTTP request executor with max retries exceeded.""" def test_func(): raise Exception("Persistent failure") with patch('time.sleep'): # Mock sleep to speed up test with pytest.raises(Exception): self.client._generic_http_request_executor(test_func) def test_job_status_enum_values(self): """Test JobStatus enum values.""" assert JobStatus.ReadyForProcessing == "ReadyForProcessing" assert JobStatus.Processing == "Processing" assert JobStatus.Completed == "Completed" assert JobStatus.Failed == "Failed" def test_file_type_enum_values(self): """Test FileTypeAPI enum values.""" assert FileTypeAPI.AutoDetect == "AutoDetect" assert FileTypeAPI.Markdown == "Markdown" assert FileTypeAPI.PlainText == "PlainText" assert FileTypeAPI.PDF == "PDF" def test_response_type_enum_values(self): """Test ResponseTypeAPI enum values.""" assert ResponseTypeAPI.JsonText == "JsonText" assert ResponseTypeAPI.JsonFile == "JsonFile" def test_client_with_different_base_urls(self): """Test client with different base URL formats.""" # Test with trailing slash client1 = CleverSwarmClient("http://localhost:8000/api/v0/") assert client1._base_url == "http://localhost:8000/api/v0/" # Test without trailing slash client2 = CleverSwarmClient("http://localhost:8000/api/v0") assert client2._base_url == "http://localhost:8000/api/v0" # Test with different port client3 = CleverSwarmClient("http://localhost:9000/api/v1/") assert client3._base_url == "http://localhost:9000/api/v1/" def test_client_initialization_edge_cases(self): """Test client initialization with edge cases.""" # Test with empty string client1 = CleverSwarmClient("") assert client1._base_url == "" # Test with None client2 = CleverSwarmClient(None) assert client2._base_url is None # Test with just username client3 = CleverSwarmClient("http://localhost:8000/api/v0/", username="testuser") assert client3._username == "testuser" assert client3._token is None # Test with just token client4 = CleverSwarmClient("http://localhost:8000/api/v0/", token="testtoken") assert client4._token == "testtoken" assert client4._username is None def test_client_attribute_modification(self): """Test that client attributes can be modified after initialization.""" client = CleverSwarmClient("http://localhost:8000/api/v0/") # Modify all attributes client._base_url = "http://newhost:9000/api/v1/" client._username = "newuser" client._token = "newtoken" client._max_retries = 10 client._timeout = 60 client._triplets_template = "new_{}_template.jsonl" # Verify changes assert client._base_url == "http://newhost:9000/api/v1/" assert client._username == "newuser" assert client._token == "newtoken" assert client._max_retries == 10 assert client._timeout == 60 assert client._triplets_template == "new_{}_template.jsonl" def test_client_string_representation(self): """Test client string representation.""" client = CleverSwarmClient("http://localhost:8000/api/v0/") # Test that the client object can be converted to string client_str = str(client) assert isinstance(client_str, str) assert "CleverSwarmClient" in client_str def test_client_equality(self): """Test client equality comparison.""" client1 = CleverSwarmClient("http://localhost:8000/api/v0/") client2 = CleverSwarmClient("http://localhost:8000/api/v0/") client3 = CleverSwarmClient("http://localhost:9000/api/v0/") # Different instances should not be equal assert client1 != client2 assert client1 != client3 # Same instance should be equal to itself assert client1 == client1 def test_client_hash(self): """Test client hash functionality.""" client = CleverSwarmClient("http://localhost:8000/api/v0/") # Test that client can be hashed client_hash = hash(client) assert isinstance(client_hash, int) # Test that same client has same hash assert hash(client) == hash(client) def test_client_with_credentials_initialization(self): """Test client initialization with various credential combinations.""" # Test with both username and token client1 = CleverSwarmClient( "http://localhost:8000/api/v0/", username="user1", token="token1" ) assert client1._username == "user1" assert client1._token == "token1" # Test with only username client2 = CleverSwarmClient( "http://localhost:8000/api/v0/", username="user2" ) assert client2._username == "user2" assert client2._token is None # Test with only token client3 = CleverSwarmClient( "http://localhost:8000/api/v0/", token="token3" ) assert client3._username is None assert client3._token == "token3" def test_client_template_functionality(self): """Test client template functionality in detail.""" client = CleverSwarmClient("http://localhost:8000/api/v0/") # Test default template assert client._triplets_template == "{}_output.jsonl" # Test template with different job IDs job_ids = ["job1", "job2", "test_job_123", "my-special-job"] for job_id in job_ids: expected = f"{job_id}_output.jsonl" actual = client._triplets_template.format(job_id) assert actual == expected # Test custom template client._triplets_template = "results_{}_final.jsonl" for job_id in job_ids: expected = f"results_{job_id}_final.jsonl" actual = client._triplets_template.format(job_id) assert actual == expected def test_client_retry_configuration(self): """Test client retry configuration.""" client = CleverSwarmClient("http://localhost:8000/api/v0/") # Test default retry configuration assert client._max_retries == 2 # Test setting different retry values for retries in [0, 1, 5, 10]: client._max_retries = retries assert client._max_retries == retries def test_client_timeout_configuration(self): """Test client timeout configuration.""" client = CleverSwarmClient("http://localhost:8000/api/v0/") # Test default timeout assert client._timeout == 10 # Test setting different timeout values for timeout in [1, 5, 30, 60, 120]: client._timeout = timeout assert client._timeout == timeout class TestCleverSwarmClientHTTP: """Test cases for CleverSwarmClient HTTP operations using httmock.""" def setup_method(self): """Set up test client.""" self.client = CleverSwarmClient("http://localhost:8000/api/v0/", token="test_token") # Authentication Tests @urlmatch(netloc='localhost:8000', path='/api/v0/login', method='POST') def login_success_mock(self, url, request): """Mock successful login response.""" return { 'status_code': 200, 'content': json.dumps({ 'access_token': 'new_token', 'token_type': 'bearer' }).encode('utf-8'), 'headers': {'content-type': 'application/json'} } @urlmatch(netloc='localhost:8000', path='/api/v0/login', method='POST') def login_unauthorized_mock(self, url, request): """Mock unauthorized login response.""" return { 'status_code': 401, 'content': b'User provided credentials are not correct.', 'headers': {'content-type': 'text/plain'} } @urlmatch(netloc='localhost:8000', path='/api/v0/login', method='POST') def login_invalid_credentials_mock(self, url, request): """Mock invalid credentials login response.""" return { 'status_code': 400, 'content': b'Invalid credentials', 'headers': {'content-type': 'text/plain'} } @patch('cleverswarm_python_client.libs.cleverswarm_client.getpass') def test_update_token_success(self, mock_getpass): """Test successful token update using httmock.""" mock_getpass.return_value = "testpassword" with HTTMock(self.login_success_mock): client = CleverSwarmClient("http://localhost:8000/api/v0/", username="testuser") result = client.update_token() assert result is client # Should return self assert client._token == "new_token" @patch('cleverswarm_python_client.libs.cleverswarm_client.getpass') def test_update_token_unauthorized(self, mock_getpass): """Test token update with unauthorized response using httmock.""" mock_getpass.return_value = "testpassword" with HTTMock(self.login_unauthorized_mock): client = CleverSwarmClient("http://localhost:8000/api/v0/", username="testuser") with pytest.raises(RequestErrorException, match="Failed process request, error from server"): client.update_token() @patch('cleverswarm_python_client.libs.cleverswarm_client.getpass') def test_update_token_invalid_credentials(self, mock_getpass): """Test token update with invalid credentials using httmock.""" mock_getpass.return_value = "testpassword" with HTTMock(self.login_invalid_credentials_mock): client = CleverSwarmClient("http://localhost:8000/api/v0/", username="testuser") with pytest.raises(InvalidUsernameOrPasswordException): client.update_token() # Job Management Tests @urlmatch(netloc='localhost:8000', path='/api/v0/jobs', method='GET') def jobs_list_success_mock(self, url, request): """Mock successful jobs list response.""" return { 'status_code': 200, 'content': json.dumps([ {"id": "job1", "type": "Benchmark", "status": "Completed"}, {"id": "job2", "type": "UnstructuredWithOntology", "status": "Processing"} ]).encode('utf-8'), 'headers': {'content-type': 'application/json'} } def test_get_jobs_list_success(self): """Test successful job list retrieval using httmock.""" with HTTMock(self.jobs_list_success_mock): result = self.client.get_jobs_list(is_benchmark=True) assert len(result) == 1 # Only Benchmark jobs are returned assert result[0]["id"] == "job1" assert result[0]["type"] == "Benchmark" def test_get_jobs_list_filtering(self): """Test job list retrieval with filtering using httmock.""" with HTTMock(self.jobs_list_success_mock): # Test benchmark jobs only result = self.client.get_jobs_list(is_benchmark=True) assert len(result) == 1 assert result[0]["type"] == "Benchmark" # Test non-benchmark jobs only result = self.client.get_jobs_list(is_benchmark=False) assert len(result) == 1 assert result[0]["type"] == "UnstructuredWithOntology" @urlmatch(netloc='localhost:8000', path=r'/api/v0/jobs/.*', method='GET') def job_status_success_mock(self, url, request): """Mock successful job status response.""" return { 'status_code': 200, 'content': json.dumps("Completed").encode('utf-8'), 'headers': {'content-type': 'application/json'} } @urlmatch(netloc='localhost:8000', path=r'/api/v0/jobs/.*', method='GET') def job_status_not_found_mock(self, url, request): """Mock job not found response.""" return { 'status_code': 404, 'content': b'Job not found', 'headers': {'content-type': 'text/plain'} } def test_get_job_status_success(self): """Test successful job status retrieval using httmock.""" with HTTMock(self.job_status_success_mock): result = self.client.get_job_status("job123") assert result == JobStatus.Completed def test_get_job_status_not_found(self): """Test job status retrieval for non-existent job using httmock.""" with HTTMock(self.job_status_not_found_mock): with pytest.raises(JobNotFoundException): self.client.get_job_status("nonexistent") @urlmatch(netloc='localhost:8000', path=r'/api/v0/jobs/.*', method='GET') def job_details_success_mock(self, url, request): """Mock successful job details response.""" return { 'status_code': 200, 'content': json.dumps({ "id": "job123", "status": "Completed", "type": "Benchmark", "created": "2023-01-01T00:00:00Z" }).encode('utf-8'), 'headers': {'content-type': 'application/json'} } def test_get_job_details_success(self): """Test successful job details retrieval using httmock.""" with HTTMock(self.job_details_success_mock): result = self.client.get_job_details("job123") assert result["id"] == "job123" assert result["status"] == "Completed" @urlmatch(netloc='localhost:8000', path=r'/api/v0/jobs/.*', method='DELETE') def delete_job_success_mock(self, url, request): """Mock successful job deletion response.""" return { 'status_code': 200, 'content': b'Job deleted', 'headers': {'content-type': 'text/plain'} } @urlmatch(netloc='localhost:8000', path=r'/api/v0/jobs/.*', method='DELETE') def delete_job_not_found_mock(self, url, request): """Mock job not found for deletion response.""" return { 'status_code': 404, 'content': b'Job not found', 'headers': {'content-type': 'text/plain'} } def test_delete_job_success(self): """Test successful job deletion using httmock.""" with HTTMock(self.delete_job_success_mock): result = self.client.delete_job("job123") assert result is True def test_delete_job_not_found(self): """Test job deletion for non-existent job using httmock.""" with HTTMock(self.delete_job_not_found_mock): with pytest.raises(JobNotFoundException): self.client.delete_job("nonexistent") @urlmatch(netloc='localhost:8000', path=r'/api/v0/jobs/.*', method='PUT') def retry_job_success_mock(self, url, request): """Mock successful job retry response.""" return { 'status_code': 200, 'content': json.dumps("Processing").encode('utf-8'), 'headers': {'content-type': 'application/json'} } def test_retry_job_success(self): """Test successful job retry using httmock.""" with HTTMock(self.retry_job_success_mock): result = self.client.retry_job("job123") assert result == JobStatus.Processing # File Upload Tests @urlmatch(netloc='localhost:8000', path='/api/v0/benchmark', method='POST') def benchmark_job_success_mock(self, url, request): """Mock successful benchmark job creation response.""" return { 'status_code': 200, 'content': json.dumps({"job_id": "benchmark_job_123"}).encode('utf-8'), 'headers': {'content-type': 'application/json'} } def test_create_benchmark_job_success(self): """Test successful benchmark job creation using httmock.""" with tempfile.TemporaryDirectory() as temp_dir: # Create valid test files text_file = Path(temp_dir) / "test.txt" json_file = Path(temp_dir) / "test.json" jsonl_file = Path(temp_dir) / "test.jsonl" text_file.write_text("Test content") json_file.write_text('{"test": "data"}') jsonl_file.write_text('{"test": "data"}') with HTTMock(self.benchmark_job_success_mock): with patch.object(self.client, 'update_token'): # Mock update_token to avoid input() result = self.client.create_benchmark_job( text_file, json_file, jsonl_file, True ) assert result == "benchmark_job_123" @urlmatch(netloc='localhost:8000', path='/api/v0/unstructured/with_ontology', method='POST') def unstructured_job_success_mock(self, url, request): """Mock successful unstructured to KG job creation response.""" return { 'status_code': 201, 'content': json.dumps({"job_id": "unstructured_job_123"}).encode('utf-8'), 'headers': {'content-type': 'application/json'} } def test_create_unstructured_to_kg_job_success(self): """Test successful unstructured to KG job creation using httmock.""" with tempfile.TemporaryDirectory() as temp_dir: # Create valid test files text_file = Path(temp_dir) / "test.txt" json_file = Path(temp_dir) / "test.json" owl_file = Path(temp_dir) / "test.owl" text_file.write_text("Test content") json_file.write_text('{"test": "data"}') owl_file.write_text('test') with HTTMock(self.unstructured_job_success_mock): with patch.object(self.client, 'update_token'): # Mock update_token to avoid input() result = self.client.create_unstructured_to_kg_job( text_file, json_file, owl_file ) assert result == "unstructured_job_123" @urlmatch(netloc='localhost:8000', path='/api/v0/unstructured/with_wildcards', method='POST') def unstructured_wildcards_job_success_mock(self, url, request): """Mock successful unstructured to KG wildcards job creation response.""" return { 'status_code': 201, 'content': json.dumps({"job_id": "wildcards_job_123"}).encode('utf-8'), 'headers': {'content-type': 'application/json'} } def test_create_unstructured_to_kg_wildcards_job_success(self): """Test successful unstructured to KG wildcards job creation using httmock.""" with tempfile.TemporaryDirectory() as temp_dir: # Create valid test files text_file = Path(temp_dir) / "test.txt" json_file = Path(temp_dir) / "test.json" owl_file = Path(temp_dir) / "test.owl" wildcards_file = Path(temp_dir) / "test.wildcards" text_file.write_text("Test content") json_file.write_text('{"test": "data"}') owl_file.write_text('test') wildcards_file.write_text("wildcard1\nwildcard2") with HTTMock(self.unstructured_wildcards_job_success_mock): with patch.object(self.client, 'update_token'): # Mock update_token to avoid input() result = self.client.create_unstructured_to_kg_wildcards_job( text_file, json_file, owl_file, wildcards_file ) assert result == "wildcards_job_123" # File Download Tests @urlmatch(netloc='localhost:8000', path=r'/api/v0/benchmark/.*', method='GET') def benchmark_file_download_mock(self, url, request): """Mock successful benchmark file download response.""" return { 'status_code': 200, 'content': b'{"test": "benchmark_data"}', 'headers': {'content-type': 'application/json'} } def test_retrieve_benchmark_files_success(self): """Test successful benchmark files retrieval using httmock.""" with tempfile.TemporaryDirectory() as temp_dir: output_file = Path(temp_dir) / "output.jsonl" with HTTMock(self.benchmark_file_download_mock): with patch.object(self.client, 'update_token'): # Mock update_token to avoid input() result = self.client.retrieve_benchmark_files("job123", [output_file]) assert result == [output_file] @urlmatch(netloc='localhost:8000', path=r'/api/v0/unstructured/.*', method='GET') def unstructured_file_download_mock(self, url, request): """Mock successful unstructured file download response.""" return { 'status_code': 200, 'content': b'{"test": "unstructured_data"}', 'headers': {'content-type': 'application/json'} } def test_retrieve_unstructured_to_kg_files_success(self): """Test successful unstructured to KG files retrieval using httmock.""" with tempfile.TemporaryDirectory() as temp_dir: output_file = Path(temp_dir) / "output.jsonl" with HTTMock(self.unstructured_file_download_mock): with patch.object(self.client, 'update_token'): # Mock update_token to avoid input() result = self.client.retrieve_unstructured_to_kg_files("job123", output_file) assert result == output_file # Error Handling Tests @urlmatch(netloc='localhost:8000', path='/api/v0/test', method='GET') def generic_http_success_mock(self, url, request): """Mock successful generic HTTP response.""" return { 'status_code': 200, 'content': json.dumps({"result": "success"}).encode('utf-8'), 'headers': {'content-type': 'application/json'} } @urlmatch(netloc='localhost:8000', path='/api/v0/test', method='GET') def generic_http_error_mock(self, url, request): """Mock error generic HTTP response.""" return { 'status_code': 500, 'content': b'Internal Server Error', 'headers': {'content-type': 'text/plain'} } def test_generic_http_request_executor_success(self): """Test generic HTTP request executor with successful response using httmock.""" with HTTMock(self.generic_http_success_mock): def test_func(): import requests return requests.get("http://localhost:8000/api/v0/test") result = self.client._generic_http_request_executor(test_func) assert result.status_code == 200 # Connection Error Tests def test_generic_http_request_executor_connection_error(self): """Test generic HTTP request executor with connection error.""" def test_func(): import requests raise requests.exceptions.ConnectionError("Connection failed") with pytest.raises(ConnectionErrorException): self.client._generic_http_request_executor(test_func) def test_generic_http_request_executor_read_timeout_error(self): """Test generic HTTP request executor with read timeout error.""" def test_func(): import requests raise requests.exceptions.ReadTimeout("Read timeout") with pytest.raises(ConnectionTimeoutException): self.client._generic_http_request_executor(test_func) def test_generic_http_request_executor_http_error(self): """Test generic HTTP request executor with HTTP error.""" def test_func(): import requests raise requests.exceptions.HTTPError("HTTP error") with pytest.raises(RequestErrorException): self.client._generic_http_request_executor(test_func) def test_generic_http_request_executor_request_error(self): """Test generic HTTP request executor with request error.""" def test_func(): import requests raise requests.exceptions.RequestException("Request error") with pytest.raises(UnexpectedConditionException, match="Client error while processing authorization"): self.client._generic_http_request_executor(test_func) def test_generic_http_request_executor_unexpected_error(self): """Test generic HTTP request executor with unexpected error.""" def test_func(): raise ValueError("Unexpected error") with pytest.raises(UnexpectedConditionException): self.client._generic_http_request_executor(test_func)