Coverage for tests / unit / tools / core / test_command_builders.py: 99%

210 statements  

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

1"""Unit tests for command_builders module.""" 

2 

3from __future__ import annotations 

4 

5import sys 

6from collections.abc import Generator 

7from unittest.mock import MagicMock, patch 

8 

9import pytest 

10from assertpy import assert_that 

11 

12from lintro.enums.tool_name import ToolName 

13from lintro.tools.core.command_builders import ( 

14 CargoBuilder, 

15 CommandBuilder, 

16 CommandBuilderRegistry, 

17 NodeJSBuilder, 

18 PytestBuilder, 

19 PythonBundledBuilder, 

20 StandaloneBuilder, 

21) 

22 

23 

24def _mock_which_for_venv( 

25 *, 

26 in_venv: bool, 

27 in_path: str | None = None, 

28 expected_names: str | set[str], 

29) -> MagicMock: 

30 """Create a shutil.which mock that controls venv vs PATH discovery. 

31 

32 When in_venv is True, shutil.which(tool, path=scripts_dir) returns 

33 a path (simulating the tool being in the venv). When False, it returns 

34 None for the venv lookup but returns in_path for the PATH lookup. 

35 The mock validates that the requested name matches expected_names and 

36 that the scripts directory path looks correct before returning results. 

37 

38 Args: 

39 in_venv: Whether the tool should be found in the venv scripts dir. 

40 in_path: Path to return for PATH-based discovery (None = not found). 

41 expected_names: Executable name(s) this mock should respond to. 

42 

43 Returns: 

44 Mock to use with patch("shutil.which", ...). 

45 """ 

46 names = {expected_names} if isinstance(expected_names, str) else expected_names 

47 

48 def which_side_effect( 

49 name: str, 

50 path: str | None = None, 

51 ) -> str | None: 

52 if path is not None: 

53 # Venv scripts lookup: validate name and path 

54 if name not in names: 

55 return None 

56 if not path.endswith(("/bin", "\\Scripts")): 

57 return None 

58 return f"/fake/venv/bin/{name}" if in_venv else None 

59 # PATH lookup: validate name 

60 if name not in names: 

61 return None 

62 return in_path 

63 

64 return MagicMock(side_effect=which_side_effect) 

65 

66 

67@pytest.fixture(autouse=True) 

68def reset_registry() -> Generator[None, None, None]: 

69 """Reset the command builder registry before and after each test. 

70 

71 Yields: 

72 None: After clearing the registry and before restoring. 

73 """ 

74 original_builders = CommandBuilderRegistry._builders.copy() 

75 yield 

76 CommandBuilderRegistry._builders = original_builders 

77 

78 

79# ============================================================================= 

80# PythonBundledBuilder tests 

81# ============================================================================= 

82 

83 

84def test_python_bundled_builder_handles_ruff() -> None: 

85 """PythonBundledBuilder can handle ruff.""" 

86 builder = PythonBundledBuilder() 

87 assert_that(builder.can_handle(ToolName.RUFF)).is_true() 

88 

89 

90def test_python_bundled_builder_handles_black() -> None: 

91 """PythonBundledBuilder can handle black.""" 

92 builder = PythonBundledBuilder() 

93 assert_that(builder.can_handle(ToolName.BLACK)).is_true() 

94 

95 

96def test_python_bundled_builder_handles_mypy() -> None: 

97 """PythonBundledBuilder can handle mypy.""" 

98 builder = PythonBundledBuilder() 

99 assert_that(builder.can_handle(ToolName.MYPY)).is_true() 

100 

101 

102def test_python_bundled_builder_does_not_handle_markdownlint() -> None: 

103 """PythonBundledBuilder does not handle Node.js tools.""" 

104 builder = PythonBundledBuilder() 

105 assert_that(builder.can_handle(ToolName.MARKDOWNLINT)).is_false() 

106 

107 

108def test_python_bundled_builder_prefers_path_binary_outside_venv() -> None: 

109 """PythonBundledBuilder prefers PATH binary when outside venv.""" 

110 builder = PythonBundledBuilder() 

111 # Simulate running outside a venv (prefix == base_prefix) 

