Coverage for tests / unit / utils / console / summary / test_delegation.py: 100%

107 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""Unit tests for ThreadSafeConsoleLogger summary delegation methods. 

2 

3This module tests the delegation functionality of ThreadSafeConsoleLogger summary 

4methods, 

5including summary table, final status, and ASCII art delegation. 

6""" 

7 

8from __future__ import annotations 

9 

10from typing import TYPE_CHECKING 

11from unittest.mock import patch 

12 

13import pytest 

14from assertpy import assert_that 

15 

16from lintro.enums.action import Action 

17from lintro.utils.console.logger import ThreadSafeConsoleLogger 

18 

19if TYPE_CHECKING: 

20 from collections.abc import Callable 

21 

22 from tests.unit.utils.conftest import FakeToolResult 

23 

24 

25# ============================================================================= 

26# Summary Table Delegation Tests 

27# ============================================================================= 

28 

29 

30def test_print_summary_table_delegates_to_module_function( 

31 fake_tool_result_factory: Callable[..., FakeToolResult], 

32) -> None: 

33 """Verify _print_summary_table delegates to print_summary_table function. 

34 

35 The method should pass through all parameters to the module-level 

36 print_summary_table function. 

37 

38 

39 Args: 

40 fake_tool_result_factory: Factory for creating FakeToolResult instances. 

41 """ 

42 logger = ThreadSafeConsoleLogger() 

43 results = [fake_tool_result_factory()] 

44 

45 with patch("lintro.utils.summary_tables.print_summary_table") as mock_print: 

46 logger._print_summary_table(Action.CHECK, results) 

47 mock_print.assert_called_once() 

48 

49 

50def test_print_summary_table_converts_string_action( 

51 fake_tool_result_factory: Callable[..., FakeToolResult], 

52) -> None: 

53 """Verify _print_summary_table converts string action to Action enum. 

54 

55 String action values should be normalized to Action enum instances 

56 before being passed to the underlying function. 

57 

58 

59 Args: 

60 fake_tool_result_factory: Factory for creating FakeToolResult instances. 

61 """ 

62 logger = ThreadSafeConsoleLogger() 

63 results = [fake_tool_result_factory()] 

64 

65 with patch("lintro.utils.summary_tables.print_summary_table") as mock_print: 

66 logger._print_summary_table("check", results) 

67 mock_print.assert_called_once() 

68 call_kwargs = mock_print.call_args.kwargs 

69 assert_that(call_kwargs["action"]).is_equal_to(Action.CHECK) 

70 

71 

72@pytest.mark.parametrize( 

73 ("action_str", "expected_action"), 

74 [ 

75 ("check", Action.CHECK), 

76 ("fix", Action.FIX), 

77 ("fmt", Action.FIX), 

78 ("test", Action.TEST), 

79 ], 

80) 

81def test_print_summary_table_action_normalization( 

82 fake_tool_result_factory: Callable[..., FakeToolResult], 

83 action_str: str, 

84 expected_action: Action, 

85) -> None: 

86 """Verify _print_summary_table normalizes various action string formats. 

87 

88 Different string representations of actions should all be correctly 

89 converted to their corresponding Action enum values. 

90 

91 

92 Args: 

93 fake_tool_result_factory: Factory for creating FakeToolResult instances. 

94 action_str: String representation of the action. 

95 expected_action: Expected Action enum value. 

96 """ 

97 logger = ThreadSafeConsoleLogger() 

98 results = [fake_tool_result_factory()] 

99 

100 with patch("lintro.utils.summary_tables.print_summary_table") as mock_print: 

101 logger._print_summary_table(action_str, results) 

102 call_kwargs = mock_print.call_args.kwargs 

103 assert_that(call_kwargs["action"]).is_equal_to(expected_action) 

104 

105 

106# ============================================================================= 

107# Totals Table Delegation Tests 

108# ============================================================================= 

109 

110 

111def test_print_totals_table_delegates_to_module_function() -> None: 

112 """Verify _print_totals_table delegates to print_totals_table function. 

113 

