Coverage for tests / integration / tools / test_mypy_integration.py: 100%

46 statements  

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

1"""Integration tests for Mypy tool definition. 

2 

3These tests require mypy to be installed and available in PATH. 

4They verify the MypyPlugin definition, check command, and set_options method. 

5""" 

6 

7from __future__ import annotations 

8 

9import shutil 

10from collections.abc import Callable 

11from pathlib import Path 

12from typing import TYPE_CHECKING 

13 

14import pytest 

15from assertpy import assert_that 

16 

17if TYPE_CHECKING: 

18 from lintro.plugins.base import BaseToolPlugin 

19 

20# Skip all tests if mypy is not installed 

21pytestmark = pytest.mark.skipif( 

22 shutil.which("mypy") is None, 

23 reason="mypy not installed", 

24) 

25 

26 

27@pytest.fixture 

28def temp_python_file_with_type_errors(tmp_path: Path) -> str: 

29 """Create a temporary Python file with type errors. 

30 

31 Creates a file containing code with deliberate type annotation violations 

32 that mypy should detect, including: 

33 - Passing string arguments where int is expected 

34 - Assigning int to a str-typed variable 

35 

36 Args: 

37 tmp_path: Pytest fixture providing a temporary directory. 

38 

39 Returns: 

40 Path to the created file as a string. 

41 """ 

42 file_path = tmp_path / "type_errors.py" 

43 file_path.write_text( 

44 """\ 

45def add(a: int, b: int) -> int: 

46 return a + b 

47 

48# Type error: passing string instead of int 

49result: int = add("hello", "world") 

50 

51def greet(name: str) -> str: 

52 return "Hello, " + name 

53 

54# Type error: assigning int to str variable 

55greeting: str = 42 

56""", 

57 ) 

58 return str(file_path) 

59 

60 

61@pytest.fixture 

62def temp_python_file_type_correct(tmp_path: Path) -> str: 

63 """Create a temporary Python file with correct types. 

64 

65 Creates a file containing properly typed Python code that should 

66 pass mypy type checking without errors. 

67 

68 Args: 

69 tmp_path: Pytest fixture providing a temporary directory. 

70 

71 Returns: 

72 Path to the created file as a string. 

73 """ 

74 file_path = tmp_path / "type_correct.py" 

75 file_path.write_text( 

76 """\ 

77def add(a: int, b: int) -> int: 

78 return a + b 

79 

80 

81result: int = add(1, 2) 

82 

83 

84def greet(name: str) -> str: 

85 return "Hello, " + name 

86 

87 

88greeting: str = greet("World") 

89""", 

90 ) 

91 return str(file_path) 

92 

93 

94# --- Tests for MypyPlugin definition --- 

95 

96 

97@pytest.mark.parametrize( 

98 ("attr", "expected"), 

99 [ 

100 ("name", "mypy"), 

101 ("can_fix", False), 

102 ], 

103 ids=["name", "can_fix"], 

104) 

105def test_definition_attributes( 

106 get_plugin: Callable[[str], BaseToolPlugin], 

107 attr: str, 

108 expected: object, 

109) -> None: 

110 """Verify MypyPlugin definition has correct attribute values. 

111 

112 Tests that the plugin definition exposes the expected values for 

113 name and can_fix attributes. 

114 

115 Args: 

116 get_plugin: Fixture factory to get plugin instances. 

117 attr: The attribute name to check on the definition. 

118 expected: The expected value of the attribute. 

119 """ 

120 mypy_plugin = get_plugin("mypy") 

121 assert_that(getattr(mypy_plugin.definition, attr)).is_equal_to(expected) 

122 

123 

124def test_definition_file_patterns(get_plugin: Callable[[str], BaseToolPlugin]) -> None: 

125 """Verify MypyPlugin definition includes Python file patterns. 

126 

127 Tests that the plugin is configured to handle Python files (*.py). 

128 

129 Args: 

130 get_plugin: Fixture factory to get plugin instances. 

131 """ 

132 mypy_plugin = get_plugin("mypy") 

133 assert_that(mypy_plugin.definition.file_patterns).contains("*.py") 

134 

135 

136# --- Integration tests for mypy check command --- 

137 

138 

139def test_check_file_with_type_errors( 

140 get_plugin: Callable[[str], BaseToolPlugin], 

141 temp_python_file_with_type_errors: str, 

142) -> None: 

143 """Verify mypy check detects type errors in problematic files. 

144 

145 Runs mypy on a file containing deliberate type violations and verifies 

146 that issues are found. 

147 

148 Args: 

149 get_plugin: Fixture factory to get plugin instances. 

150 temp_python_file_with_type_errors: Path to file with type errors. 

151 """ 

152 mypy_plugin = get_plugin("mypy") 

153 result = mypy_plugin.check([temp_python_file_with_type_errors], {}) 

154 

155 assert_that(result).is_not_none() 

156 assert_that(result.name).is_equal_to("mypy") 

157 assert_that(result.issues_count).is_greater_than(0) 

158 

159 

160def test_check_type_correct_file( 

161 get_plugin: Callable[[str], BaseToolPlugin], 

162 temp_python_file_type_correct: str, 

163) -> None: 

164 """Verify mypy check passes on type-correct files. 

165 

166 Runs mypy on a properly typed file and verifies no issues are found. 

167 

168 Args: 

169 get_plugin: Fixture factory to get plugin instances. 

170 temp_python_file_type_correct: Path to file with correct types. 

171 """ 

172 mypy_plugin = get_plugin("mypy") 

173 result = mypy_plugin.check([temp_python_file_type_correct], {}) 

174 

175 assert_that(result).is_not_none() 

176 assert_that(result.name).is_equal_to("mypy") 

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

178 

179 

180def test_check_empty_directory( 

181 get_plugin: Callable[[str], BaseToolPlugin], 

182 tmp_path: Path, 

183) -> None: 

184 """Verify mypy check handles empty directories gracefully. 

185 

186 Runs mypy on an empty directory and verifies a result is returned 

187 without errors. 

188 

189 Args: 

190 get_plugin: Fixture factory to get plugin instances. 

191 tmp_path: Pytest fixture providing a temporary directory. 

192 """ 

193 mypy_plugin = get_plugin("mypy") 

194 result = mypy_plugin.check([str(tmp_path)], {}) 

195 

196 assert_that(result).is_not_none() 

197 

198 

199# --- Tests for MypyPlugin.set_options method --- 

200 

201 

202@pytest.mark.parametrize( 

203 ("option_name", "option_value", "expected"), 

204 [ 

205 ("strict", True, True), 

206 ("ignore_missing_imports", True, True), 

207 ], 

208 ids=["strict", "ignore_missing_imports"], 

209) 

210def test_set_options( 

211 get_plugin: Callable[[str], BaseToolPlugin], 

212 option_name: str, 

213 option_value: object, 

214 expected: object, 

215) -> None: 

216 """Verify MypyPlugin.set_options correctly sets various options. 

217 

218 Tests that plugin options can be set and retrieved correctly. 

219 

220 Args: 

221 get_plugin: Fixture factory to get plugin instances. 

222 option_name: Name of the option to set. 

223 option_value: Value to set for the option. 

224 expected: Expected value when retrieving the option. 

225 """ 

226 mypy_plugin = get_plugin("mypy") 

227 mypy_plugin.set_options(**{option_name: option_value}) 

228 assert_that(mypy_plugin.options.get(option_name)).is_equal_to(expected)