112 with ( 

113 patch("shutil.which", return_value="/usr/local/bin/ruff"), 

114 patch( 

115 "lintro.tools.core.command_builders.sys.prefix", 

116 "/usr/local", 

117 ), 

118 patch( 

119 "lintro.tools.core.command_builders.sys.base_prefix", 

120 "/usr/local", 

121 ), 

122 patch( 

123 "lintro.tools.core.command_builders._is_compiled_binary", 

124 return_value=False, 

125 ), 

126 ): 

127 cmd = builder.get_command("ruff", ToolName.RUFF) 

128 assert_that(cmd).is_equal_to(["/usr/local/bin/ruff"]) 

129 

130 

131def test_python_bundled_builder_prefers_python_module_in_venv() -> None: 

132 """PythonBundledBuilder prefers python -m when tool is in venv scripts.""" 

133 builder = PythonBundledBuilder() 

134 # Simulate running inside a venv with the tool present in venv scripts 

135 with ( 

136 patch( 

137 "shutil.which", 

138 _mock_which_for_venv(in_venv=True, expected_names="ruff"), 

139 ), 

140 patch( 

141 "lintro.tools.core.command_builders.sys.prefix", 

142 "/app/.venv", 

143 ), 

144 patch( 

145 "lintro.tools.core.command_builders.sys.base_prefix", 

146 "/usr/local", 

147 ), 

148 patch( 

149 "lintro.tools.core.command_builders._is_compiled_binary", 

150 return_value=False, 

151 ), 

152 patch( 

153 "lintro.tools.core.command_builders.sysconfig.get_path", 

154 return_value="/app/.venv/bin", 

155 ), 

156 ): 

157 cmd = builder.get_command("ruff", ToolName.RUFF) 

158 # Should return [python_exe, "-m", "ruff"] when tool is in venv 

159 assert_that(cmd).is_length(3) 

160 assert_that(cmd[0]).is_equal_to(sys.executable) 

161 assert_that(cmd[1]).is_equal_to("-m") 

162 assert_that(cmd[2]).is_equal_to("ruff") 

163 

164 

165def test_python_bundled_builder_prefers_path_when_tool_not_in_venv() -> None: 

166 """PythonBundledBuilder uses PATH when tool is not in venv (Homebrew).""" 

167 builder = PythonBundledBuilder() 

168 # Simulate Homebrew: in a venv, but tool is a separate Homebrew formula 

169 with ( 

170 patch( 

171 "shutil.which", 

172 _mock_which_for_venv( 

173 in_venv=False, 

174 in_path="/opt/homebrew/bin/ruff", 

175 expected_names="ruff", 

176 ), 

177 ), 

178 patch( 

179 "lintro.tools.core.command_builders.sys.prefix", 

180 "/opt/homebrew/Cellar/lintro/0.57.7/libexec", 

181 ), 

182 patch( 

183 "lintro.tools.core.command_builders.sys.base_prefix", 

184 "/opt/homebrew/Cellar/python@3.13/3.13.0/Frameworks", 

185 ), 

186 patch( 

187 "lintro.tools.core.command_builders._is_compiled_binary", 

188 return_value=False, 

189 ), 

190 patch( 

191 "lintro.tools.core.command_builders.sysconfig.get_path", 

192 return_value="/opt/homebrew/Cellar/lintro/0.57.7/libexec/bin", 

193 ), 

194 ): 

195 cmd = builder.get_command("ruff", ToolName.RUFF) 

196 assert_that(cmd).is_equal_to(["/opt/homebrew/bin/ruff"]) 

197 

198 

199def test_python_bundled_builder_last_resort_python_m_in_venv() -> None: 

200 """PythonBundledBuilder falls back to python -m when tool nowhere.""" 

201 builder = PythonBundledBuilder() 

202 # In a venv, tool NOT in venv scripts, NOT in PATH 

203 with ( 

204 patch( 

205 "shutil.which", 

206 _mock_which_for_venv(in_venv=False, in_path=None, expected_names="ruff"), 

207 ), 

208 patch( 

209 "lintro.tools.core.command_builders.sys.prefix", 

210 "/opt/homebrew/Cellar/lintro/0.57.7/libexec", 

211 ), 

212 patch( 

213 "lintro.tools.core.command_builders.sys.base_prefix", 

214 "/opt/homebrew/Cellar/python@3.13/3.13.0/Frameworks", 

215 ), 

216 patch( 

217 "lintro.tools.core.command_builders._is_compiled_binary", 

218 return_value=False, 

219 ), 

220 patch( 

221 "lintro.tools.core.command_builders.sysconfig.get_path", 

222 return_value="/opt/homebrew/Cellar/lintro/0.57.7/libexec/bin", 

223 ), 

224 ): 

