Coverage for tests / unit / cli / test_cli_lintro_group.py: 100%

143 statements  

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

1"""Tests for LintroGroup and CLI module functionality.""" 

2 

3from unittest.mock import patch 

4 

5from assertpy import assert_that 

6from click.testing import CliRunner 

7 

8from lintro.cli import cli 

9 

10 

11def test_format_commands_displays_canonical_names() -> None: 

12 """Test that format_commands displays canonical command names.""" 

13 runner = CliRunner() 

14 result = runner.invoke(cli, ["--help"]) 

15 assert_that(result.exit_code).is_equal_to(0) 

16 assert_that(result.output).contains("check") 

17 assert_that(result.output).contains("format") 

18 assert_that(result.output).contains("test") 

19 assert_that(result.output).contains("list-tools") 

20 

21 

22def test_format_commands_with_aliases() -> None: 

23 """Test that format_commands includes aliases in help.""" 

24 runner = CliRunner() 

25 result = runner.invoke(cli, ["--help"]) 

26 # The output should contain commands with or without aliases 

27 assert_that(result.exit_code).is_equal_to(0) 

28 # Check that the Commands table is displayed (Rich table format) 

29 assert_that(result.output).contains("Commands") 

30 assert_that(result.output).contains("Alias") 

31 

32 

33def test_check_command_help_displays() -> None: 

34 """Test that check command help displays properly.""" 

35 runner = CliRunner() 

36 result = runner.invoke(cli, ["check", "--help"]) 

37 assert_that(result.exit_code).is_equal_to(0) 

38 assert_that(result.output).contains("Check files for issues") 

39 

40 

41def test_format_command_help_displays() -> None: 

42 """Test that format command help displays properly.""" 

43 runner = CliRunner() 

44 result = runner.invoke(cli, ["format", "--help"]) 

45 assert_that(result.exit_code).is_equal_to(0) 

46 assert_that(result.output).contains("Format code") 

47 

48 

49def test_test_command_help_displays() -> None: 

50 """Test that test command help displays properly.""" 

51 runner = CliRunner() 

52 result = runner.invoke(cli, ["test", "--help"]) 

53 assert_that(result.exit_code).is_equal_to(0) 

54 assert_that(result.output).contains("Run tests") 

55 

56 

57def test_list_tools_command_help_displays() -> None: 

58 """Test that list-tools command help displays properly.""" 

59 runner = CliRunner() 

60 result = runner.invoke(cli, ["list-tools", "--help"]) 

61 assert_that(result.exit_code).is_equal_to(0) 

62 

63 

64def test_invoke_single_command_execution() -> None: 

65 """Test that invoke executes single command correctly.""" 

66 runner = CliRunner() 

67 with patch("lintro.cli_utils.commands.check.run_lint_tools_simple") as mock_run: 

68 mock_run.return_value = 0 

69 result = runner.invoke(cli, ["check", "."]) 

70 assert_that(result.exit_code).is_equal_to(0) 

71 mock_run.assert_called_once() 

72 

73 

74def test_invoke_with_comma_separated_commands() -> None: 

75 """Test that invoke handles comma-separated command chaining.""" 

76 runner = CliRunner() 

77 with ( 

78 patch("lintro.cli_utils.commands.check.run_lint_tools_simple") as mock_check, 

79 patch("lintro.cli_utils.commands.format.run_lint_tools_simple") as mock_fmt, 

80 ): 

81 mock_check.return_value = 0 

82 mock_fmt.return_value = 0 

83 result = runner.invoke(cli, ["check", ".", ",", "format", "."]) 

84 # Verify exit code is success 

85 assert_that(result.exit_code).is_equal_to(0) 

86 # Both commands should have been called exactly once 

87 assert_that(mock_check.call_count).is_equal_to(1) 

88 assert_that(mock_fmt.call_count).is_equal_to(1) 

89 # Verify both commands were called with the expected path argument 

90 mock_check.assert_any_call( 

91 action="check", 

92 paths=["."], 

93 tools=None, 

94 tool_options=None, 

95 exclude=None, 

96 include_venv=False, 

97 group_by="file", 

98 output_format="grid", 

99 verbose=False, 

100 raw_output=False, 

101 output_file=None, 

102 incremental=False, 

103 debug=False, 

104 stream=False, 

105 no_log=False, 

106 auto_install=False, 

107 yes=False, 

108 ai_fix=False, 

109 ignore_conflicts=False, 

110 ) 

