1- import inspect
21import json
32import os
3+ import threading
44from datetime import datetime , timedelta , timezone
55from pathlib import Path
66from types import SimpleNamespace
@@ -37,10 +37,6 @@ def test_is_newer_version() -> None:
3737 assert not is_newer_version ("not-a-version" , "0.17.1" )
3838
3939
40- def test_get_rich_toolkit_has_no_invocation_args_override () -> None :
41- assert "args" not in inspect .signature (get_rich_toolkit ).parameters
42-
43-
4440@respx .mock
4541def test_check_for_update_returns_update_when_pypi_has_newer_version () -> None :
4642 route = respx .get (PYPI_JSON_URL ).mock (
@@ -225,6 +221,14 @@ def test_get_upgrade_command_falls_back_when_installer_is_unknown() -> None:
225221 )
226222
227223
224+ def test_get_upgrade_command_propagates_detector_errors () -> None :
225+ def broken_detector (package_name : str ) -> None :
226+ raise RuntimeError (f"Could not inspect { package_name } " )
227+
228+ with pytest .raises (RuntimeError , match = "Could not inspect fastapi-cloud-cli" ):
229+ get_upgrade_command (detector = broken_detector )
230+
231+
228232def test_format_update_message () -> None :
229233 message = format_update_message (
230234 VersionUpdate (current = "0.17.1" , latest = "0.18.0" ),
@@ -245,44 +249,23 @@ def _seed_fresh_update_cache(latest_version: str = "999.0.0") -> None:
245249 )
246250
247251
248- def test_get_rich_toolkit_prints_forced_update_message_without_wrapper (
249- monkeypatch : pytest .MonkeyPatch ,
250- capsys : pytest .CaptureFixture [str ],
251- ) -> None :
252- monkeypatch .setenv ("FASTAPI_CLOUD_DISABLE_VERSION_CHECK" , "" )
253- _seed_fresh_update_cache ()
254-
255- with get_rich_toolkit () as toolkit :
256- toolkit .print ("command output" )
257-
258- output = capsys .readouterr ().out
259- assert "command output" in output
260- assert "update" in output
261- assert "A newer FastAPI Cloud CLI version is available" in output
262-
263-
264- def test_get_rich_toolkit_prints_forced_update_once_per_click_command () -> None :
252+ def test_get_rich_toolkit_prints_update_after_toolkit_output () -> None :
265253 @click .command ()
266- def command_with_multiple_toolkits () -> None :
267- with get_rich_toolkit () as toolkit :
268- toolkit .print ("first output" )
254+ def command_with_toolkit () -> None :
269255 with get_rich_toolkit () as toolkit :
270- toolkit .print ("second output" )
271- click .echo ("after command body" )
256+ toolkit .print ("command output" )
272257
273258 _seed_fresh_update_cache ()
274259 result = CliRunner ().invoke (
275- command_with_multiple_toolkits ,
260+ command_with_toolkit ,
276261 [],
277262 env = {"FASTAPI_CLOUD_DISABLE_VERSION_CHECK" : "" },
278263 )
279264
280265 assert result .exit_code == 0 , result .output
281- assert "first output" in result .output
282- assert "second output" in result .output
283- assert "after command body" in result .output
266+ assert "command output" in result .output
284267 assert result .output .count ("A newer FastAPI Cloud CLI version is available" ) == 1
285- assert result .output .rfind ("after command body " ) < result .output .rfind ("A newer" )
268+ assert result .output .rfind ("command output " ) < result .output .rfind ("A newer" )
286269
287270
288271def test_get_rich_toolkit_skips_update_when_disabled (
@@ -322,3 +305,27 @@ def test_background_check_returns_no_message_without_update() -> None:
322305 message = check .get_update_message ()
323306
324307 assert message is None
308+
309+
310+ def test_background_check_exposes_unexpected_errors () -> None :
311+ captured_errors : list [threading .ExceptHookArgs ] = []
312+ original_hook = threading .excepthook
313+
314+ def capture_thread_error (args : threading .ExceptHookArgs ) -> None :
315+ captured_errors .append (args )
316+
317+ def broken_check () -> None :
318+ raise RuntimeError ("version check failed unexpectedly" )
319+
320+ threading .excepthook = capture_thread_error
321+ try :
322+ check = BackgroundVersionCheck (check_for_update = broken_check , join_timeout = 1 )
323+ check .start ()
324+ assert check ._thread is not None
325+ check ._thread .join (timeout = 1 )
326+ finally :
327+ threading .excepthook = original_hook
328+
329+ assert captured_errors
330+ assert captured_errors [0 ].exc_type is RuntimeError
331+ assert str (captured_errors [0 ].exc_value ) == "version check failed unexpectedly"
0 commit comments