198 lines
6.5 KiB
Python
198 lines
6.5 KiB
Python
# 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"}
|