Coverage for tests / cli / test_config_command.py: 100%

85 statements  

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

1"""Tests for the lintro config CLI command.""" 

2 

3from __future__ import annotations 

4 

5import json 

6from unittest.mock import MagicMock, patch 

7 

8import pytest 

9from assertpy import assert_that 

10from click.testing import CliRunner 

11 

12from lintro.cli import cli 

13from lintro.config.lintro_config import ( 

14 EnforceConfig, 

15 ExecutionConfig, 

16 LintroConfig, 

17 LintroToolConfig, 

18) 

19 

20 

21@pytest.fixture 

22def cli_runner() -> CliRunner: 

23 """Create a CLI runner for testing. 

24 

25 Returns: 

26 CliRunner: A Click test runner instance. 

27 """ 

28 return CliRunner() 

29 

30 

31@pytest.fixture 

32def mock_config() -> LintroConfig: 

33 """Create a mock LintroConfig for testing. 

34 

35 Returns: 

36 LintroConfig: A configured LintroConfig instance. 

37 """ 

38 return LintroConfig( 

39 execution=ExecutionConfig( 

40 enabled_tools=[], 

41 tool_order="priority", 

42 fail_fast=False, 

43 ), 

44 enforce=EnforceConfig( 

45 line_length=88, 

46 target_python="py313", 

47 ), 

48 tools={ 

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

50 "black": LintroToolConfig(enabled=True), 

51 }, 

52 config_path="/path/to/.lintro-config.yaml", 

53 ) 

54 

55 

56# ============================================================================= 

57# Tests for JSON output mode of config command 

58# ============================================================================= 

59 

60 

61@patch("lintro.cli_utils.commands.config.get_config") 

62@patch("lintro.cli_utils.commands.config.validate_config_consistency") 

63@patch("lintro.cli_utils.commands.config.is_tool_injectable") 

64def test_json_output_is_valid_json( 

65 mock_injectable: MagicMock, 

66 mock_validate: MagicMock, 

67 mock_get_config: MagicMock, 

68 mock_config: LintroConfig, 

69 cli_runner: CliRunner, 

70) -> None: 

71 """JSON output is valid JSON. 

72 

73 Args: 

74 mock_injectable: Mock for is_tool_injectable function. 

75 mock_validate: Mock for validate_config_consistency function. 

76 mock_get_config: Mock for get_config function. 

77 mock_config: Mock LintroConfig instance. 

78 cli_runner: Click test runner instance. 

79 """ 

80 mock_get_config.return_value = mock_config 

81 mock_validate.return_value = [] 

82 mock_injectable.return_value = True 

83 

84 result = cli_runner.invoke(cli, ["config", "--json"]) 

85 

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

87 # Should be valid JSON 

88 data = json.loads(result.output) 

89 assert_that(data).contains("global_settings") 

90 assert_that(data).contains("tool_configs") 

91 

92 

93@patch("lintro.cli_utils.commands.config.get_config") 

94@patch("lintro.cli_utils.commands.config.validate_config_consistency") 

95@patch("lintro.cli_utils.commands.config.is_tool_injectable") 

96def test_json_output_includes_line_length( 

97 mock_injectable: MagicMock, 

98 mock_validate: MagicMock, 

99 mock_get_config: MagicMock, 

100 mock_config: LintroConfig, 

101 cli_runner: CliRunner, 

102) -> None: 

103 """JSON output includes line length in global settings. 

104 

105 Args: 

106 mock_injectable: Mock for is_tool_injectable function. 

107 mock_validate: Mock for validate_config_consistency function. 

108 mock_get_config: Mock for get_config function. 

109 mock_config: Mock LintroConfig instance. 

110 cli_runner: Click test runner instance. 

111 """ 

112 mock_get_config.return_value = mock_config 

113 mock_validate.return_value = [] 

114 mock_injectable.return_value = True 

115 

116 result = cli_runner.invoke(cli, ["config", "--json"]) 

117 

118 data = json.loads(result.output) 

119 assert_that(data["global_settings"]["line_length"]).is_equal_to(88) 

120 

121 

122@patch("lintro.cli_utils.commands.config.get_config") 

123@patch("lintro.cli_utils.commands.config.validate_config_consistency") 

124@patch("lintro.cli_utils.commands.config.is_tool_injectable") 

125def test_json_output_includes_tool_order( 

126 mock_injectable: MagicMock, 

127 mock_validate: MagicMock, 

128 mock_get_config: MagicMock, 

129 mock_config: LintroConfig, 

130 cli_runner: CliRunner, 

131) -> None: 

132 """JSON output includes tool execution order. 

133 

134 Args: 

135 mock_injectable: Mock for is_tool_injectable function. 

136 mock_validate: Mock for validate_config_consistency function. 

137 mock_get_config: Mock for get_config function. 

138 mock_config: Mock LintroConfig instance. 

139 cli_runner: Click test runner instance. 

140 """ 

