Coverage for tests / unit / tools / executor / test_tool_configuration_enabled.py: 97%

138 statements  

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

1"""Tests for tool enabled/disabled functionality in get_tools_to_run.""" 

2 

3from __future__ import annotations 

4 

5from typing import Any 

6from unittest.mock import patch 

7 

8import pytest 

9from assertpy import assert_that 

10 

11from lintro.config.config_loader import clear_config_cache 

12from lintro.config.execution_config import ExecutionConfig 

13from lintro.config.lintro_config import LintroConfig, LintroToolConfig 

14from lintro.utils.execution.tool_configuration import get_tools_to_run 

15 

16# ============================================================================= 

17# Fixtures 

18# ============================================================================= 

19 

20 

21class _FakeToolDefinition: 

22 """Fake ToolDefinition for testing.""" 

23 

24 def __init__(self, name: str, can_fix: bool = True) -> None: 

25 self.name = name 

26 self.can_fix = can_fix 

27 self.description = "" 

28 self.file_patterns: list[str] = [] 

29 self.native_configs: list[str] = [] 

30 self.conflicts_with: list[str] = [] 

31 

32 

33class _FakeTool: 

34 """Fake tool for testing.""" 

35 

36 def __init__(self, name: str, can_fix: bool = True) -> None: 

37 self._definition = _FakeToolDefinition(name=name, can_fix=can_fix) 

38 

39 @property 

40 def definition(self) -> _FakeToolDefinition: 

41 return self._definition 

42 

43 def set_options(self, **kwargs: Any) -> None: 

44 pass 

45 

46 

47@pytest.fixture(autouse=True) 

48def _reset_config_cache() -> None: 

49 """Reset config cache before each test.""" 

50 clear_config_cache() 

51 

52 

53# ============================================================================= 

54# Disabled tools - check action 

55# ============================================================================= 

56 

57 

58def test_disabled_tool_skipped_in_all_tools_check( 

59 monkeypatch: pytest.MonkeyPatch, 

60) -> None: 

61 """Disabled tool should be skipped when using 'all' for check action.""" 

62 from lintro.tools import tool_manager 

63 

64 monkeypatch.setattr( 

65 tool_manager, 

66 "get_check_tools", 

67 lambda: ["ruff", "mypy", "bandit"], 

68 ) 

69 

70 config = LintroConfig( 

71 tools={"mypy": LintroToolConfig(enabled=False)}, 

72 ) 

73 

74 with patch( 

75 "lintro.utils.execution.tool_configuration.get_config", 

76 return_value=config, 

77 ): 

78 result = get_tools_to_run(tools=None, action="check") 

79 

80 assert_that(result.to_run).contains("ruff", "bandit") 

81 assert_that(result.to_run).does_not_contain("mypy") 

82 # Verify mypy appears in skipped list with reason 

83 skipped_by_name = {s.name: s for s in result.skipped} 

84 assert_that(skipped_by_name).contains_key("mypy") 

85 assert_that(skipped_by_name["mypy"].reason).is_equal_to("disabled in config") 

86 

87 

88def test_disabled_tool_skipped_in_all_tools_fix( 

89 monkeypatch: pytest.MonkeyPatch, 

90) -> None: 

91 """Disabled tool should be skipped when using 'all' for fix action.""" 

92 from lintro.tools import tool_manager 

93 

94 monkeypatch.setattr( 

95 tool_manager, 

96 "get_fix_tools", 

97 lambda: ["ruff", "black", "prettier"], 

98 ) 

99 

100 config = LintroConfig( 

101 tools={"black": LintroToolConfig(enabled=False)}, 

102 ) 

103 

104 with patch( 

105 "lintro.utils.execution.tool_configuration.get_config", 

106 return_value=config, 

107 ): 

108 result = get_tools_to_run(tools="all", action="fmt") 

109 

110 assert_that(result.to_run).contains("ruff", "prettier") 

111 assert_that(result.to_run).does_not_contain("black") 

112 skipped_names = [s.name for s in result.skipped] 

113 assert_that(skipped_names).contains("black") 

114 

115 

116# ============================================================================= 

117# Disabled tools - explicit selection 

118# ============================================================================= 

119 

120 

121def test_disabled_tool_skipped_when_explicitly_requested( 

122 monkeypatch: pytest.MonkeyPatch, 

123) -> None: 

124 """Disabled tool should be skipped even when explicitly requested.""" 

125 from lintro.tools import tool_manager 

126 

127 monkeypatch.setattr( 

128 tool_manager, 

129 "is_tool_registered", 

130 lambda name: name in ["ruff", "mypy"], 

131 ) 

