# This file is part of sbs # # Copyright (C) 2025 Steve Milner # # SPDX-License-Identifier: GPL-3.0-or-later # Generated by Gemini 2.5 Flash import pytest import argparse import typing from unittest.mock import MagicMock, patch, mock_open from sbs.cli import _TextOrFileAction, load_map @pytest.fixture def text_or_file_action_instance(): """ Provides an instance of _TextOrFileAction with a predefined 'dest'. """ # Initialize with a dummy option_strings and dest for testing purposes return _TextOrFileAction( option_strings=["-t", "--text-or-file"], dest="content_param" ) @pytest.fixture def mock_parser(): """ Provides a mock argparse.ArgumentParser. """ return MagicMock(spec=argparse.ArgumentParser) @pytest.fixture def mock_namespace(): """ Provides a mock argparse.Namespace to simulate argument storage. """ return MagicMock(spec=argparse.Namespace) def test_direct_string_input(text_or_file_action_instance, mock_parser, mock_namespace): """ Tests that a direct string value is correctly assigned to the namespace. """ value = "This is a direct string input." option_string = "--text-or-file" text_or_file_action_instance(mock_parser, mock_namespace, value, option_string) # Assert that the namespace attribute 'content_param' was set to the direct string value assert mock_namespace.content_param == value # Ensure no file operations were attempted with pytest.raises(AttributeError): mock_open.assert_not_called() def test_file_input_without_trailing_newline( text_or_file_action_instance, mock_parser, mock_namespace ): """ Tests that content from a file without a trailing newline is correctly read and assigned. """ file_content = "Content from a test file." value = "@path/to/test_file.txt" option_string = "--text-or-file" # Use mock_open to simulate reading from a file with patch("builtins.open", mock_open(read_data=file_content)) as mock_file: text_or_file_action_instance(mock_parser, mock_namespace, value, option_string) # Assert that 'open' was called with the correct path (without '@') mock_file.assert_called_once_with("path/to/test_file.txt") # Assert that the namespace attribute was set with the file's content assert mock_namespace.content_param == file_content def test_file_input_with_trailing_newline( text_or_file_action_instance, mock_parser, mock_namespace ): """ Tests that content from a file with a trailing newline is correctly read and the newline is stripped. """ file_content_with_newline = "Content with a newline.\n" expected_content = "Content with a newline." value = "@path/to/test_file_with_newline.txt" option_string = "--text-or-file" with patch( "builtins.open", mock_open(read_data=file_content_with_newline) ) as mock_file: text_or_file_action_instance(mock_parser, mock_namespace, value, option_string) mock_file.assert_called_once_with("path/to/test_file_with_newline.txt") # Assert that the newline was stripped from the content assert mock_namespace.content_param == expected_content def test_file_input_empty_file( text_or_file_action_instance, mock_parser, mock_namespace ): """ Tests handling of an empty file. """ file_content = "" value = "@path/to/empty_file.txt" option_string = "--text-or-file" with patch("builtins.open", mock_open(read_data=file_content)) as mock_file: text_or_file_action_instance(mock_parser, mock_namespace, value, option_string) mock_file.assert_called_once_with("path/to/empty_file.txt") assert mock_namespace.content_param == "" def test_file_not_found_error( text_or_file_action_instance, mock_parser, mock_namespace ): """ Tests that a FileNotFoundError is propagated when the file specified by '@' does not exist. """ value = "@non_existent_file.txt" option_string = "--text-or-file" # Patch open to raise FileNotFoundError with patch("builtins.open", side_effect=FileNotFoundError): with pytest.raises(FileNotFoundError): text_or_file_action_instance( mock_parser, mock_namespace, value, option_string ) # Ensure no attribute was set on the namespace with pytest.raises(AttributeError): mock_namespace.content_param def test_load_map_single_entry(): """ Test that load_map correctly parses a single username=name entry. """ file_content = "user1=Alice Smith\n" # Use patch.object to mock builtins.open and simulate file reading with patch("builtins.open", mock_open(read_data=file_content)) as mock_file: result = load_map("dummy_map.txt") # Assert that open was called with the correct filename and mode mock_file.assert_called_once_with("dummy_map.txt", "r") # Assert the mapping is correct assert result == {"user1": "Alice Smith"} def test_load_map_multiple_entries(): """ Test that load_map correctly parses multiple username=name entries. """ file_content = "user1=Alice Smith\n" "user2=Bob Johnson\n" "admin=Charlie Brown\n" with patch("builtins.open", mock_open(read_data=file_content)) as mock_file: result = load_map("multi_entry_map.txt") mock_file.assert_called_once_with("multi_entry_map.txt", "r") assert result == { "user1": "Alice Smith", "user2": "Bob Johnson", "admin": "Charlie Brown", } def test_load_map_empty_file(): """ Test that load_map returns an empty dictionary for an empty file. """ file_content = "" with patch("builtins.open", mock_open(read_data=file_content)) as mock_file: result = load_map("empty_map.txt") mock_file.assert_called_once_with("empty_map.txt", "r") assert result == {} def test_load_map_file_not_found(): """ Test that load_map raises FileNotFoundError when the file does not exist. """ # Patch open to raise FileNotFoundError with patch("builtins.open", side_effect=FileNotFoundError): with pytest.raises(FileNotFoundError): load_map("non_existent_map.txt") def test_load_map_line_with_multiple_equals(): """ Test that load_map handles lines with multiple '=' signs correctly (splits only on first). """ file_content = "user_extra=First Last=ExtraInfo\n" with patch("builtins.open", mock_open(read_data=file_content)): result = load_map("multiple_equals_map.txt") assert result == {"user_extra": "First Last=ExtraInfo"}