114 The method should pass through all parameters to the module-level 

115 print_totals_table function. 

116 """ 

117 logger = ThreadSafeConsoleLogger() 

118 

119 with patch("lintro.utils.summary_tables.print_totals_table") as mock_print: 

120 logger._print_totals_table( 

121 action=Action.CHECK, 

122 total_issues=5, 

123 affected_files=2, 

124 ) 

125 mock_print.assert_called_once_with( 

126 console_output_func=logger.console_output, 

127 action=Action.CHECK, 

128 total_issues=5, 

129 total_fixed=0, 

130 total_remaining=0, 

131 affected_files=2, 

132 severity_errors=0, 

133 severity_warnings=0, 

134 severity_info=0, 

135 total_ai_applied=0, 

136 total_ai_verified=0, 

137 ) 

138 

139 

140def test_print_totals_table_delegates_fix_mode() -> None: 

141 """Verify _print_totals_table delegates FIX mode parameters correctly. 

142 

143 FIX mode should pass total_fixed and total_remaining to the 

144 underlying function. 

145 """ 

146 logger = ThreadSafeConsoleLogger() 

147 

148 with patch("lintro.utils.summary_tables.print_totals_table") as mock_print: 

149 logger._print_totals_table( 

150 action=Action.FIX, 

151 total_fixed=10, 

152 total_remaining=3, 

153 affected_files=5, 

154 total_ai_applied=2, 

155 total_ai_verified=1, 

156 ) 

157 mock_print.assert_called_once_with( 

158 console_output_func=logger.console_output, 

159 action=Action.FIX, 

160 total_issues=0, 

161 total_fixed=10, 

162 total_remaining=3, 

163 affected_files=5, 

164 severity_errors=0, 

165 severity_warnings=0, 

166 severity_info=0, 

167 total_ai_applied=2, 

168 total_ai_verified=1, 

169 ) 

170 

171 

172@pytest.mark.parametrize( 

173 ("action", "kwargs"), 

174 [ 

175 (Action.CHECK, {"total_issues": 0, "affected_files": 0}), 

176 (Action.CHECK, {"total_issues": 10, "affected_files": 5}), 

177 (Action.FIX, {"total_fixed": 5, "total_remaining": 2, "affected_files": 3}), 

178 (Action.TEST, {"total_issues": 4, "affected_files": 1}), 

179 ], 

180) 

181def test_print_totals_table_various_inputs( 

182 action: Action, 

183 kwargs: dict[str, int], 

184) -> None: 

185 """Verify _print_totals_table handles various action and parameter combos. 

186 

187 Args: 

188 action: Action type to test. 

189 kwargs: Keyword arguments to pass to the method. 

190 """ 

191 logger = ThreadSafeConsoleLogger() 

192 

193 with patch("lintro.utils.summary_tables.print_totals_table") as mock_print: 

194 logger._print_totals_table(action=action, **kwargs) 

195 mock_print.assert_called_once() 

196 

197 

198# ============================================================================= 

199# Final Status Delegation Tests 

200# ============================================================================= 

201 

202 

203def test_print_final_status_delegates_to_module_function() -> None: 

204 """Verify _print_final_status delegates to print_final_status function. 

205 

206 The method should pass the console output function, action, and 

207 total issues to the module-level function. 

208 """ 

209 logger = ThreadSafeConsoleLogger() 

210 

211 with patch("lintro.utils.console.logger.print_final_status") as mock_print: 

212 logger._print_final_status(Action.CHECK, 5) 

213 mock_print.assert_called_once() 

214 

215 

216def test_print_final_status_converts_string_action() -> None: 

217 """Verify _print_final_status accepts string action values. 

218 

219 String actions should be passed through and handled by the underlying 

220 function's normalization logic. 

