Coverage for tests / unit / tools / pytest_tool / test_pytest_handlers.py: 100%

131 statements  

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

1"""Tests for lintro.tools.implementations.pytest.pytest_handlers module.""" 

2 

3from __future__ import annotations 

4 

5from typing import TYPE_CHECKING 

6from unittest.mock import MagicMock, patch 

7 

8from assertpy import assert_that 

9 

10from lintro.tools.implementations.pytest.pytest_handlers import ( 

11 handle_check_plugins, 

12 handle_collect_only, 

13 handle_fixture_info, 

14 handle_list_fixtures, 

15 handle_list_markers, 

16 handle_list_plugins, 

17 handle_parametrize_help, 

18) 

19 

20if TYPE_CHECKING: 

21 pass 

22 

23 

24def _make_mock_tool(name: str = "pytest") -> MagicMock: 

25 """Create a mock tool for testing. 

26 

27 Args: 

28 name: Tool name for mock. 

29 

30 Returns: 

31 MagicMock configured as a PytestPlugin. 

32 """ 

33 mock_tool = MagicMock() 

34 mock_tool.definition.name = name 

35 mock_tool._get_executable_command.return_value = ["pytest"] 

36 return mock_tool 

37 

38 

39# ============================================================================= 

40# handle_list_plugins tests 

41# ============================================================================= 

42 

43 

44@patch("lintro.tools.implementations.pytest.pytest_handlers.get_pytest_version_info") 

45@patch("lintro.tools.implementations.pytest.pytest_handlers.list_installed_plugins") 

46def test_handle_list_plugins_with_plugins( 

47 mock_list: MagicMock, 

48 mock_version: MagicMock, 

49) -> None: 

50 """handle_list_plugins lists installed plugins. 

51 

52 Args: 

53 mock_list: Mock for list_installed_plugins. 

54 mock_version: Mock for get_pytest_version_info. 

55 """ 

56 mock_version.return_value = "pytest 7.4.0" 

57 mock_list.return_value = [ 

58 {"name": "pytest-cov", "version": "4.1.0"}, 

59 {"name": "pytest-mock", "version": "3.11.1"}, 

60 ] 

61 mock_tool = _make_mock_tool() 

62 

63 result = handle_list_plugins(mock_tool) 

64 

65 assert_that(result.success).is_true() 

66 assert_that(result.output).contains("pytest 7.4.0") 

67 assert_that(result.output).contains("Installed pytest plugins (2):") 

68 assert_that(result.output).contains("pytest-cov (4.1.0)") 

69 assert_that(result.output).contains("pytest-mock (3.11.1)") 

70 

71 

72@patch("lintro.tools.implementations.pytest.pytest_handlers.get_pytest_version_info") 

73@patch("lintro.tools.implementations.pytest.pytest_handlers.list_installed_plugins") 

74def test_handle_list_plugins_no_plugins( 

75 mock_list: MagicMock, 

76 mock_version: MagicMock, 

77) -> None: 

78 """handle_list_plugins handles no plugins case. 

79 

80 Args: 

81 mock_list: Mock for list_installed_plugins. 

82 mock_version: Mock for get_pytest_version_info. 

83 """ 

84 mock_version.return_value = "pytest 7.4.0" 

85 mock_list.return_value = [] 

86 mock_tool = _make_mock_tool() 

87 

88 result = handle_list_plugins(mock_tool) 

89 

90 assert_that(result.success).is_true() 

91 assert_that(result.output).contains("No pytest plugins found") 

92 

93 

94# ============================================================================= 

95# handle_check_plugins tests 

96# ============================================================================= 

97 

98 

99@patch("lintro.tools.implementations.pytest.pytest_handlers.check_plugin_installed") 

100def test_handle_check_plugins_all_installed(mock_check: MagicMock) -> None: 

101 """handle_check_plugins reports all installed. 

102 

103 Args: 

104 mock_check: Mock for check_plugin_installed. 

105 """ 

106 mock_check.return_value = True 

107 mock_tool = _make_mock_tool() 

