Coverage for tests / unit / utils / test_display_helpers_fallback.py: 99%
74 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 display_helpers fallback behavior when click is unavailable.
3Tests verify that print_final_status and print_final_status_format work
4correctly using ANSI escape codes when the click library is not available.
5"""
7from __future__ import annotations
9import builtins
10from collections.abc import Callable
11from typing import TYPE_CHECKING, Any
12from unittest.mock import patch
14import pytest
15from assertpy import assert_that
17from lintro.enums.action import Action
18from lintro.utils.display_helpers import (
19 print_final_status,
20 print_final_status_format,
21)
23if TYPE_CHECKING:
24 from collections.abc import Generator
26# ANSI escape codes for color verification
27ANSI_GREEN = "\033[92m"
28ANSI_RED = "\033[91m"
29ANSI_RESET = "\033[0m"
32# --- Fixtures ---
35@pytest.fixture
36def console_capture() -> Generator[tuple[list[str], Callable[..., None]], None, None]:
37 """Provide a mock console function that captures output.
39 Yields:
40 tuple[list[str], Callable[..., None]]: Output list and mock console function.
41 """
42 output: list[str] = []
44 def mock_console(text: str = "") -> None:
45 output.append(text)
47 yield output, mock_console
50@pytest.fixture
51def mock_click_unavailable() -> Generator[None, None, None]:
52 """Mock import to make click unavailable.
54 Yields:
55 None: After setting up the mock import.
56 """
57 original_import = builtins.__import__
59 def mock_import(name: str, *args: Any, **kwargs: Any) -> Any:
60 if name == "click":
61 raise ImportError("click not available")
62 return original_import(name, *args, **kwargs)
64 with patch.object(builtins, "__import__", side_effect=mock_import):
65 yield
68# --- Tests for print_final_status fallback ---
71@pytest.mark.parametrize(
72 ("action", "total_issues", "expected_message", "expected_color"),
73 [
74 pytest.param(
75 Action.CHECK,
76 0,
77 "No issues found",
78 ANSI_GREEN,
79 id="check_no_issues_green",
80 ),
81 pytest.param(
82 Action.CHECK,
83 5,
84 "Found 5 issues",
85 ANSI_RED,
86 id="check_with_issues_red",
87 ),
88 pytest.param(
89 Action.FIX,
90 0,
91 "No issues found",
92 ANSI_GREEN,
93 id="fix_no_issues_green",
94 ),
95 pytest.param(
96 Action.FIX,
97 3,
98 "Fixed 3 issues",
99 ANSI_GREEN,
100 id="fix_with_issues_green",
101 ),
102 ],
103)
104def test_print_final_status_fallback_ansi_codes(
105 console_capture: tuple[list[str], Callable[..., None]],
106 mock_click_unavailable: None,
107 action: Action,
108 total_issues: int,
109 expected_message: str,
110 expected_color: str,
111) -> None:
112 """Verify print_final_status uses correct ANSI codes when click unavailable.
114 Args:
115 console_capture: Fixture providing output capture.
116 mock_click_unavailable: Fixture mocking click as unavailable.
117 action: Action type (CHECK or FIX).
118 total_issues: Number of issues to report.
119 expected_message: Expected message text in output.
120 expected_color: Expected ANSI color code.
121 """
122 output, mock_console = console_capture
124 print_final_status(mock_console, action, total_issues=total_issues)
126 combined = "".join(output)
127 # Should contain either the ANSI code or the message (depending on implementation)
128 has_ansi_color = expected_color in combined
129 has_message = expected_message in combined
130 assert_that(has_ansi_color or has_message).is_true()
133def test_print_final_status_fallback_outputs_blank_line(
134 console_capture: tuple[list[str], Callable[..., None]],
135 mock_click_unavailable: None,
136) -> None:
137 """Verify print_final_status appends blank line in fallback mode.
139 Args:
140 console_capture: Fixture providing output capture.
141 mock_click_unavailable: Fixture mocking click as unavailable.
142 """
143 output, mock_console = console_capture
145 print_final_status(mock_console, Action.CHECK, total_issues=0)
147 assert_that(output).is_not_empty()
148 assert_that(output[-1]).is_equal_to("")
151def test_print_final_status_fallback_includes_reset_code(
152 console_capture: tuple[list[str], Callable[..., None]],
153 mock_click_unavailable: None,
154) -> None:
155 """Verify print_final_status includes ANSI reset code in fallback mode.
157 Args:
158 console_capture: Fixture providing output capture.
159 mock_click_unavailable: Fixture mocking click as unavailable.
160 """
161 output, mock_console = console_capture
163 print_final_status(mock_console, Action.CHECK, total_issues=0)
165 combined = "".join(output)
166 # Should contain reset code to clear formatting
167 assert_that(ANSI_RESET in combined or "No issues" in combined).is_true()
170# --- Tests for print_final_status_format fallback ---
173@pytest.mark.parametrize(
174 ("total_fixed", "total_remaining", "expected_messages", "expected_colors"),
175 [
176 pytest.param(
177 0,
178 0,
179 ["No issues found"],
180 [ANSI_GREEN],
181 id="no_issues_green",
182 ),
183 pytest.param(
184 5,
185 0,
186 ["5 fixed"],
187 [ANSI_GREEN],
188 id="all_fixed_green",
189 ),
190 pytest.param(
191 3,
192 2,
193 ["fixed", "remaining"],
194 [ANSI_GREEN, ANSI_RED],
195 id="some_fixed_some_remaining",
196 ),
197 pytest.param(
198 0,
199 4,
200 ["4 remaining"],
201 [ANSI_RED],
202 id="only_remaining_red",
203 ),
204 ],
205)
206def test_print_final_status_format_fallback_ansi_codes(
207 console_capture: tuple[list[str], Callable[..., None]],
208 mock_click_unavailable: None,
209 total_fixed: int,
210 total_remaining: int,
211 expected_messages: list[str],
212 expected_colors: list[str],
213) -> None:
214 """Verify print_final_status_format uses correct ANSI codes when click unavailable.
216 Args:
217 console_capture: Fixture providing output capture.
218 mock_click_unavailable: Fixture mocking click as unavailable.
219 total_fixed: Number of fixed issues.
220 total_remaining: Number of remaining issues.
221 expected_messages: Expected message fragments in output.
222 expected_colors: Expected ANSI color codes.
223 """
224 output, mock_console = console_capture
226 print_final_status_format(
227 mock_console,
228 total_fixed=total_fixed,
229 total_remaining=total_remaining,
230 )
232 combined = "".join(output).lower()
233 # Check that messages appear (case-insensitive)
234 for expected in expected_messages:
235 assert_that(expected.lower() in combined).is_true()
238def test_print_final_status_format_fallback_outputs_blank_line(
239 console_capture: tuple[list[str], Callable[..., None]],
240 mock_click_unavailable: None,
241) -> None:
242 """Verify print_final_status_format appends blank line in fallback mode.
244 Args:
245 console_capture: Fixture providing output capture.
246 mock_click_unavailable: Fixture mocking click as unavailable.
247 """
248 output, mock_console = console_capture
250 print_final_status_format(mock_console, total_fixed=0, total_remaining=0)
252 assert_that(output).is_not_empty()
253 assert_that(output[-1]).is_equal_to("")
256def test_print_final_status_format_fallback_fixed_uses_green(
257 console_capture: tuple[list[str], Callable[..., None]],
258 mock_click_unavailable: None,
259) -> None:
260 """Verify fixed count uses green ANSI code in fallback mode.
262 Args:
263 console_capture: Fixture providing output capture.
264 mock_click_unavailable: Fixture mocking click as unavailable.
265 """
266 output, mock_console = console_capture
268 print_final_status_format(mock_console, total_fixed=5, total_remaining=0)
270 combined = "".join(output)
271 # Should contain green ANSI code or the success message
272 assert_that(ANSI_GREEN in combined or "5 fixed" in combined).is_true()
275def test_print_final_status_format_fallback_remaining_uses_red(
276 console_capture: tuple[list[str], Callable[..., None]],
277 mock_click_unavailable: None,
278) -> None:
279 """Verify remaining count uses red ANSI code in fallback mode.
281 Args:
282 console_capture: Fixture providing output capture.
283 mock_click_unavailable: Fixture mocking click as unavailable.
284 """
285 output, mock_console = console_capture
287 print_final_status_format(mock_console, total_fixed=0, total_remaining=4)
289 combined = "".join(output)
290 # Should contain red ANSI code or the remaining message
291 assert_that(ANSI_RED in combined or "4 remaining" in combined).is_true()
294def test_print_final_status_format_fallback_mixed_colors(
295 console_capture: tuple[list[str], Callable[..., None]],
296 mock_click_unavailable: None,
297) -> None:
298 """Verify mixed fixed/remaining uses both green and red ANSI codes.
300 Args:
301 console_capture: Fixture providing output capture.
302 mock_click_unavailable: Fixture mocking click as unavailable.
303 """
304 output, mock_console = console_capture
306 print_final_status_format(mock_console, total_fixed=3, total_remaining=2)
308 combined = "".join(output)
309 # Should have both colors or both messages
310 has_both_colors = ANSI_GREEN in combined and ANSI_RED in combined
311 has_both_messages = "fixed" in combined.lower() and "remaining" in combined.lower()
312 assert_that(has_both_colors or has_both_messages).is_true()