221 """ 

222 logger = ThreadSafeConsoleLogger() 

223 

224 with patch("lintro.utils.console.logger.print_final_status") as mock_print: 

225 logger._print_final_status("fmt", 3) 

226 mock_print.assert_called_once() 

227 

228 

229@pytest.mark.parametrize( 

230 ("action", "total_issues"), 

231 [ 

232 (Action.CHECK, 0), 

233 (Action.CHECK, 10), 

234 (Action.FIX, 0), 

235 (Action.FIX, 5), 

236 ("check", 3), 

237 ("fmt", 7), 

238 ], 

239) 

240def test_print_final_status_various_inputs( 

241 action: Action | str, 

242 total_issues: int, 

243) -> None: 

244 """Verify _print_final_status handles various action and issue combinations. 

245 

246 Both enum and string action values with different issue counts should 

247 be properly delegated. 

248 

249 

250 Args: 

251 action: Action type (enum or string). 

252 total_issues: Number of total issues. 

253 """ 

254 logger = ThreadSafeConsoleLogger() 

255 

256 with patch("lintro.utils.console.logger.print_final_status") as mock_print: 

257 logger._print_final_status(action, total_issues) 

258 mock_print.assert_called_once() 

259 

260 

261# ============================================================================= 

262# Final Status Format Delegation Tests 

263# ============================================================================= 

264 

265 

266def test_print_final_status_format_delegates_correctly() -> None: 

267 """Verify _print_final_status_format delegates with correct parameters. 

268 

269 The method should pass the console output function, total fixed, 

270 and total remaining to the module-level function. 

271 """ 

272 logger = ThreadSafeConsoleLogger() 

273 

274 with patch("lintro.utils.console.logger.print_final_status_format") as mock_print: 

275 logger._print_final_status_format(10, 2) 

276 mock_print.assert_called_once_with( 

277 console_output_func=logger.console_output, 

278 total_fixed=10, 

279 total_remaining=2, 

280 ) 

281 

282 

283@pytest.mark.parametrize( 

284 ("total_fixed", "total_remaining"), 

285 [ 

286 (0, 0), 

287 (5, 0), 

288 (0, 3), 

289 (10, 5), 

290 (100, 50), 

291 ], 

292) 

293def test_print_final_status_format_various_counts( 

294 total_fixed: int, 

295 total_remaining: int, 

296) -> None: 

297 """Verify _print_final_status_format handles various count combinations. 

298 

299 Different fixed and remaining combinations should be properly passed 

300 to the underlying function. 

301 

302 

303 Args: 

304 total_fixed: Number of fixed issues. 

305 total_remaining: Number of remaining issues. 

306 """ 

307 logger = ThreadSafeConsoleLogger() 

308 

309 with patch("lintro.utils.console.logger.print_final_status_format") as mock_print: 

310 logger._print_final_status_format(total_fixed, total_remaining) 

311 mock_print.assert_called_once_with( 

312 console_output_func=logger.console_output, 

313 total_fixed=total_fixed, 

314 total_remaining=total_remaining, 

315 ) 

316 

317 

318# ============================================================================= 

319# ASCII Art Delegation Tests 

320# ============================================================================= 

321 

322 

323def test_print_ascii_art_delegates_correctly() -> None: 

324 """Verify _print_ascii_art delegates with correct parameters. 

325 

326 The method should pass the console output function and issue count 

327 to the module-level print_ascii_art function. 

328 """ 

329 logger = ThreadSafeConsoleLogger() 

330 

331 with patch("lintro.utils.console.logger.print_ascii_art") as mock_print: 

332 logger._print_ascii_art(5) 

333 mock_print.assert_called_once_with( 

334 console_output_func=logger.console_output, 

335 issue_count=5, 

336 ) 

337 

338 

339@pytest.mark.parametrize("issue_count", [0, 1, 5, 10, 100]) 

340def test_print_ascii_art_various_counts(issue_count: int) -> None: 

341 """Verify _print_ascii_art handles various issue counts. 

342 

343 Different issue counts should be properly passed to display 

344 either success or failure ASCII art. 

345 

346 

347 Args: 

348 issue_count: Number of issues to display. 