108 

109 result = handle_check_plugins(mock_tool, "pytest-cov, pytest-mock") 

110 

111 assert_that(result.success).is_true() 

112 assert_that(result.output).contains("Installed plugins (2)") 

113 assert_that(result.issues_count).is_equal_to(0) 

114 

115 

116@patch("lintro.tools.implementations.pytest.pytest_handlers.check_plugin_installed") 

117def test_handle_check_plugins_some_missing(mock_check: MagicMock) -> None: 

118 """handle_check_plugins reports missing plugins. 

119 

120 Args: 

121 mock_check: Mock for check_plugin_installed. 

122 """ 

123 mock_check.side_effect = [True, False] 

124 mock_tool = _make_mock_tool() 

125 

126 result = handle_check_plugins(mock_tool, "pytest-cov, pytest-missing") 

127 

128 assert_that(result.success).is_false() 

129 assert_that(result.output).contains("Missing plugins (1)") 

130 assert_that(result.output).contains("pytest-missing") 

131 assert_that(result.issues_count).is_equal_to(1) 

132 

133 

134def test_handle_check_plugins_no_plugins_specified() -> None: 

135 """handle_check_plugins errors when no plugins specified.""" 

136 mock_tool = _make_mock_tool() 

137 

138 result = handle_check_plugins(mock_tool, None) 

139 

140 assert_that(result.success).is_false() 

141 assert_that(result.output).contains("required_plugins must be specified") 

142 

143 

144# ============================================================================= 

145# handle_collect_only tests 

146# ============================================================================= 

147 

148 

149def test_handle_collect_only_success() -> None: 

150 """handle_collect_only parses collected tests.""" 

151 mock_tool = _make_mock_tool() 

152 mock_tool._run_subprocess.return_value = ( 

153 True, 

154 "<Function test_one>\n<Function test_two>\ntest_file.py::test_three", 

155 ) 

156 

157 result = handle_collect_only(mock_tool, ["tests/"]) 

158 

159 assert_that(result.success).is_true() 

160 assert_that(result.output).contains("Collected") 

161 assert_that(result.output).contains("test_one") 

162 

163 

164def test_handle_collect_only_failure() -> None: 

165 """handle_collect_only handles subprocess failure.""" 

166 mock_tool = _make_mock_tool() 

167 mock_tool._run_subprocess.return_value = (False, "Error message") 

168 

169 result = handle_collect_only(mock_tool, ["tests/"]) 

170 

171 assert_that(result.success).is_false() 

172 assert_that(result.output).is_equal_to("Error message") 

173 

174 

175def test_handle_collect_only_exception() -> None: 

176 """handle_collect_only handles exceptions.""" 

177 mock_tool = _make_mock_tool() 

178 mock_tool._run_subprocess.side_effect = OSError("Command not found") 

179 

180 result = handle_collect_only(mock_tool, ["tests/"]) 

181 

182 assert_that(result.success).is_false() 

183 assert_that(result.output).contains("Error collecting tests") 

184 

185 

186# ============================================================================= 

187# handle_list_fixtures tests 

188# ============================================================================= 

189 

190 

191def test_handle_list_fixtures_success() -> None: 

192 """handle_list_fixtures returns fixture output.""" 

193 mock_tool = _make_mock_tool() 

194 mock_tool._run_subprocess.return_value = (True, "tmp_path\ncaplog\nmonkeypatch") 

195 

196 result = handle_list_fixtures(mock_tool, ["tests/"]) 

197 

198 assert_that(result.success).is_true() 

199 assert_that(result.output).contains("tmp_path") 

200 

201 

202def test_handle_list_fixtures_failure() -> None: 

203 """handle_list_fixtures handles failure.""" 

204 mock_tool = _make_mock_tool() 

205 mock_tool._run_subprocess.return_value = (False, "Error") 

206 

207 result = handle_list_fixtures(mock_tool, ["tests/"]) 

208 

209 assert_that(result.success).is_false() 

210 

211 

