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
« 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."""
3from __future__ import annotations
5from typing import Any
6from unittest.mock import patch
8import pytest
9from assertpy import assert_that
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
16# =============================================================================
17# Fixtures
18# =============================================================================
21class _FakeToolDefinition:
22 """Fake ToolDefinition for testing."""
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] = []
33class _FakeTool:
34 """Fake tool for testing."""
36 def __init__(self, name: str, can_fix: bool = True) -> None:
37 self._definition = _FakeToolDefinition(name=name, can_fix=can_fix)
39 @property
40 def definition(self) -> _FakeToolDefinition:
41 return self._definition
43 def set_options(self, **kwargs: Any) -> None:
44 pass
47@pytest.fixture(autouse=True)
48def _reset_config_cache() -> None:
49 """Reset config cache before each test."""
50 clear_config_cache()
53# =============================================================================
54# Disabled tools - check action
55# =============================================================================
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
64 monkeypatch.setattr(
65 tool_manager,
66 "get_check_tools",
67 lambda: ["ruff", "mypy", "bandit"],
68 )
70 config = LintroConfig(
71 tools={"mypy": LintroToolConfig(enabled=False)},
72 )
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")
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")
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
94 monkeypatch.setattr(
95 tool_manager,
96 "get_fix_tools",
97 lambda: ["ruff", "black", "prettier"],
98 )
100 config = LintroConfig(
101 tools={"black": LintroToolConfig(enabled=False)},
102 )
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")
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")
116# =============================================================================
117# Disabled tools - explicit selection
118# =============================================================================
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
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 )
138 config = LintroConfig(
139 tools={"mypy": LintroToolConfig(enabled=False)},
140 )
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")
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")
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
160 monkeypatch.setattr(
161 tool_manager,
162 "get_check_tools",
163 lambda: ["ruff", "mypy"],
164 )
166 config = LintroConfig(
167 tools={
168 "ruff": LintroToolConfig(enabled=False),
169 "mypy": LintroToolConfig(enabled=False),
170 },
171 )
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")
179 assert_that(result.to_run).is_empty()
180 assert_that(result.skipped).is_length(2)
183# =============================================================================
184# Enabled tools
185# =============================================================================
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
194 monkeypatch.setattr(
195 tool_manager,
196 "get_check_tools",
197 lambda: ["ruff", "mypy"],
198 )
200 config = LintroConfig(
201 tools={
202 "ruff": LintroToolConfig(enabled=True),
203 "mypy": LintroToolConfig(enabled=False),
204 },
205 )
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")
213 assert_that(result.to_run).is_equal_to(["ruff"])
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
222 monkeypatch.setattr(
223 tool_manager,
224 "get_check_tools",
225 lambda: ["ruff", "mypy", "bandit"],
226 )
228 config = LintroConfig(
229 tools={"mypy": LintroToolConfig(enabled=False)},
230 )
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")
238 assert_that(result.to_run).contains("ruff", "bandit")
239 assert_that(result.to_run).does_not_contain("mypy")
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
248 monkeypatch.setattr(
249 tool_manager,
250 "get_check_tools",
251 lambda: ["ruff", "mypy", "bandit"],
252 )
254 config = LintroConfig()
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")
262 assert_that(result.to_run).contains("ruff", "mypy", "bandit")
263 assert_that(result.skipped).is_empty()
266# =============================================================================
267# Execution enabled_tools filter
268# =============================================================================
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
277 monkeypatch.setattr(
278 tool_manager,
279 "get_check_tools",
280 lambda: ["ruff", "mypy", "bandit"],
281 )
283 config = LintroConfig(
284 execution=ExecutionConfig(enabled_tools=["ruff"]),
285 )
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")
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")
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
307 monkeypatch.setattr(
308 tool_manager,
309 "get_check_tools",
310 lambda: ["ruff", "mypy", "bandit"],
311 )
313 config = LintroConfig(
314 execution=ExecutionConfig(enabled_tools=["ruff", "mypy"]),
315 tools={"mypy": LintroToolConfig(enabled=False)},
316 )
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")
324 assert_that(result.to_run).is_equal_to(["ruff"])
327# =============================================================================
328# Fix action
329# =============================================================================
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
338 fake_ruff = _FakeTool(name="ruff", can_fix=True)
339 fake_black = _FakeTool(name="black", can_fix=True)
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}")
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 )
360 config = LintroConfig(
361 tools={"black": LintroToolConfig(enabled=False)},
362 )
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")
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")
376# =============================================================================
377# Case insensitivity
378# =============================================================================
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
387 monkeypatch.setattr(
388 tool_manager,
389 "get_check_tools",
390 lambda: ["ruff", "MyPy"],
391 )
393 config = LintroConfig(
394 tools={"mypy": LintroToolConfig(enabled=False)},
395 )
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")
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")