Coverage for tests / unit / tools / shfmt / test_execution.py: 100%
54 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"""Unit tests for shfmt plugin execution."""
3from __future__ import annotations
5from pathlib import Path
6from typing import TYPE_CHECKING
7from unittest.mock import patch
9from assertpy import assert_that
11from lintro.tools.definitions.shfmt import ShfmtPlugin
13if TYPE_CHECKING:
14 pass
17# =============================================================================
18# Tests for ShfmtPlugin.check method
19# =============================================================================
22def test_check_with_mocked_subprocess_success(
23 shfmt_plugin: ShfmtPlugin,
24 tmp_path: Path,
25) -> None:
26 """Check returns success when no issues found.
28 Args:
29 shfmt_plugin: The ShfmtPlugin instance to test.
30 tmp_path: Temporary directory path for test files.
31 """
32 test_file = tmp_path / "test_script.sh"
33 test_file.write_text('#!/bin/bash\necho "hello"\n')
35 with patch(
36 "lintro.plugins.execution_preparation.verify_tool_version",
37 return_value=None,
38 ):
39 with patch.object(
40 shfmt_plugin,
41 "_run_subprocess",
42 return_value=(True, ""),
43 ):
44 result = shfmt_plugin.check([str(test_file)], {})
46 assert_that(result.success).is_true()
47 assert_that(result.issues_count).is_equal_to(0)
50def test_check_with_mocked_subprocess_issues(
51 shfmt_plugin: ShfmtPlugin,
52 tmp_path: Path,
53) -> None:
54 """Check returns issues when shfmt finds formatting problems.
56 Args:
57 shfmt_plugin: The ShfmtPlugin instance to test.
58 tmp_path: Temporary directory path for test files.
59 """
60 test_file = tmp_path / "test_script.sh"
61 test_file.write_text(
62 '#!/bin/bash\nif [ "$foo" = "bar" ]; then\necho "match"\nfi\n',
63 )
65 shfmt_diff_output = f"""--- {test_file}
66+++ {test_file}
67@@ -1,4 +1,4 @@
68 #!/bin/bash
69-if [ "$foo" = "bar" ]; then
70+if [ "$foo" = "bar" ]; then
71 echo "match"
72 fi"""
74 with patch(
75 "lintro.plugins.execution_preparation.verify_tool_version",
76 return_value=None,
77 ):
78 with patch.object(
79 shfmt_plugin,
80 "_run_subprocess",
81 return_value=(False, shfmt_diff_output),
82 ):
83 result = shfmt_plugin.check([str(test_file)], {})
85 assert_that(result.success).is_false()
86 assert_that(result.issues_count).is_greater_than(0)
89def test_check_with_no_shell_files(
90 shfmt_plugin: ShfmtPlugin,
91 tmp_path: Path,
92) -> None:
93 """Check returns success when no shell files found.
95 Args:
96 shfmt_plugin: The ShfmtPlugin instance to test.
97 tmp_path: Temporary directory path for test files.
98 """
99 non_sh_file = tmp_path / "test.txt"
100 non_sh_file.write_text("Not a shell file")
102 with patch.object(shfmt_plugin, "_verify_tool_version", return_value=None):
103 result = shfmt_plugin.check([str(non_sh_file)], {})
105 assert_that(result.success).is_true()
106 assert_that(result.output).contains("No")
109# =============================================================================
110# Tests for ShfmtPlugin.fix method
111# =============================================================================
114def test_fix_with_mocked_subprocess_success(
115 shfmt_plugin: ShfmtPlugin,
116 tmp_path: Path,
117) -> None:
118 """Fix returns success when fixes are applied.
120 Args:
121 shfmt_plugin: The ShfmtPlugin instance to test.
122 tmp_path: Temporary directory path for test files.
123 """
124 test_file = tmp_path / "test_script.sh"
125 test_file.write_text(
126 '#!/bin/bash\nif [ "$foo" = "bar" ]; then\necho "match"\nfi\n',
127 )
129 shfmt_diff_output = f"""--- {test_file}
130+++ {test_file}
131@@ -1,4 +1,4 @@
132 #!/bin/bash
133-if [ "$foo" = "bar" ]; then
134+if [ "$foo" = "bar" ]; then
135 echo "match"
136 fi"""
138 call_count = 0
140 def mock_run_subprocess(
141 cmd: list[str],
142 timeout: int,
143 cwd: str | None = None,
144 ) -> tuple[bool, str]:
145 """Mock subprocess that returns diff on check, success on fix.
147 Args:
148 cmd: Command list.
149 timeout: Timeout in seconds.
150 cwd: Working directory.
152 Returns:
153 Tuple of (success, output).
154 """
155 nonlocal call_count
156 call_count += 1
157 # First call is check with -d flag
158 if "-d" in cmd:
159 return (False, shfmt_diff_output)
160 # Second call is fix with -w flag
161 return (True, "")
163 with patch(
164 "lintro.plugins.execution_preparation.verify_tool_version",
165 return_value=None,
166 ):
167 with patch.object(
168 shfmt_plugin,
169 "_run_subprocess",
170 side_effect=mock_run_subprocess,
171 ):
172 result = shfmt_plugin.fix([str(test_file)], {})
174 assert_that(result.success).is_true()
175 assert_that(result.fixed_issues_count).is_greater_than(0)
176 # Verify the mock was called expected number of times (check + fix)
177 assert_that(call_count).is_equal_to(2)
180def test_fix_with_nothing_to_fix(
181 shfmt_plugin: ShfmtPlugin,
182 tmp_path: Path,
183) -> None:
184 """Fix returns success when no fixes needed.
186 Args:
187 shfmt_plugin: The ShfmtPlugin instance to test.
188 tmp_path: Temporary directory path for test files.
189 """
190 test_file = tmp_path / "test_script.sh"
191 test_file.write_text('#!/bin/bash\necho "hello"\n')
193 with patch(
194 "lintro.plugins.execution_preparation.verify_tool_version",
195 return_value=None,
196 ):
197 with patch.object(
198 shfmt_plugin,
199 "_run_subprocess",
200 return_value=(True, ""),
201 ):
202 result = shfmt_plugin.fix([str(test_file)], {})
204 assert_that(result.success).is_true()
205 assert_that(result.output).contains("No fixes needed")