212def test_handle_list_fixtures_exception() -> None: 

213 """handle_list_fixtures handles exceptions.""" 

214 mock_tool = _make_mock_tool() 

215 mock_tool._run_subprocess.side_effect = ValueError("Bad value") 

216 

217 result = handle_list_fixtures(mock_tool, ["tests/"]) 

218 

219 assert_that(result.success).is_false() 

220 assert_that(result.output).contains("Error listing fixtures") 

221 

222 

223# ============================================================================= 

224# handle_fixture_info tests 

225# ============================================================================= 

226 

227 

228def test_handle_fixture_info_found() -> None: 

229 """handle_fixture_info finds fixture details.""" 

230 mock_tool = _make_mock_tool() 

231 mock_tool._run_subprocess.return_value = ( 

232 True, 

233 "tmp_path -- Provides temporary directory\n" 

234 " Scope: function\n" 

235 "caplog -- Captures logging output", 

236 ) 

237 

238 result = handle_fixture_info(mock_tool, "tmp_path", ["tests/"]) 

239 

240 assert_that(result.success).is_true() 

241 assert_that(result.output).contains("tmp_path") 

242 

243 

244def test_handle_fixture_info_not_found() -> None: 

245 """handle_fixture_info reports not found.""" 

246 mock_tool = _make_mock_tool() 

247 mock_tool._run_subprocess.return_value = (True, "other_fixture -- Some fixture") 

248 

249 result = handle_fixture_info(mock_tool, "nonexistent", ["tests/"]) 

250 

251 assert_that(result.success).is_false() 

252 assert_that(result.output).contains("not found") 

253 

254 

255def test_handle_fixture_info_exception() -> None: 

256 """handle_fixture_info handles exceptions.""" 

257 mock_tool = _make_mock_tool() 

258 mock_tool._run_subprocess.side_effect = RuntimeError("Runtime error") 

259 

260 result = handle_fixture_info(mock_tool, "tmp_path", ["tests/"]) 

261 

262 assert_that(result.success).is_false() 

263 assert_that(result.output).contains("Error getting fixture info") 

264 

265 

266# ============================================================================= 

267# handle_list_markers tests 

268# ============================================================================= 

269 

270 

271def test_handle_list_markers_success() -> None: 

272 """handle_list_markers returns markers output.""" 

273 mock_tool = _make_mock_tool() 

274 mock_tool._run_subprocess.return_value = ( 

275 True, 

276 "@pytest.mark.skip\n@pytest.mark.parametrize", 

277 ) 

278 

279 result = handle_list_markers(mock_tool) 

280 

281 assert_that(result.success).is_true() 

282 assert_that(result.output).contains("pytest.mark") 

283 

284 

285def test_handle_list_markers_failure() -> None: 

286 """handle_list_markers handles failure.""" 

287 mock_tool = _make_mock_tool() 

288 mock_tool._run_subprocess.return_value = (False, "Error") 

289 

290 result = handle_list_markers(mock_tool) 

291 

292 assert_that(result.success).is_false() 

293 

294 

295def test_handle_list_markers_exception() -> None: 

296 """handle_list_markers handles exceptions.""" 

297 mock_tool = _make_mock_tool() 

298 mock_tool._run_subprocess.side_effect = OSError("Error") 

299 

300 result = handle_list_markers(mock_tool) 

301 

302 assert_that(result.success).is_false() 

303 assert_that(result.output).contains("Error listing markers") 

304 

305 

306# ============================================================================= 

307# handle_parametrize_help tests 

308# ============================================================================= 

309 

310 

311def test_handle_parametrize_help_returns_help_text() -> None: 

312 """handle_parametrize_help returns help text.""" 

313 mock_tool = _make_mock_tool() 

314 

315 result = handle_parametrize_help(mock_tool) 

316 

317 assert_that(result.success).is_true() 

318 assert_that(result.output).contains("Parametrization Help") 

319 assert_that(result.output).contains("@pytest.mark.parametrize") 

320 assert_that(result.output).contains("Example:")