Coverage for tests / unit / parsers / test_shfmt_parser.py: 100%

101 statements  

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

1"""Unit tests for shfmt parser.""" 

2 

3from __future__ import annotations 

4 

5import pytest 

6from assertpy import assert_that 

7 

8from lintro.parsers.shfmt.shfmt_parser import parse_shfmt_output 

9 

10 

11@pytest.mark.parametrize( 

12 "output", 

13 [ 

14 None, 

15 "", 

16 " \n \n ", 

17 ], 

18 ids=["none", "empty", "whitespace_only"], 

19) 

20def test_parse_shfmt_output_returns_empty_for_no_content( 

21 output: str | None, 

22) -> None: 

23 """Parse empty, None, or whitespace-only output returns empty list. 

24 

25 Args: 

26 output: The shfmt output to parse. 

27 """ 

28 result = parse_shfmt_output(output) 

29 assert_that(result).is_empty() 

30 

31 

32def test_parse_shfmt_output_single_file_diff() -> None: 

33 """Parse a single file diff output into one issue.""" 

34 output = """--- script.sh.orig 

35+++ script.sh 

36@@ -1,3 +1,3 @@ 

37-if [ "$foo" = "bar" ]; then 

38+if [ "$foo" = "bar" ]; then 

39 echo "match" 

40 fi""" 

41 result = parse_shfmt_output(output) 

42 assert_that(result).is_length(1) 

43 assert_that(result[0].file).is_equal_to("script.sh") 

44 assert_that(result[0].line).is_equal_to(1) 

45 assert_that(result[0].message).is_equal_to("Needs formatting") 

46 assert_that(result[0].fixable).is_true() 

47 assert_that(result[0].diff_content).contains("if [ ") 

48 assert_that(result[0].diff_content).contains("if [ ") 

49 

50 

51def test_parse_shfmt_output_multiple_files() -> None: 

52 """Parse diff output with multiple files.""" 

53 output = """--- script1.sh.orig 

54+++ script1.sh 

55@@ -1,2 +1,2 @@ 

56-echo "hello" 

57+echo "hello" 

58--- script2.sh.orig 

59+++ script2.sh 

60@@ -5,2 +5,2 @@ 

61-if[ "$x" ]; then 

62+if [ "$x" ]; then""" 

63 result = parse_shfmt_output(output) 

64 assert_that(result).is_length(2) 

65 assert_that(result[0].file).is_equal_to("script1.sh") 

66 assert_that(result[0].line).is_equal_to(1) 

67 assert_that(result[1].file).is_equal_to("script2.sh") 

68 assert_that(result[1].line).is_equal_to(5) 

69 

70 

71def test_parse_shfmt_output_file_with_path() -> None: 

72 """Parse diff with directory path in filename.""" 

73 output = """--- scripts/deploy/setup.sh.orig 

74+++ scripts/deploy/setup.sh 

75@@ -10,2 +10,2 @@ 

76-echo "deploying" 

77+echo "deploying" 

78""" 

79 result = parse_shfmt_output(output) 

80 assert_that(result).is_length(1) 

81 assert_that(result[0].file).is_equal_to("scripts/deploy/setup.sh") 

82 assert_that(result[0].line).is_equal_to(10) 

83 

84 

85def test_parse_shfmt_output_extracts_diff_content() -> None: 

86 """Verify diff content is captured correctly.""" 

87 output = """--- test.sh.orig 

88+++ test.sh 

89@@ -1,5 +1,5 @@ 

90 #!/bin/bash 

91-x=1+2 

92+x=1 + 2 

93 echo $x 

94""" 

95 result = parse_shfmt_output(output) 

96 assert_that(result).is_length(1) 

97 assert_that(result[0].diff_content).contains("--- test.sh.orig") 

98 assert_that(result[0].diff_content).contains("+++ test.sh") 

99 assert_that(result[0].diff_content).contains("-x=1+2") 

100 assert_that(result[0].diff_content).contains("+x=1 + 2") 

101 

102 

103def test_parse_shfmt_output_multiple_hunks_uses_first_line() -> None: 

104 """When multiple hunks exist, use the first hunk's line number.""" 

105 output = """--- multi.sh.orig 

106+++ multi.sh 

107@@ -3,2 +3,2 @@ 

108-echo "first" 

109+echo "first" 

110@@ -10,2 +10,2 @@ 

111-echo "second" 

112+echo "second" 

113""" 

114 result = parse_shfmt_output(output) 

115 assert_that(result).is_length(1) 

116 # Should use line 3 from first hunk 

117 assert_that(result[0].line).is_equal_to(3) 

118 

119 

120def test_parse_shfmt_output_column_is_zero() -> None: 

121 """Column is always 0 (shfmt doesn't provide column info).""" 

122 output = """--- test.sh.orig 

123+++ test.sh 

124@@ -1 +1 @@ 

125-echo "test" 

126+echo "test" 

127""" 

128 result = parse_shfmt_output(output) 

129 assert_that(result[0].column).is_equal_to(0) 

130 

131 

132def test_parse_shfmt_output_fixable_is_true() -> None: 