111 mock_fmt.assert_any_call( 

112 action="fmt", 

113 paths=["."], 

114 tools=None, 

115 tool_options=None, 

116 exclude=None, 

117 include_venv=False, 

118 group_by="auto", 

119 output_format="grid", 

120 verbose=False, 

121 raw_output=False, 

122 output_file=None, 

123 debug=False, 

124 stream=False, 

125 no_log=False, 

126 auto_install=False, 

127 yes=False, 

128 ) 

129 

130 

131def test_invoke_aggregates_exit_codes_success() -> None: 

132 """Test that invoke aggregates exit codes from chained commands.""" 

133 runner = CliRunner() 

134 with patch("lintro.tools.tool_manager.get_all_tools") as mock_get: 

135 mock_get.return_value = {} 

136 result = runner.invoke(cli, ["list-tools"]) 

137 # Should return 0 when command succeeds 

138 assert_that(result.exit_code).is_equal_to(0) 

139 

140 

141def test_invoke_with_version_flag() -> None: 

142 """Test that invoke handles --version flag.""" 

143 runner = CliRunner() 

144 result = runner.invoke(cli, ["--version"]) 

145 assert_that(result.exit_code).is_equal_to(0) 

146 assert_that(result.output).contains("version") 

147 

148 

149def test_invoke_with_help_flag() -> None: 

150 """Test that invoke handles --help flag.""" 

151 runner = CliRunner() 

152 result = runner.invoke(cli, ["--help"]) 

153 assert_that(result.exit_code).is_equal_to(0) 

154 assert_that(result.output).contains("Lintro") 

155 

156 

157def test_invoke_without_command_shows_help() -> None: 

158 """Test that invoking cli without command succeeds.""" 

159 runner = CliRunner() 

160 result = runner.invoke(cli, []) 

161 # Should succeed (exit code 0) when invoked without command 

162 assert_that(result.exit_code).is_equal_to(0) 

163 

164 

165def test_invoke_with_invalid_command() -> None: 

166 """Test that invoke handles invalid commands.""" 

167 runner = CliRunner() 

168 result = runner.invoke(cli, ["invalid-command"]) 

169 assert_that(result.exit_code).is_not_equal_to(0) 

170 

171 

172def test_invoke_command_not_found() -> None: 

173 """Test error handling for non-existent command.""" 

174 runner = CliRunner() 

175 result = runner.invoke(cli, ["nonexistent"]) 

176 assert_that(result.exit_code).is_not_equal_to(0) 

177 assert_that(result.output).contains("No such command") 

178 

179 

180def test_chaining_ignores_empty_command_groups() -> None: 

181 """Test that chaining ignores empty command groups.""" 

182 runner = CliRunner() 

183 with patch("lintro.cli_utils.commands.check.run_lint_tools_simple") as mock_run: 

184 mock_run.return_value = 0 

185 # Multiple commas in a row would create empty groups 

186 result = runner.invoke(cli, ["check", ".", ",", ",", "check", "."]) 

187 # Should still execute the check commands, ignoring empty groups 

188 assert_that(result.exit_code).is_equal_to(0) 

189 assert_that(mock_run.call_count).is_equal_to(2) 

190 # Verify both calls were for the expected path 

191 assert_that(mock_run.call_args_list[0][1]["paths"]).is_equal_to(["."]) 

192 assert_that(mock_run.call_args_list[1][1]["paths"]).is_equal_to(["."]) 

193 

194 

195def test_chaining_with_flags() -> None: 

196 """Test that chaining preserves flags for each command.""" 

197 runner = CliRunner() 

198 with patch("lintro.cli_utils.commands.check.run_lint_tools_simple") as mock_run: 

199 mock_run.return_value = 0 

200 result = runner.invoke( 

201 cli, 

202 ["check", ".", "--tools", "ruff", ",", "check", "."], 

203 ) 

204 # Verify exit code is success 

205 assert_that(result.exit_code).is_equal_to(0) 

206 # Both commands should have been called 

207 assert_that(mock_run.call_count).is_equal_to(2) 

