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

1"""Unit tests for totals table generation. 

2 

3Tests for count_affected_files helper and print_totals_table function 

4in lintro.utils.summary_tables. 

5""" 

6 

7from __future__ import annotations 

8 

9from typing import TYPE_CHECKING 

10 

11from assertpy import assert_that 

12 

13from lintro.enums.action import Action 

14from lintro.utils.summary_tables import count_affected_files, print_totals_table 

15 

16if TYPE_CHECKING: 

17 from collections.abc import Callable 

18 

19 from tests.unit.utils.conftest import FakeIssue, FakeToolResult 

20 

21 

22# ============================================================================= 

23# count_affected_files Tests 

24# ============================================================================= 

25 

26 

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) 

30 

31 

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. 

36 

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) 

42 

43 

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. 

49 

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) 

60 

61 

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. 

67 

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) 

82 

83 

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. 

89 

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) 

106 

107 

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. 

113 

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) 

127 

128 

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. 

134 

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 

140 

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) 

150 

151 

152def test_count_affected_files_no_issues_attribute() -> None: 

153 """Verify count_affected_files handles objects without issues attribute.""" 

154 

155 class NoIssuesResult: 

156 name: str = "tool" 

157 

158 assert_that(count_affected_files([NoIssuesResult()])).is_equal_to(0) 

159 

160 

161# ============================================================================= 

162# print_totals_table Tests - CHECK Mode 

163# ============================================================================= 

164 

165 

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. 

170 

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") 

184 

185 

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. 

190 

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") 

204 

205 

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. 

210 

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") 

224 

225 

226# ============================================================================= 

227# print_totals_table Tests - TEST Mode 

228# ============================================================================= 

229 

230 

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. 

235 

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") 

249 

250 

251# ============================================================================= 

252# print_totals_table Tests - FIX Mode 

253# ============================================================================= 

254 

255 

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. 

260 

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") 

275 

276 

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. 

281 

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") 

296 

297 

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. 

302 

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") 

317 

318 

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. 

323 

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") 

337 

338 

339# ============================================================================= 

340# print_totals_table Tests - Format and Header 

341# ============================================================================= 

342 

343 

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. 

348 

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("|") 

362 

363 

364def test_totals_table_contains_header( 

365 console_capture: tuple[Callable[..., None], list[str]], 

366) -> None: 

367 """Verify totals table output contains TOTALS header. 

368 

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") 

381 

382 

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. 

387 

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")