225 cmd = builder.get_command("ruff", ToolName.RUFF) 

226 # Last resort: python -m 

227 assert_that(cmd).is_length(3) 

228 assert_that(cmd[0]).is_equal_to(sys.executable) 

229 assert_that(cmd[1]).is_equal_to("-m") 

230 assert_that(cmd[2]).is_equal_to("ruff") 

231 

232 

233def test_python_bundled_builder_falls_back_to_python_module() -> None: 

234 """PythonBundledBuilder falls back to python -m when tool not in PATH.""" 

235 builder = PythonBundledBuilder() 

236 with ( 

237 patch("shutil.which", return_value=None), 

238 patch( 

239 "lintro.tools.core.command_builders._is_compiled_binary", 

240 return_value=False, 

241 ), 

242 ): 

243 cmd = builder.get_command("ruff", ToolName.RUFF) 

244 # Should return [python_exe, "-m", "ruff"] 

245 assert_that(cmd).is_length(3) 

246 assert_that(cmd[0]).is_equal_to(sys.executable) 

247 assert_that(cmd[1]).is_equal_to("-m") 

248 assert_that(cmd[2]).is_equal_to("ruff") 

249 

250 

251def test_python_bundled_builder_skips_python_module_when_compiled() -> None: 

252 """PythonBundledBuilder skips python -m fallback when compiled.""" 

253 builder = PythonBundledBuilder() 

254 with ( 

255 patch("shutil.which", return_value=None), 

256 patch( 

257 "lintro.tools.core.command_builders._is_compiled_binary", 

258 return_value=True, 

259 ), 

260 ): 

261 cmd = builder.get_command("ruff", ToolName.RUFF) 

262 # Should return just [tool_name] when compiled 

263 assert_that(cmd).is_equal_to(["ruff"]) 

264 

265 

266# ============================================================================= 

267# PytestBuilder tests 

268# ============================================================================= 

269 

270 

271def test_pytest_builder_handles_pytest() -> None: 

272 """PytestBuilder can handle pytest.""" 

273 builder = PytestBuilder() 

274 assert_that(builder.can_handle(ToolName.PYTEST)).is_true() 

275 

276 

277def test_pytest_builder_does_not_handle_ruff() -> None: 

278 """PytestBuilder does not handle ruff.""" 

279 builder = PytestBuilder() 

280 assert_that(builder.can_handle(ToolName.RUFF)).is_false() 

281 

282 

283def test_pytest_builder_prefers_path_binary_outside_venv() -> None: 

284 """PytestBuilder prefers PATH binary when outside venv.""" 

285 builder = PytestBuilder() 

286 # Simulate running outside a venv (prefix == base_prefix) 

287 with ( 

288 patch("shutil.which", return_value="/usr/local/bin/pytest"), 

289 patch( 

290 "lintro.tools.core.command_builders.sys.prefix", 

291 "/usr/local", 

292 ), 

293 patch( 

294 "lintro.tools.core.command_builders.sys.base_prefix", 

295 "/usr/local", 

296 ), 

297 patch( 

298 "lintro.tools.core.command_builders._is_compiled_binary", 

299 return_value=False, 

300 ), 

301 ): 

302 cmd = builder.get_command("pytest", ToolName.PYTEST) 

303 assert_that(cmd).is_equal_to(["/usr/local/bin/pytest"]) 

304 

305 

306def test_pytest_builder_prefers_python_module_in_venv() -> None: 

307 """PytestBuilder prefers python -m pytest when tool is in venv scripts.""" 

308 builder = PytestBuilder() 

309 # Simulate running inside a venv with pytest present in venv scripts 

310 with ( 

311 patch( 

312 "shutil.which", 

313 _mock_which_for_venv(in_venv=True, expected_names="pytest"), 

314 ), 

315 patch( 

316 "lintro.tools.core.command_builders.sys.prefix", 

317 "/app/.venv", 

318 ), 

319 patch( 

320 "lintro.tools.core.command_builders.sys.base_prefix", 

321 "/usr/local", 

322 ), 

323 patch( 

324 "lintro.tools.core.command_builders._is_compiled_binary", 

325 return_value=False, 

326 ), 

327 patch( 

328 "lintro.tools.core.command_builders.sysconfig.get_path", 

329 return_value="/app/.venv/bin", 

330 ), 

331 ): 