141 mock_get_config.return_value = mock_config 

142 mock_validate.return_value = [] 

143 mock_injectable.return_value = True 

144 

145 result = cli_runner.invoke(cli, ["config", "--json"]) 

146 

147 data = json.loads(result.output) 

148 # Should have tools in priority order (black before ruff) 

149 assert_that(data).contains("tool_execution_order") 

150 tool_names = [t["tool"] for t in data["tool_execution_order"]] 

151 assert_that(tool_names).contains("black") 

152 # Verify black comes before ruff (lower priority = runs first) 

153 assert_that(tool_names).contains("ruff") 

154 assert_that(tool_names.index("black")).is_less_than(tool_names.index("ruff")) 

155 

156 

157@patch("lintro.cli_utils.commands.config.get_config") 

158@patch("lintro.cli_utils.commands.config.validate_config_consistency") 

159@patch("lintro.cli_utils.commands.config.is_tool_injectable") 

160def test_json_output_includes_warnings( 

161 mock_injectable: MagicMock, 

162 mock_validate: MagicMock, 

163 mock_get_config: MagicMock, 

164 mock_config: LintroConfig, 

165 cli_runner: CliRunner, 

166) -> None: 

167 """JSON output includes configuration warnings. 

168 

169 Args: 

170 mock_injectable: Mock for is_tool_injectable function. 

171 mock_validate: Mock for validate_config_consistency function. 

172 mock_get_config: Mock for get_config function. 

173 mock_config: Mock LintroConfig instance. 

174 cli_runner: Click test runner instance. 

175 """ 

176 mock_get_config.return_value = mock_config 

177 mock_validate.return_value = ["black: Native config differs"] 

178 mock_injectable.return_value = True 

179 

180 result = cli_runner.invoke(cli, ["config", "--json"]) 

181 

182 data = json.loads(result.output) 

183 assert_that(data).contains("warnings") 

184 assert_that(len(data["warnings"])).is_greater_than(0) 

185 assert_that(data["warnings"][0]).contains("black") 

186 

187 

188@patch("lintro.cli_utils.commands.config.get_config") 

189@patch("lintro.cli_utils.commands.config.validate_config_consistency") 

190@patch("lintro.cli_utils.commands.config.is_tool_injectable") 

191def test_json_output_includes_config_source( 

192 mock_injectable: MagicMock, 

193 mock_validate: MagicMock, 

194 mock_get_config: MagicMock, 

195 mock_config: LintroConfig, 

196 cli_runner: CliRunner, 

197) -> None: 

198 """JSON output includes config_source field. 

199 

200 Args: 

201 mock_injectable: Mock for is_tool_injectable function. 

202 mock_validate: Mock for validate_config_consistency function. 

203 mock_get_config: Mock for get_config function. 

204 mock_config: Mock LintroConfig instance. 

205 cli_runner: Click test runner instance. 

206 """ 

207 mock_get_config.return_value = mock_config 

208 mock_validate.return_value = [] 

209 mock_injectable.return_value = True 

210 

211 result = cli_runner.invoke(cli, ["config", "--json"]) 

212 

213 data = json.loads(result.output) 

214 assert_that(data).contains("config_source") 

215 assert_that(data["config_source"]).contains(".lintro-config.yaml") 

216 

217 

218# ============================================================================= 

219# Tests for verbose mode of config command 

220# ============================================================================= 

221 

222 

223@patch("lintro.cli_utils.commands.config.get_config") 

224@patch("lintro.cli_utils.commands.config.validate_config_consistency") 

225@patch("lintro.cli_utils.commands.config.is_tool_injectable") 

226@patch("lintro.cli_utils.commands.config._load_native_tool_config") 

227def test_verbose_shows_native_config( 

228 mock_native_config: MagicMock, 

229 mock_injectable: MagicMock, 

230 mock_validate: MagicMock, 

231 mock_get_config: MagicMock, 

232 mock_config: LintroConfig, 

233 cli_runner: CliRunner, 

234) -> None: 

235 """Verbose mode shows native config column. 

236 

237 Args: 

238 mock_native_config: Mock for _load_native_tool_config function. 

239 mock_injectable: Mock for is_tool_injectable function. 

240 mock_validate: Mock for validate_config_consistency function. 

241 mock_get_config: Mock for get_config function. 

242 mock_config: Mock LintroConfig instance. 

243 cli_runner: Click test runner instance. 

244 """ 

245 mock_get_config.return_value = mock_config 

246 mock_validate.return_value = [] 

247 mock_injectable.return_value = False 

248 mock_native_config.return_value = {"printWidth": 100} 

249 

250 result = cli_runner.invoke(cli, ["config", "--verbose"]) 

251 

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

253 # Verbose should show Native Config column 

254 assert_that( 

255 "Native" in result.output or "native" in result.output.lower(), 

256 ).is_true()