Coverage for tests / unit / utils / summary / test_totals_table.py: 100%
94 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 totals table generation.
3Tests for count_affected_files helper and print_totals_table function
4in lintro.utils.summary_tables.
5"""
7from __future__ import annotations
9from typing import TYPE_CHECKING
11from assertpy import assert_that
13from lintro.enums.action import Action
14from lintro.utils.summary_tables import count_affected_files, print_totals_table
16if TYPE_CHECKING:
17 from collections.abc import Callable
19 from tests.unit.utils.conftest import FakeIssue, FakeToolResult
22# =============================================================================
23# count_affected_files Tests
24# =============================================================================
27def test_count_affected_files_empty_results() -> None:
28 """Verify count_affected_files returns 0 for empty results list."""
29 assert_that(count_affected_files([])).is_equal_to(0)
32def test_count_affected_files_no_issues(
33 fake_tool_result_factory: Callable[..., FakeToolResult],
34) -> None:
35 """Verify count_affected_files returns 0 when results have no issues.
37 Args:
38 fake_tool_result_factory: Factory for creating FakeToolResult instances.
39 """
40 results = [fake_tool_result_factory(issues=[])]
41 assert_that(count_affected_files(results)).is_equal_to(0)
44def test_count_affected_files_single_file(
45 fake_tool_result_factory: Callable[..., FakeToolResult],
46 fake_issue_factory: Callable[..., FakeIssue],
47) -> None:
48 """Verify count_affected_files counts a single affected file.
50 Args:
51 fake_tool_result_factory: Factory for creating FakeToolResult instances.
52 fake_issue_factory: Factory for creating FakeIssue instances.
53 """
54 results = [
55 fake_tool_result_factory(
56 issues=[fake_issue_factory(file="src/main.py")],
57 ),
58 ]
59 assert_that(count_affected_files(results)).is_equal_to(1)
62def test_count_affected_files_deduplication_within_tool(
63 fake_tool_result_factory: Callable[..., FakeToolResult],
64 fake_issue_factory: Callable[..., FakeIssue],
65) -> None:
66 """Verify count_affected_files deduplicates files within a single tool.
68 Args:
69 fake_tool_result_factory: Factory for creating FakeToolResult instances.
70 fake_issue_factory: Factory for creating FakeIssue instances.
71 """
72 results = [
73 fake_tool_result_factory(
74 issues=[
75 fake_issue_factory(file="src/main.py"),
76 fake_issue_factory(file="src/main.py"),
77 fake_issue_factory(file="src/utils.py"),
78 ],
79 ),
80 ]
81 assert_that(count_affected_files(results)).is_equal_to(2)
84def test_count_affected_files_deduplication_across_tools(
85 fake_tool_result_factory: Callable[..., FakeToolResult],
86 fake_issue_factory: Callable[..., FakeIssue],
87) -> None:
88 """Verify count_affected_files deduplicates files across multiple tools.
90 Args:
91 fake_tool_result_factory: Factory for creating FakeToolResult instances.
92 fake_issue_factory: Factory for creating FakeIssue instances.
93 """
94 results = [
95 fake_tool_result_factory(
96 issues=[fake_issue_factory(file="src/main.py")],
97 ),
98 fake_tool_result_factory(
99 issues=[
100 fake_issue_factory(file="src/main.py"),
101 fake_issue_factory(file="src/other.py"),
102 ],
103 ),
104 ]
105 assert_that(count_affected_files(results)).is_equal_to(2)
108def test_count_affected_files_empty_file_paths_excluded(
109 fake_tool_result_factory: Callable[..., FakeToolResult],
110 fake_issue_factory: Callable[..., FakeIssue],
111) -> None:
112 """Verify count_affected_files excludes issues with empty file paths.
114 Args:
115 fake_tool_result_factory: Factory for creating FakeToolResult instances.
116 fake_issue_factory: Factory for creating FakeIssue instances.
117 """
118 results = [
119 fake_tool_result_factory(
120 issues=[
121 fake_issue_factory(file=""),
122 fake_issue_factory(file="src/valid.py"),
123 ],
124 ),
125 ]
126 assert_that(count_affected_files(results)).is_equal_to(1)
129def test_count_affected_files_path_objects_deduplicated(
130 fake_tool_result_factory: Callable[..., FakeToolResult],
131 fake_issue_factory: Callable[..., FakeIssue],
132) -> None:
133 """Verify count_affected_files deduplicates Path objects and strings.
135 Args:
136 fake_tool_result_factory: Factory for creating FakeToolResult instances.
137 fake_issue_factory: Factory for creating FakeIssue instances.
138 """
139 from pathlib import Path
141 results = [
142 fake_tool_result_factory(
143 issues=[
144 fake_issue_factory(file=Path("src/main.py")),
145 fake_issue_factory(file="src/main.py"),
146 ],
147 ),
148 ]
149 assert_that(count_affected_files(results)).is_equal_to(1)
152def test_count_affected_files_no_issues_attribute() -> None:
153 """Verify count_affected_files handles objects without issues attribute."""
155 class NoIssuesResult:
156 name: str = "tool"
158 assert_that(count_affected_files([NoIssuesResult()])).is_equal_to(0)
161# =============================================================================
162# print_totals_table Tests - CHECK Mode
163# =============================================================================
166def test_totals_table_check_mode_contains_total_issues(
167 console_capture: tuple[Callable[..., None], list[str]],
168) -> None:
169 """Verify CHECK mode table contains Total Issues row.
171 Args:
172 console_capture: Fixture for capturing console output.
173 """
174 capture_func, output = console_capture
175 print_totals_table(
176 console_output_func=capture_func,
177 action=Action.CHECK,
178 total_issues=5,
179 affected_files=2,
180 )
181 combined = "\n".join(output)
182 assert_that(combined).contains("Total Issues")
183 assert_that(combined).contains("5")
186def test_totals_table_check_mode_contains_affected_files(
187 console_capture: tuple[Callable[..., None], list[str]],
188) -> None:
189 """Verify CHECK mode table contains Affected Files row.
191 Args:
192 console_capture: Fixture for capturing console output.
193 """
194 capture_func, output = console_capture
195 print_totals_table(
196 console_output_func=capture_func,
197 action=Action.CHECK,
198 total_issues=5,
199 affected_files=3,
200 )
201 combined = "\n".join(output)
202 assert_that(combined).contains("Affected Files")
203 assert_that(combined).contains("3")
206def test_totals_table_check_mode_does_not_contain_fix_rows(
207 console_capture: tuple[Callable[..., None], list[str]],
208) -> None:
209 """Verify CHECK mode table does not contain FIX-specific rows.
211 Args:
212 console_capture: Fixture for capturing console output.
213 """
214 capture_func, output = console_capture
215 print_totals_table(
216 console_output_func=capture_func,
217 action=Action.CHECK,
218 total_issues=5,
219 affected_files=2,
220 )
221 combined = "\n".join(output)
222 assert_that(combined).does_not_contain("Fixed Issues")
223 assert_that(combined).does_not_contain("Remaining Issues")
226# =============================================================================
227# print_totals_table Tests - TEST Mode
228# =============================================================================
231def test_totals_table_test_mode_uses_check_layout(
232 console_capture: tuple[Callable[..., None], list[str]],
233) -> None:
234 """Verify TEST mode table uses same layout as CHECK mode.
236 Args:
237 console_capture: Fixture for capturing console output.
238 """
239 capture_func, output = console_capture
240 print_totals_table(
241 console_output_func=capture_func,
242 action=Action.TEST,
243 total_issues=4,
244 affected_files=1,
245 )
246 combined = "\n".join(output)
247 assert_that(combined).contains("Total Issues")
248 assert_that(combined).contains("Affected Files")
251# =============================================================================
252# print_totals_table Tests - FIX Mode
253# =============================================================================
256def test_totals_table_fix_mode_contains_fixed_issues(
257 console_capture: tuple[Callable[..., None], list[str]],
258) -> None:
259 """Verify FIX mode table contains Fixed Issues row.
261 Args:
262 console_capture: Fixture for capturing console output.
263 """
264 capture_func, output = console_capture
265 print_totals_table(
266 console_output_func=capture_func,
267 action=Action.FIX,
268 total_fixed=10,
269 total_remaining=2,
270 affected_files=5,
271 )
272 combined = "\n".join(output)
273 assert_that(combined).contains("Fixed Issues")
274 assert_that(combined).contains("10")
277def test_totals_table_fix_mode_contains_remaining_issues(
278 console_capture: tuple[Callable[..., None], list[str]],
279) -> None:
280 """Verify FIX mode table contains Remaining Issues row.
282 Args:
283 console_capture: Fixture for capturing console output.
284 """
285 capture_func, output = console_capture
286 print_totals_table(
287 console_output_func=capture_func,
288 action=Action.FIX,
289 total_fixed=10,
290 total_remaining=2,
291 affected_files=5,
292 )
293 combined = "\n".join(output)
294 assert_that(combined).contains("Remaining Issues")
295 assert_that(combined).contains("2")
298def test_totals_table_fix_mode_contains_affected_files(
299 console_capture: tuple[Callable[..., None], list[str]],
300) -> None:
301 """Verify FIX mode table contains Affected Files row.
303 Args:
304 console_capture: Fixture for capturing console output.
305 """
306 capture_func, output = console_capture
307 print_totals_table(
308 console_output_func=capture_func,
309 action=Action.FIX,
310 total_fixed=10,
311 total_remaining=2,
312 affected_files=5,
313 )
314 combined = "\n".join(output)
315 assert_that(combined).contains("Affected Files")
316 assert_that(combined).contains("5")
319def test_totals_table_fix_mode_does_not_contain_total_issues(
320 console_capture: tuple[Callable[..., None], list[str]],
321) -> None:
322 """Verify FIX mode table does not contain Total Issues row.
324 Args:
325 console_capture: Fixture for capturing console output.
326 """
327 capture_func, output = console_capture
328 print_totals_table(
329 console_output_func=capture_func,
330 action=Action.FIX,
331 total_fixed=10,
332 total_remaining=2,
333 affected_files=5,
334 )
335 combined = "\n".join(output)
336 assert_that(combined).does_not_contain("Total Issues")
339# =============================================================================
340# print_totals_table Tests - Format and Header
341# =============================================================================
344def test_totals_table_uses_grid_format(
345 console_capture: tuple[Callable[..., None], list[str]],
346) -> None:
347 """Verify totals table uses grid format with expected characters.
349 Args:
350 console_capture: Fixture for capturing console output.
351 """
352 capture_func, output = console_capture
353 print_totals_table(
354 console_output_func=capture_func,
355 action=Action.CHECK,
356 total_issues=1,
357 affected_files=1,
358 )
359 combined = "\n".join(output)
360 assert_that(combined).contains("+")
361 assert_that(combined).contains("|")
364def test_totals_table_contains_header(
365 console_capture: tuple[Callable[..., None], list[str]],
366) -> None:
367 """Verify totals table output contains TOTALS header.
369 Args:
370 console_capture: Fixture for capturing console output.
371 """
372 capture_func, output = console_capture
373 print_totals_table(
374 console_output_func=capture_func,
375 action=Action.CHECK,
376 total_issues=0,
377 affected_files=0,
378 )
379 combined = "\n".join(output)
380 assert_that(combined).contains("TOTALS")
383def test_totals_table_contains_metric_count_headers(
384 console_capture: tuple[Callable[..., None], list[str]],
385) -> None:
386 """Verify totals table contains Metric and Count column headers.
388 Args:
389 console_capture: Fixture for capturing console output.
390 """
391 capture_func, output = console_capture
392 print_totals_table(
393 console_output_func=capture_func,
394 action=Action.CHECK,
395 total_issues=0,
396 affected_files=0,
397 )
398 combined = "\n".join(output)
399 assert_that(combined).contains("Metric")
400 assert_that(combined).contains("Count")