332 cmd = builder.get_command("pytest", ToolName.PYTEST) 

333 # Should return [python_exe, "-m", "pytest"] when tool is in venv 

334 assert_that(cmd).is_length(3) 

335 assert_that(cmd[0]).is_equal_to(sys.executable) 

336 assert_that(cmd[1]).is_equal_to("-m") 

337 assert_that(cmd[2]).is_equal_to("pytest") 

338 

339 

340def test_pytest_builder_prefers_path_when_tool_not_in_venv() -> None: 

341 """PytestBuilder uses PATH when pytest is not in venv (Homebrew).""" 

342 builder = PytestBuilder() 

343 with ( 

344 patch( 

345 "shutil.which", 

346 _mock_which_for_venv( 

347 in_venv=False, 

348 in_path="/opt/homebrew/bin/pytest", 

349 expected_names="pytest", 

350 ), 

351 ), 

352 patch( 

353 "lintro.tools.core.command_builders.sys.prefix", 

354 "/opt/homebrew/Cellar/lintro/0.57.7/libexec", 

355 ), 

356 patch( 

357 "lintro.tools.core.command_builders.sys.base_prefix", 

358 "/opt/homebrew/Cellar/python@3.13/3.13.0/Frameworks", 

359 ), 

360 patch( 

361 "lintro.tools.core.command_builders._is_compiled_binary", 

362 return_value=False, 

363 ), 

364 patch( 

365 "lintro.tools.core.command_builders.sysconfig.get_path", 

366 return_value="/opt/homebrew/Cellar/lintro/0.57.7/libexec/bin", 

367 ), 

368 ): 

369 cmd = builder.get_command("pytest", ToolName.PYTEST) 

370 assert_that(cmd).is_equal_to(["/opt/homebrew/bin/pytest"]) 

371 

372 

373def test_pytest_builder_last_resort_python_m_in_venv() -> None: 

374 """PytestBuilder falls back to python -m when pytest nowhere.""" 

375 builder = PytestBuilder() 

376 with ( 

377 patch( 

378 "shutil.which", 

379 _mock_which_for_venv(in_venv=False, in_path=None, expected_names="pytest"), 

380 ), 

381 patch( 

382 "lintro.tools.core.command_builders.sys.prefix", 

383 "/opt/homebrew/Cellar/lintro/0.57.7/libexec", 

384 ), 

385 patch( 

386 "lintro.tools.core.command_builders.sys.base_prefix", 

387 "/opt/homebrew/Cellar/python@3.13/3.13.0/Frameworks", 

388 ), 

389 patch( 

390 "lintro.tools.core.command_builders._is_compiled_binary", 

391 return_value=False, 

392 ), 

393 patch( 

394 "lintro.tools.core.command_builders.sysconfig.get_path", 

395 return_value="/opt/homebrew/Cellar/lintro/0.57.7/libexec/bin", 

396 ), 

397 ): 

398 cmd = builder.get_command("pytest", ToolName.PYTEST) 

399 assert_that(cmd).is_length(3) 

400 assert_that(cmd[0]).is_equal_to(sys.executable) 

401 assert_that(cmd[1]).is_equal_to("-m") 

402 assert_that(cmd[2]).is_equal_to("pytest") 

403 

404 

405def test_pytest_builder_falls_back_to_python_module() -> None: 

406 """PytestBuilder falls back to python -m pytest when not in PATH.""" 

407 builder = PytestBuilder() 

408 with ( 

409 patch("shutil.which", return_value=None), 

410 patch( 

411 "lintro.tools.core.command_builders._is_compiled_binary", 

412 return_value=False, 

413 ), 

414 ): 

415 cmd = builder.get_command("pytest", ToolName.PYTEST) 

416 # Should return [python_exe, "-m", "pytest"] 

417 assert_that(cmd).is_length(3) 

418 assert_that(cmd[0]).is_equal_to(sys.executable) 

419 assert_that(cmd[1]).is_equal_to("-m") 

420 assert_that(cmd[2]).is_equal_to("pytest") 

421 

422 

423def test_pytest_builder_skips_python_module_when_compiled() -> None: 

424 """PytestBuilder skips python -m fallback when compiled.""" 

425 builder = PytestBuilder() 