349 """ 

350 logger = ThreadSafeConsoleLogger() 

351 

352 with patch("lintro.utils.console.logger.print_ascii_art") as mock_print: 

353 logger._print_ascii_art(issue_count) 

354 mock_print.assert_called_once_with( 

355 console_output_func=logger.console_output, 

356 issue_count=issue_count, 

357 ) 

358 

359 

360# ============================================================================= 

361# Integration Tests - Full Execution Summary Flow 

362# ============================================================================= 

363 

364 

365def test_execution_summary_outputs_header_and_border( 

366 fake_tool_result_factory: Callable[..., FakeToolResult], 

367) -> None: 

368 """Verify print_execution_summary outputs properly styled header. 

369 

370 The execution summary should begin with a styled header including 

371 the section title and border for visual clarity. 

372 

373 

374 Args: 

375 fake_tool_result_factory: Factory for creating FakeToolResult instances. 

376 """ 

377 logger = ThreadSafeConsoleLogger() 

378 results = [fake_tool_result_factory(success=True, issues_count=0)] 

379 

380 with ( 

381 patch.object(logger, "console_output") as mock_output, 

382 patch.object(logger, "_print_summary_table"), 

383 patch.object(logger, "_print_ascii_art"), 

384 ): 

385 logger.print_execution_summary(Action.CHECK, results) 

386 # Should have multiple output calls including header 

387 assert_that(mock_output.call_count).is_greater_than(0) 

388 

389 

390def test_execution_summary_calls_all_components( 

391 fake_tool_result_factory: Callable[..., FakeToolResult], 

392) -> None: 

393 """Verify print_execution_summary invokes all required components. 

394 

395 The method should call summary table and ASCII art display as part 

396 of the complete execution summary output. 

397 

398 

399 Args: 

400 fake_tool_result_factory: Factory for creating FakeToolResult instances. 

401 """ 

402 logger = ThreadSafeConsoleLogger() 

403 results = [fake_tool_result_factory(success=True, issues_count=3)] 

404 

405 with ( 

406 patch.object(logger, "console_output"), 

407 patch.object(logger, "_print_summary_table") as mock_table, 

408 patch.object(logger, "_print_ascii_art") as mock_art, 

409 ): 

410 logger.print_execution_summary(Action.CHECK, results) 

411 mock_table.assert_called_once() 

412 mock_art.assert_called_once() 

413 

414 

415def test_execution_summary_empty_results_handled( 

416 console_capture: tuple[Callable[[str], None], list[str]], 

417) -> None: 

418 """Verify print_execution_summary handles empty results list gracefully. 

419 

420 Even with no tool results, the summary should complete without errors 

421 and show appropriate totals (zero). 

422 

423 

424 Args: 

425 console_capture: Fixture for capturing console output. 

426 """ 

427 logger = ThreadSafeConsoleLogger() 

428 

429 with ( 

430 patch.object(logger, "console_output"), 

431 patch.object(logger, "_print_summary_table"), 

432 patch.object(logger, "_print_ascii_art") as mock_art, 

433 ): 

434 logger.print_execution_summary(Action.CHECK, []) 

435 mock_art.assert_called_once_with(total_issues=0) 

436 

437 

438@pytest.mark.parametrize( 

439 "action", 

440 [Action.CHECK, Action.FIX, Action.TEST], 

441) 

442def test_execution_summary_all_action_types( 

443 fake_tool_result_factory: Callable[..., FakeToolResult], 

444 action: Action, 

445) -> None: 

446 """Verify print_execution_summary handles all action types. 

447 

448 CHECK, FIX, and TEST actions should all produce valid summary output 

449 without errors. 

450 

451 

452 Args: 

453 fake_tool_result_factory: Factory for creating FakeToolResult instances. 

454 action: Action type to test. 

455 """ 

456 logger = ThreadSafeConsoleLogger() 

457 results = [fake_tool_result_factory(success=True, issues_count=0)] 

458 

459 with ( 

460 patch.object(logger, "console_output"), 

461 patch.object(logger, "_print_summary_table"), 

462 patch.object(logger, "_print_ascii_art"), 

463 ): 

464 # Should not raise for any action type 

465 logger.print_execution_summary(action, results)