@@ -416,6 +416,38 @@ def test_run_info_renders_package_metadata_with_nested_runtime_plugins(
416416 assert mock_console .print .call_count >= 1
417417
418418
419+ @patch ("data_designer.cli.controllers.plugin_catalog_controller.print_warning" )
420+ @patch ("data_designer.cli.controllers.plugin_catalog_controller.console" )
421+ @patch ("data_designer.cli.controllers.plugin_catalog_controller.display_config_preview" )
422+ def test_run_info_still_renders_package_metadata_when_install_plan_cannot_be_built (
423+ mock_display_config_preview : MagicMock ,
424+ mock_console : MagicMock ,
425+ mock_print_warning : MagicMock ,
426+ controller : PluginCatalogController ,
427+ ) -> None :
428+ entry = _entry ()
429+ catalog = _catalog ()
430+ controller .catalog_service .get_catalog .return_value = catalog
431+ controller .catalog_service .get_package_entries .return_value = [entry ]
432+ controller .catalog_service .evaluate_compatibility .return_value = CompatibilityResult (True , [])
433+ controller .catalog_service .get_package_current_version .return_value = "0.2.0"
434+ controller .install_service .build_install_plan .side_effect = ValueError ("pip is unavailable" )
435+
436+ controller .run_info ("text-transform" , catalog_alias = "local" )
437+
438+ mock_console .print .assert_any_call (" Version: [bold]0.2.0[/bold]" )
439+ mock_console .print .assert_any_call (" Runtime plugins: [bold]text-transform (processor)[/bold]" )
440+ mock_console .print .assert_any_call (" Compatibility: [bold green]data-designer>=0.5.7 ✓[/bold green]" )
441+ mock_print_warning .assert_called_once_with ("pip is unavailable" )
442+ metadata = mock_display_config_preview .call_args .args [0 ]
443+ assert metadata ["package" ] == {
444+ "name" : "data-designer-text-transform" ,
445+ "description" : "Transform text records" ,
446+ "version" : "0.2.0" ,
447+ }
448+ mock_display_config_preview .assert_called_once ()
449+
450+
419451@pytest .mark .parametrize (
420452 ("install_mode" , "expected_strategy" ),
421453 [
@@ -874,9 +906,11 @@ def test_run_install_preserves_version_in_runtime_plugin_recovery_hint(
874906
875907
876908@patch ("data_designer.cli.controllers.plugin_catalog_controller.console" )
909+ @patch ("data_designer.cli.controllers.plugin_catalog_controller.print_error" )
877910@patch ("data_designer.cli.controllers.plugin_catalog_controller.print_warning" )
878911def test_run_install_dry_run_renders_incompatible_plan_and_block_message (
879912 mock_print_warning : MagicMock ,
913+ mock_print_error : MagicMock ,
880914 mock_console : MagicMock ,
881915 controller : PluginCatalogController ,
882916) -> None :
@@ -894,9 +928,16 @@ def test_run_install_dry_run_renders_incompatible_plan_and_block_message(
894928 controller .run_install ("data-designer-text-transform" , catalog_alias = "local" , dry_run = True )
895929
896930 assert exc_info .value .exit_code == 1
931+ controller .catalog_service .get_package_entries .assert_called_once_with (
932+ "data-designer-text-transform" ,
933+ "local" ,
934+ refresh = False ,
935+ include_incompatible = True ,
936+ )
897937 controller .install_service .build_install_plan .assert_called_once_with (entry , catalog , manager = "auto" )
898938 controller .install_service .install .assert_not_called ()
899939 controller .install_service .verify_entry_points .assert_not_called ()
940+ mock_print_error .assert_not_called ()
900941 mock_console .print .assert_any_call (" Install strategy: [bold]pip install[/bold]" )
901942 assert all (
902943 "Command:" not in str (call_args .args [0 ]) for call_args in mock_console .print .call_args_list if call_args .args
@@ -911,44 +952,6 @@ def test_run_install_dry_run_renders_incompatible_plan_and_block_message(
911952 )
912953
913954
914- @patch ("data_designer.cli.controllers.plugin_catalog_controller.console" )
915- @patch ("data_designer.cli.controllers.plugin_catalog_controller.print_error" )
916- @patch ("data_designer.cli.controllers.plugin_catalog_controller.print_warning" )
917- def test_run_install_dry_run_renders_incompatible_entry_for_inspection (
918- mock_print_warning : MagicMock ,
919- mock_print_error : MagicMock ,
920- mock_console : MagicMock ,
921- controller : PluginCatalogController ,
922- ) -> None :
923- entry = _entry (data_designer_requirement = "data-designer>=99.0" , data_designer_specifier = ">=99.0" )
924- catalog = _catalog ()
925- controller .catalog_service .get_catalog .return_value = catalog
926- controller .catalog_service .get_package_entries .return_value = [entry ]
927- controller .catalog_service .evaluate_compatibility .return_value = CompatibilityResult (
928- False ,
929- ["Data Designer 0.5.7 does not satisfy >=99.0" ],
930- )
931- controller .install_service .build_install_plan .return_value = _plan (catalog )
932-
933- with pytest .raises (typer .Exit ) as exc_info :
934- controller .run_install ("data-designer-text-transform" , catalog_alias = "local" , dry_run = True )
935-
936- assert exc_info .value .exit_code == 1
937- controller .catalog_service .get_package_entries .assert_called_once_with (
938- "data-designer-text-transform" ,
939- "local" ,
940- refresh = False ,
941- include_incompatible = True ,
942- )
943- controller .install_service .build_install_plan .assert_called_once_with (entry , catalog , manager = "auto" )
944- controller .install_service .install .assert_not_called ()
945- mock_print_error .assert_not_called ()
946- mock_print_warning .assert_called_once_with (
947- "Dry run complete; no changes made. Install would be blocked because compatibility checks failed."
948- )
949- assert mock_console .print .call_count >= 1
950-
951-
952955@patch ("data_designer.cli.controllers.plugin_catalog_controller.console" )
953956@patch ("data_designer.cli.controllers.plugin_catalog_controller.print_warning" )
954957def test_run_install_warns_when_install_plan_has_source_warning (
@@ -1025,6 +1028,32 @@ def test_run_install_warns_when_verification_misses_entry_point(
10251028 assert mock_console .print .call_count >= 1
10261029
10271030
1031+ @patch ("data_designer.cli.controllers.plugin_catalog_controller.console" )
1032+ @patch ("data_designer.cli.controllers.plugin_catalog_controller.print_error" )
1033+ def test_run_install_wraps_package_manager_failure (
1034+ mock_print_error : MagicMock ,
1035+ mock_console : MagicMock ,
1036+ controller : PluginCatalogController ,
1037+ ) -> None :
1038+ entry = _entry ()
1039+ catalog = _catalog ()
1040+ plan = _plan (catalog )
1041+ controller .catalog_service .get_catalog .return_value = catalog
1042+ controller .catalog_service .get_package_entries .return_value = [entry ]
1043+ controller .catalog_service .evaluate_compatibility .return_value = CompatibilityResult (True , [])
1044+ controller .install_service .build_install_plan .return_value = plan
1045+ controller .install_service .install .side_effect = RuntimeError ("installer exited with status 2" )
1046+
1047+ with pytest .raises (typer .Exit ) as exc_info :
1048+ controller .run_install ("data-designer-text-transform" , catalog_alias = "local" , yes = True )
1049+
1050+ assert exc_info .value .exit_code == 1
1051+ controller .install_service .install .assert_called_once_with (plan )
1052+ controller .install_service .verify_entry_points .assert_not_called ()
1053+ mock_print_error .assert_called_once_with ("installer exited with status 2" )
1054+ assert mock_console .print .call_count >= 1
1055+
1056+
10281057@patch ("data_designer.cli.controllers.plugin_catalog_controller.console" )
10291058@patch ("data_designer.cli.controllers.plugin_catalog_controller.print_success" )
10301059def test_run_uninstall_dry_run_renders_plan_without_uninstalling (
@@ -1104,6 +1133,31 @@ def test_run_uninstall_wraps_plan_error(
11041133 mock_print_error .assert_called_once_with ("Failed to build plugin uninstall plan: uv was requested" )
11051134
11061135
1136+ @patch ("data_designer.cli.controllers.plugin_catalog_controller.console" )
1137+ @patch ("data_designer.cli.controllers.plugin_catalog_controller.print_error" )
1138+ def test_run_uninstall_wraps_package_manager_failure (
1139+ mock_print_error : MagicMock ,
1140+ mock_console : MagicMock ,
1141+ controller : PluginCatalogController ,
1142+ ) -> None :
1143+ entry = _entry ()
1144+ catalog = _catalog ()
1145+ plan = _uninstall_plan (catalog )
1146+ controller .catalog_service .get_catalog .return_value = catalog
1147+ controller .catalog_service .get_package_entries .return_value = [entry ]
1148+ controller .install_service .build_uninstall_plan .return_value = plan
1149+ controller .install_service .uninstall .side_effect = RuntimeError ("uninstaller exited with status 2" )
1150+
1151+ with pytest .raises (typer .Exit ) as exc_info :
1152+ controller .run_uninstall ("data-designer-text-transform" , catalog_alias = "local" , yes = True )
1153+
1154+ assert exc_info .value .exit_code == 1
1155+ controller .install_service .uninstall .assert_called_once_with (plan )
1156+ controller .install_service .verify_entry_points_removed .assert_not_called ()
1157+ mock_print_error .assert_called_once_with ("uninstaller exited with status 2" )
1158+ assert mock_console .print .call_count >= 1
1159+
1160+
11071161@patch ("data_designer.cli.controllers.plugin_catalog_controller.console" )
11081162@patch ("data_designer.cli.controllers.plugin_catalog_controller.print_success" )
11091163def test_run_uninstall_reports_success_when_entry_points_are_removed (
0 commit comments