132 monkeypatch.setattr( 

133 tool_manager, 

134 "get_tool_names", 

135 lambda: ["ruff", "mypy"], 

136 ) 

137 

138 config = LintroConfig( 

139 tools={"mypy": LintroToolConfig(enabled=False)}, 

140 ) 

141 

142 with patch( 

143 "lintro.utils.execution.tool_configuration.get_config", 

144 return_value=config, 

145 ): 

146 result = get_tools_to_run(tools="ruff,mypy", action="check") 

147 

148 assert_that(result.to_run).is_equal_to(["ruff"]) 

149 assert_that(result.to_run).does_not_contain("mypy") 

150 skipped_names = [s.name for s in result.skipped] 

151 assert_that(skipped_names).contains("mypy") 

152 

153 

154def test_all_tools_disabled_returns_empty_list( 

155 monkeypatch: pytest.MonkeyPatch, 

156) -> None: 

157 """When all available tools are disabled, return empty to_run list.""" 

158 from lintro.tools import tool_manager 

159 

160 monkeypatch.setattr( 

161 tool_manager, 

162 "get_check_tools", 

163 lambda: ["ruff", "mypy"], 

164 ) 

165 

166 config = LintroConfig( 

167 tools={ 

168 "ruff": LintroToolConfig(enabled=False), 

169 "mypy": LintroToolConfig(enabled=False), 

170 }, 

171 ) 

172 

173 with patch( 

174 "lintro.utils.execution.tool_configuration.get_config", 

175 return_value=config, 

176 ): 

177 result = get_tools_to_run(tools=None, action="check") 

178 

179 assert_that(result.to_run).is_empty() 

180 assert_that(result.skipped).is_length(2) 

181 

182 

183# ============================================================================= 

184# Enabled tools 

185# ============================================================================= 

186 

187 

188def test_enabled_tool_runs_normally( 

189 monkeypatch: pytest.MonkeyPatch, 

190) -> None: 

191 """Explicitly enabled tool should run normally.""" 

192 from lintro.tools import tool_manager 

193 

194 monkeypatch.setattr( 

195 tool_manager, 

196 "get_check_tools", 

197 lambda: ["ruff", "mypy"], 

198 ) 

199 

200 config = LintroConfig( 

201 tools={ 

202 "ruff": LintroToolConfig(enabled=True), 

203 "mypy": LintroToolConfig(enabled=False), 

204 }, 

205 ) 

206 

207 with patch( 

208 "lintro.utils.execution.tool_configuration.get_config", 

209 return_value=config, 

210 ): 

211 result = get_tools_to_run(tools=None, action="check") 

212 

213 assert_that(result.to_run).is_equal_to(["ruff"]) 

214 

215 

216def test_tool_not_in_config_defaults_to_enabled( 

217 monkeypatch: pytest.MonkeyPatch, 

218) -> None: 

219 """Tools not explicitly configured should default to enabled.""" 

220 from lintro.tools import tool_manager 

221 

222 monkeypatch.setattr( 

223 tool_manager, 

224 "get_check_tools", 

225 lambda: ["ruff", "mypy", "bandit"], 

226 ) 

227 

228 config = LintroConfig( 

229 tools={"mypy": LintroToolConfig(enabled=False)}, 

230 ) 

231 

232 with patch( 

233 "lintro.utils.execution.tool_configuration.get_config", 

234 return_value=config, 

235 ): 

236 result = get_tools_to_run(tools=None, action="check") 

237 

238 assert_that(result.to_run).contains("ruff", "bandit") 

239 assert_that(result.to_run).does_not_contain("mypy") 

240 

241 

242def test_empty_config_all_tools_enabled( 

243 monkeypatch: pytest.MonkeyPatch, 

244) -> None: 

245 """Empty config should have all tools enabled by default.""" 

246 from lintro.tools import tool_manager 

247 

248 monkeypatch.setattr( 

249 tool_manager, 

250 "get_check_tools", 

251 lambda: ["ruff", "mypy", "bandit"], 

252 ) 

253 

254 config = LintroConfig() 

255 

256 with patch( 

257 "lintro.utils.execution.tool_configuration.get_config", 

258 return_value=config, 

259 ): 

260 result = get_tools_to_run(tools=None, action="check") 

261 

262 assert_that(result.to_run).contains("ruff", "mypy", "bandit") 

263 assert_that(result.skipped).is_empty() 

264 

265 

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

267# Execution enabled_tools filter 

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

269 

270 

271def test_execution_enabled_tools_filter( 

272 monkeypatch: pytest.MonkeyPatch, 

273) -> None: 

274 """Only tools in execution.enabled_tools should run.""" 

275 from lintro.tools import tool_manager 

