Coverage for tests / unit / utils / console / test_logger_output_methods.py: 100%
42 statements
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Unit tests for ThreadSafeConsoleLogger console output and log file methods.
3Tests cover the console_output method with various color options and
4the save_console_log file creation functionality.
5"""
7from __future__ import annotations
9from pathlib import Path
10from unittest.mock import patch
12import pytest
13from assertpy import assert_that
15from lintro.utils.console.logger import ThreadSafeConsoleLogger
17# =============================================================================
18# Console Output Method Tests
19# =============================================================================
22def test_console_output_no_color(logger: ThreadSafeConsoleLogger) -> None:
23 """Verify console_output calls click.echo with plain text when no color specified.
25 Without a color argument, the text should be passed directly to click.echo
26 without any styling applied.
28 Args:
29 logger: ThreadSafeConsoleLogger instance fixture.
30 """
31 with patch("click.echo") as mock_echo:
32 logger.console_output("test message")
33 mock_echo.assert_called_once_with("test message")
36def test_console_output_with_color(logger: ThreadSafeConsoleLogger) -> None:
37 """Verify console_output applies color styling when color argument provided.
39 When a color is specified, click.style should be called to wrap the text
40 with the appropriate color, then click.echo displays the styled result.
42 Args:
43 logger: ThreadSafeConsoleLogger instance fixture.
44 """
45 with (
46 patch("click.echo") as mock_echo,
47 patch("click.style", return_value="styled text") as mock_style,
48 ):
49 logger.console_output("test message", color="red")
50 mock_style.assert_called_once_with("test message", fg="red")
51 mock_echo.assert_called_once_with("styled text")
54@pytest.mark.parametrize(
55 ("color", "expected_fg"),
56 [
57 pytest.param("red", "red", id="red"),
58 pytest.param("green", "green", id="green"),
59 pytest.param("yellow", "yellow", id="yellow"),
60 pytest.param("cyan", "cyan", id="cyan"),
61 pytest.param("blue", "blue", id="blue"),
62 pytest.param("magenta", "magenta", id="magenta"),
63 ],
64)
65def test_console_output_various_colors(
66 logger: ThreadSafeConsoleLogger,
67 color: str,
68 expected_fg: str,
69) -> None:
70 """Verify console_output correctly applies various color options.
72 Different color values should be passed through to click.style's fg parameter
73 without modification.
75 Args:
76 logger: ThreadSafeConsoleLogger instance fixture.
77 color: The color to apply to output.
78 expected_fg: The expected foreground color value.
79 """
80 with patch("click.echo"), patch("click.style") as mock_style:
81 logger.console_output("test", color=color)
82 mock_style.assert_called_once_with("test", fg=expected_fg)
85# =============================================================================
86# Console Log File Tests
87# =============================================================================
90def test_save_console_log_creates_file(tmp_path: Path) -> None:
91 """Verify save_console_log creates console.log file in run directory.
93 When a run_dir is configured, save_console_log should create a console.log
94 file marker in that directory.
96 Args:
97 tmp_path: Temporary directory path for test files.
98 """
99 logger = ThreadSafeConsoleLogger(run_dir=tmp_path)
100 logger.save_console_log()
101 log_file = tmp_path / "console.log"
102 assert_that(log_file.exists()).is_true()
105def test_save_console_log_no_run_dir_is_noop(logger: ThreadSafeConsoleLogger) -> None:
106 """Verify save_console_log does nothing when no run directory configured.
108 Without a run_dir, there's nowhere to save the log file, so the method
109 should complete without error and without side effects.
111 Args:
112 logger: ThreadSafeConsoleLogger instance fixture.
113 """
114 # Should not raise any exception
115 logger.save_console_log()
118def test_save_console_log_handles_os_error(tmp_path: Path) -> None:
119 """Verify save_console_log handles OSError gracefully with error log.
121 When file creation fails due to OS-level issues, the error should be
122 caught and logged rather than propagating as an exception.
124 Args:
125 tmp_path: Temporary directory path for test files.
126 """
127 logger = ThreadSafeConsoleLogger(run_dir=tmp_path)
128 logger._messages = ["Test message"]
129 with (
130 patch("builtins.open", side_effect=OSError("Permission denied")),
131 patch("lintro.utils.console.logger.logger.error") as mock_error,
132 ):
133 logger.save_console_log()
134 mock_error.assert_called_once()
135 error_message = str(mock_error.call_args)
136 assert_that(error_message).contains(
137 "Failed to save console log",
138 )
141def test_save_console_log_handles_permission_error(tmp_path: Path) -> None:
142 """Verify save_console_log handles PermissionError gracefully.
144 Permission errors during file creation should be caught and logged
145 without crashing the application.
147 Args:
148 tmp_path: Temporary directory path for test files.
149 """
150 logger = ThreadSafeConsoleLogger(run_dir=tmp_path)
151 logger._messages = ["Test message"]
152 with (
153 patch(
154 "builtins.open",
155 side_effect=PermissionError("Access denied"),
156 ),
157 patch("lintro.utils.console.logger.logger.error") as mock_error,
158 ):
159 logger.save_console_log()
160 mock_error.assert_called_once()
161 assert_that(str(mock_error.call_args)).contains(
162 "Failed to save console log",
163 )