426 with ( 

427 patch("shutil.which", return_value=None), 

428 patch( 

429 "lintro.tools.core.command_builders._is_compiled_binary", 

430 return_value=True, 

431 ), 

432 ): 

433 cmd = builder.get_command("pytest", ToolName.PYTEST) 

434 # Should return just ["pytest"] when compiled 

435 assert_that(cmd).is_equal_to(["pytest"]) 

436 

437 

438# ============================================================================= 

439# NodeJSBuilder tests 

440# ============================================================================= 

441 

442 

443def test_nodejs_builder_handles_markdownlint() -> None: 

444 """NodeJSBuilder can handle markdownlint.""" 

445 builder = NodeJSBuilder() 

446 assert_that(builder.can_handle(ToolName.MARKDOWNLINT)).is_true() 

447 

448 

449def test_nodejs_builder_handles_astro_check() -> None: 

450 """NodeJSBuilder can handle astro-check.""" 

451 builder = NodeJSBuilder() 

452 assert_that(builder.can_handle(ToolName.ASTRO_CHECK)).is_true() 

453 

454 

455def test_nodejs_builder_does_not_handle_ruff() -> None: 

456 """NodeJSBuilder does not handle Python tools.""" 

457 builder = NodeJSBuilder() 

458 assert_that(builder.can_handle(ToolName.RUFF)).is_false() 

459 

460 

461def test_nodejs_builder_uses_bunx_when_available() -> None: 

462 """NodeJSBuilder uses bunx when available.""" 

463 builder = NodeJSBuilder() 

464 with patch("shutil.which", return_value="/usr/local/bin/bunx"): 

465 cmd = builder.get_command("markdownlint", ToolName.MARKDOWNLINT) 

466 assert_that(cmd).is_equal_to(["bunx", "markdownlint-cli2"]) 

467 

468 

469def test_nodejs_builder_falls_back_to_package_name() -> None: 

470 """NodeJSBuilder falls back to package name when bunx not available.""" 

471 builder = NodeJSBuilder() 

472 with patch("shutil.which", return_value=None): 

473 cmd = builder.get_command("markdownlint", ToolName.MARKDOWNLINT) 

474 assert_that(cmd).is_equal_to(["markdownlint-cli2"]) 

475 

476 

477def test_nodejs_builder_astro_check_uses_astro_binary() -> None: 

478 """NodeJSBuilder resolves astro-check to astro binary.""" 

479 builder = NodeJSBuilder() 

480 with patch("shutil.which", return_value="/usr/local/bin/bunx"): 

481 cmd = builder.get_command("astro-check", ToolName.ASTRO_CHECK) 

482 assert_that(cmd).is_equal_to(["bunx", "astro"]) 

483 

484 

485def test_nodejs_builder_handles_vue_tsc() -> None: 

486 """NodeJSBuilder can handle vue-tsc.""" 

487 builder = NodeJSBuilder() 

488 assert_that(builder.can_handle(ToolName.VUE_TSC)).is_true() 

489 

490 

491def test_nodejs_builder_vue_tsc_uses_vue_tsc_binary() -> None: 

492 """NodeJSBuilder resolves vue-tsc to vue-tsc binary.""" 

493 builder = NodeJSBuilder() 

494 with patch("shutil.which", return_value="/usr/local/bin/bunx"): 

495 cmd = builder.get_command("vue-tsc", ToolName.VUE_TSC) 

496 assert_that(cmd).is_equal_to(["bunx", "vue-tsc"]) 

497 

498 

499# ============================================================================= 

500# CargoBuilder tests 

501# ============================================================================= 

502 

503 

504def test_cargo_builder_handles_clippy() -> None: 

505 """CargoBuilder can handle clippy.""" 

506 builder = CargoBuilder() 

507 assert_that(builder.can_handle(ToolName.CLIPPY)).is_true() 

508 

509 

510def test_cargo_builder_does_not_handle_ruff() -> None: 

511 """CargoBuilder does not handle Python tools.""" 

512 builder = CargoBuilder() 

513 assert_that(builder.can_handle(ToolName.RUFF)).is_false() 

514 

515 

516def test_cargo_builder_returns_cargo_clippy() -> None: 

517 """CargoBuilder returns ['cargo', 'clippy'] command.""" 

518 builder = CargoBuilder() 

519 cmd = builder.get_command("clippy", ToolName.CLIPPY) 

