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

64 statements  

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

1"""Integration tests for SQLFluff tool definition. 

2 

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

4They verify the SqlfluffPlugin definition, check command, fix command, 

5and set_options method. 

6""" 

7 

8from __future__ import annotations 

9 

10import shutil 

11from collections.abc import Callable 

12from pathlib import Path 

13from typing import TYPE_CHECKING 

14 

15import pytest 

16from assertpy import assert_that 

17 

18if TYPE_CHECKING: 

19 from lintro.plugins.base import BaseToolPlugin 

20 

21# Skip all tests if sqlfluff is not installed 

22pytestmark = pytest.mark.skipif( 

23 shutil.which("sqlfluff") is None, 

24 reason="sqlfluff not installed", 

25) 

26 

27 

28@pytest.fixture 

29def temp_sql_file_with_issues(tmp_path: Path) -> str: 

30 """Create a temporary SQL file with linting issues. 

31 

32 Creates a file containing SQL with deliberate linting issues 

33 that sqlfluff should detect, including: 

34 - Lowercase keywords 

35 - Missing aliases 

36 

37 Also creates a .sqlfluff config file with dialect=ansi since 

38 SQLFluff v3.0.0+ requires a dialect to be specified. 

39 

40 Args: 

41 tmp_path: Pytest fixture providing a temporary directory. 

42 

43 Returns: 

44 Path to the created file as a string. 

45 """ 

46 # Create .sqlfluff config with dialect (required in v3.0.0+) 

47 config_path = tmp_path / ".sqlfluff" 

48 config_path.write_text( 

49 """\ 

50[sqlfluff] 

51dialect = ansi 

52""", 

53 ) 

54 file_path = tmp_path / "query.sql" 

55 file_path.write_text( 

56 """\ 

57-- SQL file with linting issues 

58select id, name from users where id = 1; 

59SELECT u.id, u.name FROM users u; 

60""", 

61 ) 

62 return str(file_path) 

63 

64 

65@pytest.fixture 

66def temp_sql_file_clean(tmp_path: Path) -> str: 

67 """Create a temporary SQL file with no issues. 

68 

69 Creates a file containing properly formatted SQL that should pass 

70 sqlfluff checking with minimal issues. 

71 

72 Also creates a .sqlfluff config file with dialect=ansi since 

73 SQLFluff v3.0.0+ requires a dialect to be specified. 

74 

75 Args: 

76 tmp_path: Pytest fixture providing a temporary directory. 

77 

78 Returns: 

79 Path to the created file as a string. 

80 """ 

81 # Create .sqlfluff config with dialect (required in v3.0.0+) 

82 config_path = tmp_path / ".sqlfluff" 

83 config_path.write_text( 

84 """\ 

85[sqlfluff] 

86dialect = ansi 

87""", 

88 ) 

89 file_path = tmp_path / "clean.sql" 

90 file_path.write_text( 

91 """\ 

92-- Clean SQL file 

93SELECT 

94 id, 

95 name 

96FROM users 

97WHERE id = 1; 

98""", 

99 ) 

100 return str(file_path) 

101 

102 

103# --- Tests for SqlfluffPlugin definition --- 

104 

105 

106@pytest.mark.parametrize( 

107 ("attr", "expected"), 

108 [ 

109 ("name", "sqlfluff"), 

110 ("can_fix", True), 

111 ], 

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

113) 

114def test_definition_attributes( 

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

116 attr: str, 

117 expected: object, 

118) -> None: 

119 """Verify SqlfluffPlugin definition has correct attribute values. 

120 

121 Tests that the plugin definition exposes the expected values for 

122 name and can_fix attributes. 

123 

124 Args: 

125 get_plugin: Fixture factory to get plugin instances. 

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

127 expected: The expected value of the attribute. 

128 """ 

129 sqlfluff_plugin = get_plugin("sqlfluff") 

130 assert_that(getattr(sqlfluff_plugin.definition, attr)).is_equal_to(expected) 

131 

132 

133def test_definition_file_patterns( 

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

135) -> None: 

136 """Verify SqlfluffPlugin definition includes expected file patterns. 

137 

138 Tests that the plugin is configured to handle SQL files. 

139 

140 Args: 

141 get_plugin: Fixture factory to get plugin instances. 

142 """ 

143 sqlfluff_plugin = get_plugin("sqlfluff") 

144 assert_that(sqlfluff_plugin.definition.file_patterns).contains("*.sql") 

145 

146 

147def test_definition_tool_type( 

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

149) -> None: 

150 """Verify SqlfluffPlugin is both a linter and formatter. 

151 

152 Args: 

153 get_plugin: Fixture factory to get plugin instances. 

154 """ 

155 from lintro.enums.tool_type import ToolType 

156 

