120 lines
4.2 KiB
Python
120 lines
4.2 KiB
Python
import logging
|
|
from unittest.mock import mock_open, patch
|
|
|
|
import pytest
|
|
|
|
from control_backend.logging.setup_logging import add_logging_level, setup_logging
|
|
|
|
|
|
def test_add_logging_level():
|
|
# Add a unique level to avoid conflicts with other tests/libraries
|
|
level_name = "TESTLEVEL"
|
|
level_num = 35
|
|
|
|
add_logging_level(level_name, level_num)
|
|
|
|
assert logging.getLevelName(level_num) == level_name
|
|
assert hasattr(logging, level_name)
|
|
assert hasattr(logging.getLoggerClass(), level_name.lower())
|
|
|
|
# Test functionality
|
|
logger = logging.getLogger("test_custom_level")
|
|
with patch.object(logger, "_log") as mock_log:
|
|
getattr(logger, level_name.lower())("message")
|
|
mock_log.assert_called_with(level_num, "message", ())
|
|
|
|
# Test duplicates
|
|
with pytest.raises(AttributeError):
|
|
add_logging_level(level_name, level_num)
|
|
|
|
with pytest.raises(AttributeError):
|
|
add_logging_level("INFO", 20) # Existing level
|
|
|
|
|
|
def test_setup_logging_no_file(caplog):
|
|
with patch("os.path.exists", return_value=False):
|
|
setup_logging("dummy.yaml")
|
|
assert "Logging config file not found" in caplog.text
|
|
|
|
|
|
def test_setup_logging_yaml_error(caplog):
|
|
with patch("os.path.exists", return_value=True):
|
|
with patch("builtins.open", mock_open(read_data="invalid: [yaml")):
|
|
with patch("logging.config.dictConfig") as mock_dict_config:
|
|
setup_logging("config.yaml")
|
|
|
|
# Verify we logged the warning
|
|
assert "Could not load logging configuration" in caplog.text
|
|
# Verify dictConfig was called with empty dict (which would crash real dictConfig)
|
|
mock_dict_config.assert_called_with({})
|
|
assert "Could not load logging configuration" in caplog.text
|
|
|
|
|
|
def test_setup_logging_success():
|
|
config_data = """
|
|
version: 1
|
|
handlers:
|
|
console:
|
|
class: logging.StreamHandler
|
|
root:
|
|
handlers: [console]
|
|
level: INFO
|
|
custom_levels:
|
|
MYLEVEL: 15
|
|
"""
|
|
with patch("os.path.exists", return_value=True):
|
|
with patch("builtins.open", mock_open(read_data=config_data)):
|
|
with patch("logging.config.dictConfig") as mock_dict_config:
|
|
setup_logging("config.yaml")
|
|
mock_dict_config.assert_called()
|
|
assert hasattr(logging, "MYLEVEL")
|
|
|
|
|
|
def test_setup_logging_zmq_handler(mock_zmq_context):
|
|
config_data = """
|
|
version: 1
|
|
handlers:
|
|
ui:
|
|
class: logging.NullHandler
|
|
# In real config this would be a zmq handler, but for unit test logic
|
|
# we just want to see if the socket injection happens
|
|
"""
|
|
with patch("os.path.exists", return_value=True):
|
|
with patch("builtins.open", mock_open(read_data=config_data)):
|
|
with patch("logging.config.dictConfig") as mock_dict_config:
|
|
setup_logging("config.yaml")
|
|
|
|
args = mock_dict_config.call_args[0][0]
|
|
assert "interface_or_socket" in args["handlers"]["ui"]
|
|
|
|
|
|
def test_add_logging_level_method_name_exists_in_logging():
|
|
# method_name explicitly set to an existing logging method → triggers first hasattr branch
|
|
with pytest.raises(AttributeError) as exc:
|
|
add_logging_level("NEWDUPLEVEL", 37, method_name="info")
|
|
assert "info already defined in logging module" in str(exc.value)
|
|
|
|
|
|
def test_add_logging_level_method_name_exists_in_logger_class():
|
|
# 'makeRecord' exists on Logger class but not on the logging module
|
|
with pytest.raises(AttributeError) as exc:
|
|
add_logging_level("ANOTHERLEVEL", 38, method_name="makeRecord")
|
|
assert "makeRecord already defined in logger class" in str(exc.value)
|
|
|
|
|
|
def test_add_logging_level_log_to_root_path_executes_without_error():
|
|
# Verify log_to_root is installed and callable — without asserting logging output
|
|
level_name = "ROOTTEST"
|
|
level_num = 36
|
|
|
|
add_logging_level(level_name, level_num)
|
|
|
|
# Simply call the injected root logger method
|
|
# The line is executed even if we don't validate output
|
|
root_logging_method = getattr(logging, level_name.lower(), None)
|
|
assert callable(root_logging_method)
|
|
|
|
# Execute the method to hit log_to_root in coverage.
|
|
# No need to verify log output.
|
|
root_logging_method("some message")
|