520 assert_that(cmd).is_equal_to(["cargo", "clippy"]) 

521 

522 

523def test_cargo_builder_handles_cargo_audit() -> None: 

524 """CargoBuilder can handle cargo_audit.""" 

525 builder = CargoBuilder() 

526 assert_that(builder.can_handle(ToolName.CARGO_AUDIT)).is_true() 

527 

528 

529def test_cargo_builder_returns_cargo_audit() -> None: 

530 """CargoBuilder returns ['cargo', 'audit'] command for cargo_audit.""" 

531 builder = CargoBuilder() 

532 cmd = builder.get_command("cargo_audit", ToolName.CARGO_AUDIT) 

533 assert_that(cmd).is_equal_to(["cargo", "audit"]) 

534 

535 

536# ============================================================================= 

537# StandaloneBuilder tests 

538# ============================================================================= 

539 

540 

541def test_standalone_builder_handles_hadolint() -> None: 

542 """StandaloneBuilder can handle hadolint.""" 

543 builder = StandaloneBuilder() 

544 assert_that(builder.can_handle(ToolName.HADOLINT)).is_true() 

545 

546 

547def test_standalone_builder_handles_actionlint() -> None: 

548 """StandaloneBuilder can handle actionlint.""" 

549 builder = StandaloneBuilder() 

550 assert_that(builder.can_handle(ToolName.ACTIONLINT)).is_true() 

551 

552 

553def test_standalone_builder_does_not_handle_ruff() -> None: 

554 """StandaloneBuilder does not handle Python bundled tools.""" 

555 builder = StandaloneBuilder() 

556 assert_that(builder.can_handle(ToolName.RUFF)).is_false() 

557 

558 

559def test_standalone_builder_returns_tool_name() -> None: 

560 """StandaloneBuilder returns just the tool name.""" 

561 builder = StandaloneBuilder() 

562 cmd = builder.get_command("hadolint", ToolName.HADOLINT) 

563 assert_that(cmd).is_equal_to(["hadolint"]) 

564 

565 

566# ============================================================================= 

567# CommandBuilderRegistry tests 

568# ============================================================================= 

569 

570 

571def test_registry_uses_first_matching_builder() -> None: 

572 """Registry returns command from first builder that can_handle().""" 

573 CommandBuilderRegistry.clear() 

574 

575 # Register a custom builder that handles ruff 

576 class CustomRuffBuilder(CommandBuilder): 

577 def can_handle(self, tool_name_enum: ToolName | None) -> bool: 

578 return tool_name_enum == ToolName.RUFF 

579 

580 def get_command( 

581 self, 

582 tool_name: str, 

583 tool_name_enum: ToolName | None, 

584 ) -> list[str]: 

585 return ["custom-ruff"] 

586 

587 CommandBuilderRegistry.register(CustomRuffBuilder()) 

588 CommandBuilderRegistry.register(PythonBundledBuilder()) 

589 

590 cmd = CommandBuilderRegistry.get_command("ruff", ToolName.RUFF) 

591 assert_that(cmd).is_equal_to(["custom-ruff"]) 

592 

593 

594def test_registry_fallback_to_tool_name() -> None: 

595 """Registry falls back to [tool_name] if no builder matches.""" 

596 CommandBuilderRegistry.clear() 

597 

598 cmd = CommandBuilderRegistry.get_command("unknown_tool", None) 

599 assert_that(cmd).is_equal_to(["unknown_tool"]) 

600 

601 

602def test_registry_is_registered() -> None: 

603 """Registry can check if a builder exists for a tool.""" 

604 CommandBuilderRegistry.clear() 

605 CommandBuilderRegistry.register(PythonBundledBuilder()) 

606 

607 assert_that(CommandBuilderRegistry.is_registered(ToolName.RUFF)).is_true() 

608 assert_that(CommandBuilderRegistry.is_registered(ToolName.MARKDOWNLINT)).is_false() 

609 

610 

611def test_registry_clear() -> None: 

612 """Registry clear removes all builders.""" 

613 CommandBuilderRegistry.clear() 

614 CommandBuilderRegistry.register(PythonBundledBuilder()) 

615 

616 assert_that(CommandBuilderRegistry._builders).is_length(1) 

617 

618 CommandBuilderRegistry.clear() 

619 assert_that(CommandBuilderRegistry._builders).is_empty()