157 sqlfluff_plugin = get_plugin("sqlfluff") 

158 expected_type = ToolType.LINTER | ToolType.FORMATTER 

159 assert_that(sqlfluff_plugin.definition.tool_type).is_equal_to(expected_type) 

160 

161 

162# --- Integration tests for sqlfluff check command --- 

163 

164 

165def test_check_file_with_issues( 

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

167 temp_sql_file_with_issues: str, 

168) -> None: 

169 """Verify sqlfluff check detects linting issues in SQL files. 

170 

171 Runs sqlfluff on a file containing deliberate linting issues 

172 and verifies that issues are found. 

173 

174 Args: 

175 get_plugin: Fixture factory to get plugin instances. 

176 temp_sql_file_with_issues: Path to file with linting issues. 

177 """ 

178 sqlfluff_plugin = get_plugin("sqlfluff") 

179 result = sqlfluff_plugin.check([temp_sql_file_with_issues], {}) 

180 

181 assert_that(result).is_not_none() 

182 assert_that(result.name).is_equal_to("sqlfluff") 

183 # SQLFluff should detect at least one issue 

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

185 # success should be False when issues are detected 

186 assert_that(result.success).is_false() 

187 

188 

189def test_check_clean_file( 

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

191 temp_sql_file_clean: str, 

192) -> None: 

193 """Verify sqlfluff check handles clean files. 

194 

195 Runs sqlfluff on a properly formatted file and verifies the result. 

196 

197 Args: 

198 get_plugin: Fixture factory to get plugin instances. 

199 temp_sql_file_clean: Path to file with no issues. 

200 """ 

201 sqlfluff_plugin = get_plugin("sqlfluff") 

202 result = sqlfluff_plugin.check([temp_sql_file_clean], {}) 

203 

204 assert_that(result).is_not_none() 

205 assert_that(result.name).is_equal_to("sqlfluff") 

206 assert_that(result.success).is_true() 

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

208 

209 

210def test_check_empty_directory( 

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

212 tmp_path: Path, 

213) -> None: 

214 """Verify sqlfluff check handles empty directories gracefully. 

215 

216 Runs sqlfluff on an empty directory and verifies a result is returned 

217 without errors. 

218 

219 Args: 

220 get_plugin: Fixture factory to get plugin instances. 

221 tmp_path: Pytest fixture providing a temporary directory. 

222 """ 

223 sqlfluff_plugin = get_plugin("sqlfluff") 

224 result = sqlfluff_plugin.check([str(tmp_path)], {}) 

225 

226 assert_that(result).is_not_none() 

227 

228 

229# --- Integration tests for sqlfluff fix command --- 

230 

231 

232def test_fix_formats_sql_file( 

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

234 temp_sql_file_with_issues: str, 

235) -> None: 

236 """Verify sqlfluff fix formats SQL files. 

237 

238 Runs sqlfluff fix on a file with linting issues and verifies 

239 that the operation completes successfully. 

240 

241 Args: 

242 get_plugin: Fixture factory to get plugin instances. 

243 temp_sql_file_with_issues: Path to file with linting issues. 

244 """ 

245 sqlfluff_plugin = get_plugin("sqlfluff") 

246 result = sqlfluff_plugin.fix([temp_sql_file_with_issues], {}) 

247 

248 assert_that(result).is_not_none() 

249 assert_that(result.name).is_equal_to("sqlfluff") 

250 assert_that(result.success).is_true() 

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

252 

253 

254# --- Tests for SqlfluffPlugin.set_options method --- 

255 

256 

257@pytest.mark.parametrize( 

258 ("option_name", "option_value"), 

259 [ 

260 ("dialect", "ansi"), 

261 ("dialect", "postgres"), 

262 ("dialect", "mysql"), 

263 ("templater", "raw"), 

264 ("templater", "jinja"), 

265 ("exclude_rules", ["L010", "L014"]), 

266 ("rules", ["L001", "L002"]), 

267 ], 

268 ids=[ 

269 "dialect_ansi", 

270 "dialect_postgres", 

271 "dialect_mysql", 

272 "templater_raw", 

273 "templater_jinja", 

274 "exclude_rules", 

275 "rules", 

276 ], 

277) 

278def test_set_options( 

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

280 option_name: str, 

281 option_value: object, 

282) -> None: 

283 """Verify SqlfluffPlugin.set_options correctly sets various options. 

284 

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

286 

287 Args: 

288 get_plugin: Fixture factory to get plugin instances. 

289 option_name: Name of the option to set. 

290 option_value: Value to set for the option. 

291 """ 

292 sqlfluff_plugin = get_plugin("sqlfluff") 

293 sqlfluff_plugin.set_options(**{option_name: option_value}) 

294 assert_that(sqlfluff_plugin.options.get(option_name)).is_equal_to(option_value)