diff --git a/src/fabric_cli/commands/fs/fab_fs_exists.py b/src/fabric_cli/commands/fs/fab_fs_exists.py index 2e034b01..2feb8e95 100644 --- a/src/fabric_cli/commands/fs/fab_fs_exists.py +++ b/src/fabric_cli/commands/fs/fab_fs_exists.py @@ -34,7 +34,7 @@ def exec_command(args: Namespace, context: FabricElement) -> None: # Workspaces and Items def _check_if_element_exists(element: FabricElement, args: Namespace) -> None: - text_message = fab_constant.INFO_EXISTS_TRUE if element.id else fab_constant.INFO_EXISTS_FALSE + text_message = True if element.id else False utils_ui.print_output_format(args, message=text_message) @@ -49,10 +49,9 @@ def _check_if_onelake_file_or_directory_exists( args.directory = f"{workspace_id}/?recursive=false&resource=filesystem&directory={item_id}/{local_path}&getShortcutMetadata=true" try: onelake_api.list_tables_files_recursive(args) - utils_ui.print_output_format(args, message=fab_constant.INFO_EXISTS_TRUE) + utils_ui.print_output_format(args, message=True) except FabricCLIError as e: if e.status_code == fab_constant.ERROR_NOT_FOUND: - utils_ui.print_output_format(args, message=fab_constant.INFO_EXISTS_FALSE - ) + utils_ui.print_output_format(args, message=False) else: raise e diff --git a/src/fabric_cli/core/fab_constant.py b/src/fabric_cli/core/fab_constant.py index 2e21356b..a08add17 100644 --- a/src/fabric_cli/core/fab_constant.py +++ b/src/fabric_cli/core/fab_constant.py @@ -196,8 +196,6 @@ COMMAND_VERSION_DESCRIPTION = "Show version information." # Info -INFO_EXISTS_TRUE = "true" -INFO_EXISTS_FALSE = "false" INFO_FEATURE_NOT_SUPPORTED = "Feature is not supported" # Warnings diff --git a/src/fabric_cli/core/fab_output.py b/src/fabric_cli/core/fab_output.py index a44774b0..24929e5d 100644 --- a/src/fabric_cli/core/fab_output.py +++ b/src/fabric_cli/core/fab_output.py @@ -21,7 +21,7 @@ def __init__( self, data: Optional[Any], hidden_data: Optional[List[Any]], - message: Optional[str], + message: Optional[str | bool], error_code: Optional[str] = None, ): self._data = data if isinstance(data, list) else ([data] if data else None) @@ -40,7 +40,7 @@ def hidden_data(self) -> Optional[List[str]]: return self._hidden_data @property - def message(self) -> Optional[str]: + def message(self) -> Optional[str | bool]: return self._message def to_dict(self) -> Dict[str, Any]: @@ -73,7 +73,7 @@ def __init__( output_format_type=None, show_headers=False, status: OutputStatus = OutputStatus.Success, - message: Optional[str] = None, + message: Optional[str | bool] = None, error_code: Optional[str] = None, data: Optional[Any] = None, hidden_data: Optional[Any] = None, diff --git a/src/fabric_cli/utils/fab_ui.py b/src/fabric_cli/utils/fab_ui.py index 708f7507..de9312c9 100644 --- a/src/fabric_cli/utils/fab_ui.py +++ b/src/fabric_cli/utils/fab_ui.py @@ -90,7 +90,7 @@ def print_version(args=None): def print_output_format( args: Namespace, - message: Optional[str] = None, + message: Optional[str | bool] = None, data: Optional[Any] = None, hidden_data: Optional[Any] = None, show_headers: bool = False, @@ -328,6 +328,21 @@ def _safe_print_formatted_text( except (RuntimeError, AttributeError, Exception) as e: _print_fallback(escaped_text, e, to_stderr) +def _format_message_for_text(message: str | bool) -> str: + """Format a message value for text output. + + Converts boolean values to lowercase string representation. + + Args: + message: The message to format (string or boolean) + + Returns: + String representation of the message + """ + if isinstance(message, bool): + return str(message).lower() + return message + def _print_output_format_result_text(output: FabricCLIOutput) -> None: # if there is no result to print it means something went wrong @@ -369,8 +384,9 @@ def _print_output_format_result_text(output: FabricCLIOutput) -> None: _print_raw_data(output_result.hidden_data) - if output_result.message: - print_done(f"{output_result.message}\n") + if output_result.message is not None: + message_str = _format_message_for_text(output_result.message) + print_done(f"{message_str}\n") def _print_raw_data(data: list[Any], to_stderr: bool = False) -> None: """ diff --git a/tests/test_commands/conftest.py b/tests/test_commands/conftest.py index 112924fd..1ab2ae64 100644 --- a/tests/test_commands/conftest.py +++ b/tests/test_commands/conftest.py @@ -552,9 +552,9 @@ def delete_cassette_if_record_mode_all(vcr_instance, cassette_name): # region mock fixtures -@pytest.fixture(autouse=True) -def setup_default_format(mock_fab_set_state_config): - mock_fab_set_state_config(fab_constant.FAB_OUTPUT_FORMAT, "text") +@pytest.fixture(autouse=True, scope="class") +def setup_default_format(): + state_config.set_config(fab_constant.FAB_OUTPUT_FORMAT, "text") @pytest.fixture(autouse=True) diff --git a/tests/test_commands/test_exists.py b/tests/test_commands/test_exists.py index 98747fc5..13ea55b0 100644 --- a/tests/test_commands/test_exists.py +++ b/tests/test_commands/test_exists.py @@ -16,7 +16,7 @@ def test_exists_workspace_exists_success( # Assert mock_print_done.assert_called_once() - assert constant.INFO_EXISTS_TRUE in mock_print_done.call_args[0][0] + assert "true" in mock_print_done.call_args[0][0] def test_exists_item_exists_success( self, item_factory, mock_print_done, cli_executor @@ -32,7 +32,7 @@ def test_exists_item_exists_success( # Assert mock_print_done.assert_called_once() - assert constant.INFO_EXISTS_TRUE in mock_print_done.call_args[0][0] + assert "true" in mock_print_done.call_args[0][0] def test_exists_virtual_workspace_item_capacity_exists_success( self, mock_print_done, cli_executor, test_data: StaticTestData @@ -44,7 +44,7 @@ def test_exists_virtual_workspace_item_capacity_exists_success( # Assert mock_print_done.assert_called_once() - assert constant.INFO_EXISTS_TRUE in mock_print_done.call_args[0][0] + assert "true" in mock_print_done.call_args[0][0] def test_exists_onelake_exists_success( self, item_factory, mock_print_done, cli_executor @@ -60,7 +60,7 @@ def test_exists_onelake_exists_success( # Assert mock_print_done.assert_called_once() - assert constant.INFO_EXISTS_TRUE in mock_print_done.call_args[0][0] + assert "true" in mock_print_done.call_args[0][0] def test_exists_item_doesnt_exist_success( self, item_factory, mock_print_done, cli_executor @@ -77,7 +77,7 @@ def test_exists_item_doesnt_exist_success( # Assert mock_print_done.assert_called_once() - assert constant.INFO_EXISTS_FALSE in mock_print_done.call_args[0][0] + assert "false" in mock_print_done.call_args[0][0] def test_exists_onelake_doesnt_exist_success( self, item_factory, mock_print_done, cli_executor @@ -95,7 +95,7 @@ def test_exists_onelake_doesnt_exist_success( # Assert mock_print_done.assert_called_once() - assert constant.INFO_EXISTS_FALSE in mock_print_done.call_args[0][0] + assert "false" in mock_print_done.call_args[0][0] def test_exists_folder_exists_success( self, folder_factory, mock_print_done, cli_executor @@ -111,7 +111,7 @@ def test_exists_folder_exists_success( # Assert mock_print_done.assert_called_once() - assert constant.INFO_EXISTS_TRUE in mock_print_done.call_args[0][0] + assert "true" in mock_print_done.call_args[0][0] def test_exists_subfolder_exists_success( self, folder_factory, mock_print_done, cli_executor @@ -128,7 +128,7 @@ def test_exists_subfolder_exists_success( # Assert mock_print_done.assert_called_once() - assert constant.INFO_EXISTS_TRUE in mock_print_done.call_args[0][0] + assert "true" in mock_print_done.call_args[0][0] def test_exists_folder_doesnt_exist_success( self, workspace, mock_print_done, cli_executor @@ -143,6 +143,6 @@ def test_exists_folder_doesnt_exist_success( # Assert mock_print_done.assert_called_once() - assert constant.INFO_EXISTS_FALSE in mock_print_done.call_args[0][0] + assert "false" in mock_print_done.call_args[0][0] # endregion diff --git a/tests/test_utils/test_fab_ui.py b/tests/test_utils/test_fab_ui.py index 2c35648e..80faf5ff 100644 --- a/tests/test_utils/test_fab_ui.py +++ b/tests/test_utils/test_fab_ui.py @@ -781,3 +781,45 @@ def test_print_version_seccess(): ui.print_version() ui.print_version(None) # Just verify it doesn't crash - output verification would require mocking + + +def test_print_output_format_boolean_message_json_success( + mock_fab_set_state_config, capsys +): + """Test that boolean message values are properly serialized as JSON booleans, not strings.""" + mock_fab_set_state_config(constant.FAB_OUTPUT_FORMAT, "json") + args = Namespace(command="exists", output_format="json") + + ui.print_output_format(args, message=True) + captured = capsys.readouterr() + output_true = json.loads(captured.out) + + assert "result" in output_true + assert "message" in output_true["result"] + assert output_true["result"]["message"] is True + assert isinstance(output_true["result"]["message"], bool) + + ui.print_output_format(args, message=False) + captured = capsys.readouterr() + output_false = json.loads(captured.out) + + assert "result" in output_false + assert "message" in output_false["result"] + assert output_false["result"]["message"] is False + assert isinstance(output_false["result"]["message"], bool) + + +def test_print_output_format_boolean_message_text_success(capsys): + """Test that boolean message values are properly converted to strings for text output.""" + args = Namespace(command="exists", output_format="text") + + ui.print_output_format(args, message=True) + captured = capsys.readouterr() + + assert "true" in captured.out.lower() + + ui.print_output_format(args, message=False) + captured = capsys.readouterr() + + assert "false" in captured.out.lower() +