208 # Validate first call preserves the --tools flag 

209 assert_that(mock_run.call_args_list[0][1]["paths"]).is_equal_to(["."]) 

210 assert_that(mock_run.call_args_list[0][1]["tools"]).is_equal_to("ruff") 

211 # Validate second call reflects the second command invocation with default tools 

212 assert_that(mock_run.call_args_list[1][1]["paths"]).is_equal_to(["."]) 

213 assert_that(mock_run.call_args_list[1][1]["tools"]).is_none() 

214 

215 

216def test_invoke_with_realistic_comma_separated_inputs() -> None: 

217 """Test that invoke handles realistic comma-separated command inputs. 

218 

219 Tests inputs like fmt,chk. This test intentionally uses the single-token 

220 "fmt,chk" syntax (as opposed to the multi-argument comma syntax used elsewhere). 

221 """ 

222 runner = CliRunner() 

223 with ( 

224 patch("lintro.cli_utils.commands.format.run_lint_tools_simple") as mock_fmt, 

225 patch("lintro.cli_utils.commands.check.run_lint_tools_simple") as mock_check, 

226 ): 

227 mock_fmt.return_value = 0 

228 mock_check.return_value = 0 

229 # Test realistic input: fmt,chk (comma-separated in single token) 

230 result = runner.invoke(cli, ["fmt,chk", "."]) 

231 assert_that(result.exit_code).is_equal_to(0) 

232 # Both commands should have been called exactly once 

233 assert_that(mock_fmt.call_count).is_equal_to(1) 

234 assert_that(mock_check.call_count).is_equal_to(1) 

235 

236 

237def test_invoke_with_chk_tst_input() -> None: 

238 """Test that invoke handles chk,tst style input.""" 

239 runner = CliRunner() 

240 with ( 

241 patch("lintro.cli_utils.commands.check.run_lint_tools_simple") as mock_check, 

242 patch("lintro.cli_utils.commands.test.run_lint_tools_simple") as mock_test, 

243 ): 

244 mock_check.return_value = 0 

245 mock_test.return_value = 0 

246 # Test realistic input: chk,tst (comma-separated in single token) 

247 result = runner.invoke(cli, ["chk,tst", "."]) 

248 assert_that(result.exit_code).is_equal_to(0) 

249 # Both commands should have been called exactly once 

250 assert_that(mock_check.call_count).is_equal_to(1) 

251 assert_that(mock_test.call_count).is_equal_to(1) 

252 

253 

254def test_invoke_handles_system_exit() -> None: 

255 """Test that invoke properly handles SystemExit exceptions.""" 

256 runner = CliRunner() 

257 with patch("lintro.cli_utils.commands.check.run_lint_tools_simple") as mock_run: 

258 mock_run.side_effect = SystemExit(1) 

259 result = runner.invoke(cli, ["check", "."]) 

260 # Should handle SystemExit gracefully 

261 assert_that(isinstance(result.exception, SystemExit)).is_true() 

262 assert_that(result.exit_code).is_equal_to(1) 

263 mock_run.assert_called_once() 

264 

265 

266def test_invoke_preserves_max_exit_code() -> None: 

267 """Test that chained command execution preserves the max exit code.""" 

268 runner = CliRunner() 

269 with patch("lintro.cli_utils.commands.check.run_lint_tools_simple") as mock_run: 

270 # Simulate different exit codes from multiple runs 

271 mock_run.side_effect = [0, 1] 

272 result = runner.invoke(cli, ["check", ".", ",", "check", "."]) 

273 # Result should reflect the maximum exit code (1) 

274 assert_that(result.exit_code).is_equal_to(1) 

275 

276 

277def test_invoke_with_exception_in_command() -> None: 

278 """Test error handling when command raises exception.""" 

279 runner = CliRunner() 

280 with patch("lintro.cli_utils.commands.check.run_lint_tools_simple") as mock_run: 

281 mock_run.side_effect = Exception("Test error") 

282 result = runner.invoke(cli, ["check", "."]) 

283 # Should handle exceptions gracefully with non-zero exit code 

284 assert_that(result.exit_code).is_not_equal_to(0) 

285 # Verify the mocked function was called 

286 mock_run.assert_called_once()