Coverage for tests / integration / tools / test_cargo_deny_integration.py: 92%

53 statements  

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

1"""Integration tests for cargo-deny tool definition. 

2 

3These tests require cargo-deny and cargo to be installed and available in PATH. 

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

5""" 

6 

7from __future__ import annotations 

8 

9import re 

10import shutil 

11import subprocess 

12from collections.abc import Callable 

13from pathlib import Path 

14from typing import TYPE_CHECKING 

15 

16import pytest 

17from assertpy import assert_that 

18from packaging.version import Version 

19 

20if TYPE_CHECKING: 

21 from lintro.plugins.base import BaseToolPlugin 

22 

23 

24def _get_cargo_deny_version() -> Version | None: 

25 """Get the installed cargo-deny version. 

26 

27 Returns: 

28 Version object or None if not installed or version cannot be determined. 

29 """ 

30 if shutil.which("cargo") is None: 

31 return None 

32 try: 

33 result = subprocess.run( 

34 ["cargo", "deny", "--version"], 

35 capture_output=True, 

36 text=True, 

37 timeout=10, 

38 ) 

39 match = re.search(r"(\d+\.\d+\.\d+)", result.stdout) 

40 if match: 

41 return Version(match.group(1)) 

42 except (subprocess.SubprocessError, ValueError): 

43 pass 

44 return None 

45 

46 

47_CARGO_DENY_MIN_VERSION = Version("0.14.0") 

48_installed_version = _get_cargo_deny_version() 

49 

50# Skip all tests if cargo-deny is not installed or version is below minimum 

51pytestmark = pytest.mark.skipif( 

52 shutil.which("cargo") is None 

53 or _installed_version is None 

54 or _installed_version < _CARGO_DENY_MIN_VERSION, 

55 reason=f"cargo-deny >= {_CARGO_DENY_MIN_VERSION} or cargo not installed " 

56 f"(found: {_installed_version})", 

57) 

58 

59 

60# --- Tests for CargoDenyPlugin definition --- 

61 

62 

63@pytest.mark.parametrize( 

64 ("attr", "expected"), 

65 [ 

66 ("name", "cargo_deny"), 

67 ("can_fix", False), 

68 ], 

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

70) 

71def test_definition_attributes( 

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

73 attr: str, 

74 expected: object, 

75) -> None: 

76 """Verify CargoDenyPlugin definition has correct attribute values. 

77 

78 Tests that the plugin definition exposes the expected values for 

79 name and can_fix attributes. 

80 

81 Args: 

82 get_plugin: Fixture factory to get plugin instances. 

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

84 expected: The expected value of the attribute. 

85 """ 

86 plugin = get_plugin("cargo_deny") 

87 assert_that(getattr(plugin.definition, attr)).is_equal_to(expected) 

88 

89 

90def test_definition_file_patterns( 

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

92) -> None: 

93 """Verify CargoDenyPlugin definition includes Cargo file patterns. 

94 

95 Tests that the plugin is configured to handle Cargo.toml and deny.toml. 

96 

97 Args: 

98 get_plugin: Fixture factory to get plugin instances. 

99 """ 

100 plugin = get_plugin("cargo_deny") 

101 assert_that(plugin.definition.file_patterns).contains("Cargo.toml") 

102 assert_that(plugin.definition.file_patterns).contains("deny.toml") 

103 

104 

105# --- Integration tests for cargo-deny check command --- 

106 

107 

108def test_check_empty_directory( 

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

110 tmp_path: Path, 

111) -> None: 

112 """Verify cargo-deny check handles empty directories gracefully. 

113 

114 Runs cargo-deny on an empty directory and verifies a result is returned 

115 with zero issues. 

116 

117 Args: 

118 get_plugin: Fixture factory to get plugin instances. 

119 tmp_path: Pytest fixture providing a temporary directory. 

120 """ 

121 plugin = get_plugin("cargo_deny") 

122 result = plugin.check([str(tmp_path)], {}) 

123 

124 assert_that(result).is_not_none() 

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

126 

127 

128def test_check_no_cargo_toml( 

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

130 tmp_path: Path, 

131) -> None: 

132 """Verify cargo-deny check handles directories without Cargo.toml. 

133 

134 Creates a directory with a deny.toml but no Cargo.toml and verifies 

135 cargo-deny skips gracefully. 

136 

137 Args: 

138 get_plugin: Fixture factory to get plugin instances. 

139 tmp_path: Pytest fixture providing a temporary directory. 

140 """ 

141 deny_toml = tmp_path / "deny.toml" 

142 deny_toml.write_text("[advisories]\n") 

143 

144 plugin = get_plugin("cargo_deny") 

145 result = plugin.check([str(deny_toml)], {}) 

146 

147 assert_that(result).is_not_none() 

148 assert_that(result.output).contains("No Cargo.toml found") 

149 

150 

151# --- Tests for CargoDenyPlugin.set_options method --- 

152 

153 

154@pytest.mark.parametrize( 

155 ("option_name", "option_value", "expected"), 

156 [ 

157 ("timeout", 30, 30), 

158 ("timeout", 60, 60), 

159 ("timeout", 120, 120), 

160 ], 

161 ids=[ 

162 "timeout_30", 

163 "timeout_60", 

164 "timeout_120", 

165 ], 

166) 

167def test_set_options_timeout( 

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

169 option_name: str, 

170 option_value: object, 

171 expected: object, 

172) -> None: 

173 """Verify CargoDenyPlugin.set_options correctly sets timeout. 

174 

175 Tests that plugin timeout option can be set and retrieved correctly. 

176 

177 Args: 

178 get_plugin: Fixture factory to get plugin instances. 

179 option_name: Name of the option to set. 

180 option_value: Value to set for the option. 

181 expected: Expected value when retrieving the option. 

182 """ 

183 plugin = get_plugin("cargo_deny") 

184 plugin.set_options(**{option_name: option_value}) 

185 assert_that(plugin.options.get(option_name)).is_equal_to(expected) 

186 

187 

188def test_invalid_timeout( 

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

190) -> None: 

191 """Verify CargoDenyPlugin.set_options rejects invalid timeout values. 

192 

193 Tests that invalid timeout values raise ValueError. 

194 

195 Args: 

196 get_plugin: Fixture factory to get plugin instances. 

197 """ 

198 plugin = get_plugin("cargo_deny") 

199 with pytest.raises(ValueError, match="must be positive"): 

200 plugin.set_options(timeout=-1)