133 """All shfmt issues are fixable.""" 

134 output = """--- test.sh.orig 

135+++ test.sh 

136@@ -1 +1 @@ 

137-echo "test" 

138+echo "test" 

139""" 

140 result = parse_shfmt_output(output) 

141 assert_that(result[0].fixable).is_true() 

142 

143 

144def test_parse_shfmt_output_no_orig_suffix() -> None: 

145 """Handle diff output without .orig suffix on --- line.""" 

146 output = """--- test.sh 

147+++ test.sh 

148@@ -1 +1 @@ 

149-echo "test" 

150+echo "test" 

151""" 

152 result = parse_shfmt_output(output) 

153 assert_that(result).is_length(1) 

154 assert_that(result[0].file).is_equal_to("test.sh") 

155 

156 

157# ============================================================================= 

158# Edge case tests 

159# ============================================================================= 

160 

161 

162def test_parse_shfmt_output_unicode_in_content() -> None: 

163 """Handle Unicode characters in diff content.""" 

164 output = """--- unicode.sh.orig 

165+++ unicode.sh 

166@@ -1 +1 @@ 

167-echo "Olá mundo" 

168+echo "Olá mundo" 

169""" 

170 result = parse_shfmt_output(output) 

171 assert_that(result).is_length(1) 

172 assert_that(result[0].diff_content).contains("Olá") 

173 

174 

175def test_parse_shfmt_output_file_path_with_spaces() -> None: 

176 """Handle file paths with spaces.""" 

177 output = """--- my scripts/test.sh.orig 

178+++ my scripts/test.sh 

179@@ -1 +1 @@ 

180-echo "test" 

181+echo "test" 

182""" 

183 result = parse_shfmt_output(output) 

184 assert_that(result).is_length(1) 

185 assert_that(result[0].file).is_equal_to("my scripts/test.sh") 

186 

187 

188def test_parse_shfmt_output_very_large_line_number() -> None: 

189 """Handle very large line numbers.""" 

190 output = """--- large.sh.orig 

191+++ large.sh 

192@@ -999999 +999999 @@ 

193-echo "test" 

194+echo "test" 

195""" 

196 result = parse_shfmt_output(output) 

197 assert_that(result).is_length(1) 

198 assert_that(result[0].line).is_equal_to(999999) 

199 

200 

201def test_parse_shfmt_output_deeply_nested_path() -> None: 

202 """Handle deeply nested file paths.""" 

203 deep_path = "a/b/c/d/e/f/g/h/i/j/script.sh" 

204 output = f"""--- {deep_path}.orig 

205+++ {deep_path} 

206@@ -1 +1 @@ 

207-echo "test" 

208+echo "test" 

209""" 

210 result = parse_shfmt_output(output) 

211 assert_that(result).is_length(1) 

212 assert_that(result[0].file).is_equal_to(deep_path) 

213 

214 

215def test_parse_shfmt_output_special_chars_in_content() -> None: 

216 """Handle special characters in diff content.""" 

217 output = """--- special.sh.orig 

218+++ special.sh 

219@@ -1 +1 @@ 

220-echo '$var' && echo "quote: \"nested\"" 

221+echo '$var' && echo "quote: \"nested\"" 

222""" 

223 result = parse_shfmt_output(output) 

224 assert_that(result).is_length(1) 

225 assert_that(result[0].diff_content).contains("$var") 

226 assert_that(result[0].diff_content).contains("nested") 

227 

228 

229def test_parse_shfmt_output_empty_hunk() -> None: 

230 """Handle diff with context-only hunk (no changes).""" 

231 # This is an edge case - shfmt wouldn't normally output this, 

232 # but parser should handle it gracefully 

233 output = """--- test.sh.orig 

234+++ test.sh 

235@@ -1,3 +1,3 @@ 

236 #!/bin/bash 

237 echo "unchanged" 

238 exit 0 

239""" 

240 result = parse_shfmt_output(output) 

241 assert_that(result).is_length(1) 

242 assert_that(result[0].file).is_equal_to("test.sh") 

243 # Line defaults to 1 from the hunk header (fallback behavior) 

244 assert_that(result[0].line).is_equal_to(1) 

245 

246 

247def test_parse_shfmt_output_bash_extension() -> None: 

248 """Parse file with .bash extension.""" 

249 output = """--- script.bash.orig 

250+++ script.bash 

251@@ -1 +1 @@ 

252-echo "test" 

253+echo "test" 

254""" 

255 result = parse_shfmt_output(output) 

256 assert_that(result).is_length(1) 

257 assert_that(result[0].file).is_equal_to("script.bash") 

258 

259 

260def test_parse_shfmt_output_ksh_extension() -> None: 

261 """Parse file with .ksh extension.""" 

262 output = """--- script.ksh.orig 

263+++ script.ksh 

264@@ -1 +1 @@ 

265-echo "test" 

266+echo "test" 

267""" 

268 result = parse_shfmt_output(output) 

269 assert_that(result).is_length(1) 

270 assert_that(result[0].file).is_equal_to("script.ksh")