276 

277 monkeypatch.setattr( 

278 tool_manager, 

279 "get_check_tools", 

280 lambda: ["ruff", "mypy", "bandit"], 

281 ) 

282 

283 config = LintroConfig( 

284 execution=ExecutionConfig(enabled_tools=["ruff"]), 

285 ) 

286 

287 with patch( 

288 "lintro.utils.execution.tool_configuration.get_config", 

289 return_value=config, 

290 ): 

291 result = get_tools_to_run(tools=None, action="check") 

292 

293 assert_that(result.to_run).is_equal_to(["ruff"]) 

294 # mypy and bandit should be in skipped with "not in enabled_tools" reason 

295 skipped_names = [s.name for s in result.skipped] 

296 assert_that(skipped_names).contains("mypy", "bandit") 

297 for s in result.skipped: 

298 assert_that(s.reason).is_equal_to("not in enabled_tools") 

299 

300 

301def test_execution_enabled_tools_combined_with_tool_disabled( 

302 monkeypatch: pytest.MonkeyPatch, 

303) -> None: 

304 """Both execution.enabled_tools and tools.enabled should be checked.""" 

305 from lintro.tools import tool_manager 

306 

307 monkeypatch.setattr( 

308 tool_manager, 

309 "get_check_tools", 

310 lambda: ["ruff", "mypy", "bandit"], 

311 ) 

312 

313 config = LintroConfig( 

314 execution=ExecutionConfig(enabled_tools=["ruff", "mypy"]), 

315 tools={"mypy": LintroToolConfig(enabled=False)}, 

316 ) 

317 

318 with patch( 

319 "lintro.utils.execution.tool_configuration.get_config", 

320 return_value=config, 

321 ): 

322 result = get_tools_to_run(tools=None, action="check") 

323 

324 assert_that(result.to_run).is_equal_to(["ruff"]) 

325 

326 

327# ============================================================================= 

328# Fix action 

329# ============================================================================= 

330 

331 

332def test_disabled_tool_skipped_in_fix_action( 

333 monkeypatch: pytest.MonkeyPatch, 

334) -> None: 

335 """Disabled tool should be skipped for fix action.""" 

336 from lintro.tools import tool_manager 

337 

338 fake_ruff = _FakeTool(name="ruff", can_fix=True) 

339 fake_black = _FakeTool(name="black", can_fix=True) 

340 

341 def mock_get_tool(name: str) -> _FakeTool: 

342 if name == "ruff": 

343 return fake_ruff 

344 if name == "black": 

345 return fake_black 

346 raise ValueError(f"Unknown tool: {name}") 

347 

348 monkeypatch.setattr( 

349 tool_manager, 

350 "is_tool_registered", 

351 lambda name: name in ["ruff", "black"], 

352 ) 

353 monkeypatch.setattr(tool_manager, "get_tool", mock_get_tool) 

354 monkeypatch.setattr( 

355 tool_manager, 

356 "get_tool_names", 

357 lambda: ["ruff", "black"], 

358 ) 

359 

360 config = LintroConfig( 

361 tools={"black": LintroToolConfig(enabled=False)}, 

362 ) 

363 

364 with patch( 

365 "lintro.utils.execution.tool_configuration.get_config", 

366 return_value=config, 

367 ): 

368 result = get_tools_to_run(tools="ruff,black", action="fmt") 

369 

370 assert_that(result.to_run).is_equal_to(["ruff"]) 

371 assert_that(result.to_run).does_not_contain("black") 

372 skipped_names = [s.name for s in result.skipped] 

373 assert_that(skipped_names).contains("black") 

374 

375 

376# ============================================================================= 

377# Case insensitivity 

378# ============================================================================= 

379 

380 

381def test_disabled_tool_case_insensitive( 

382 monkeypatch: pytest.MonkeyPatch, 

383) -> None: 

384 """Tool enabled check should be case-insensitive.""" 

385 from lintro.tools import tool_manager 

386 

387 monkeypatch.setattr( 

388 tool_manager, 

389 "get_check_tools", 

390 lambda: ["ruff", "MyPy"], 

391 ) 

392 

393 config = LintroConfig( 

394 tools={"mypy": LintroToolConfig(enabled=False)}, 

395 ) 

396 

397 with patch( 

398 "lintro.utils.execution.tool_configuration.get_config", 

399 return_value=config, 

400 ): 

401 result = get_tools_to_run(tools=None, action="check") 

402 

403 assert_that(result.to_run).is_equal_to(["ruff"]) 

404 assert_that(result.to_run).does_not_contain("MyPy") 

405 assert_that(result.to_run).does_not_contain("mypy")