@@ -47,6 +47,21 @@ def test_init(self, mock_http_client: AsyncMock, client: OCSClient) -> None:
4747 assert client .client is mock_http_client
4848 assert client .base_url == "https://nextcloud.example.com"
4949 assert client .token == "test-token"
50+ assert client .timeout is None
51+
52+ def test_init_with_custom_timeout (self , mock_http_client : AsyncMock ) -> None :
53+ """Test OCSClient initialization with custom timeout."""
54+ client = OCSClient (
55+ http_client = mock_http_client ,
56+ base_url = "https://nextcloud.example.com" ,
57+ token = "test-token" ,
58+ timeout = 10.0 ,
59+ )
60+ assert client .timeout == 10.0
61+
62+ def test_init_timeout_is_none_by_default (self , client : OCSClient ) -> None :
63+ """Test that timeout defaults to None."""
64+ assert client .timeout is None
5065
5166 def test_init_strips_trailing_slash (self , mock_http_client : AsyncMock ) -> None :
5267 """Test that trailing slash is stripped from base_url."""
@@ -183,6 +198,39 @@ async def test_search_files_no_results(self, client: OCSClient, mock_http_client
183198
184199 assert result == []
185200
201+ async def test_default_timeout_uses_client_default (self , client : OCSClient , mock_http_client : AsyncMock ) -> None :
202+ """Test that default timeout (None) does not pass timeout kwarg, preserving client default."""
203+ mock_response = create_mock_response (status_code = 200 , json_data = {"ocs" : {"data" : {"entries" : []}}})
204+ mock_http_client .get .return_value = mock_response
205+
206+ await client .search_files (term = "test" )
207+
208+ call_kwargs = mock_http_client .get .call_args [1 ]
209+ assert "timeout" not in call_kwargs
210+
211+ async def test_custom_timeout_passes_value (self , mock_http_client : AsyncMock ) -> None :
212+ """Test that custom timeout passes timeout value to client.get()."""
213+ client = OCSClient (
214+ http_client = mock_http_client ,
215+ base_url = "https://nextcloud.example.com" ,
216+ token = "test-token" ,
217+ timeout = 10.0 ,
218+ )
219+ mock_response = create_mock_response (status_code = 200 , json_data = {"ocs" : {"data" : {"entries" : []}}})
220+ mock_http_client .get .return_value = mock_response
221+
222+ await client .search_files (term = "test" )
223+
224+ call_kwargs = mock_http_client .get .call_args [1 ]
225+ assert call_kwargs ["timeout" ] == 10.0
226+
227+ async def test_timeout_exception_is_reraised (self , client : OCSClient , mock_http_client : AsyncMock ) -> None :
228+ """Test that httpx.TimeoutException is re-raised, not wrapped as ExternalServiceError."""
229+ mock_http_client .get .side_effect = httpx .ReadTimeout ("read timed out" )
230+
231+ with pytest .raises (httpx .TimeoutException ):
232+ await client .search_files (term = "test" )
233+
186234 async def test_get_file_activities_single_file (self , client : OCSClient , mock_http_client : AsyncMock ) -> None :
187235 """Test file activities with a single file per activity."""
188236 mock_response = create_mock_response (
0 commit comments