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
« prev ^ index » next coverage.py v7.13.0, created at 2026-04-03 18:53 +0000
1"""Integration tests for cargo-deny tool definition.
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"""
7from __future__ import annotations
9import re
10import shutil
11import subprocess
12from collections.abc import Callable
13from pathlib import Path
14from typing import TYPE_CHECKING
16import pytest
17from assertpy import assert_that
18from packaging.version import Version
20if TYPE_CHECKING:
21 from lintro.plugins.base import BaseToolPlugin
24def _get_cargo_deny_version() -> Version | None:
25 """Get the installed cargo-deny version.
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
47_CARGO_DENY_MIN_VERSION = Version("0.14.0")
48_installed_version = _get_cargo_deny_version()
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)
60# --- Tests for CargoDenyPlugin definition ---
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.
78 Tests that the plugin definition exposes the expected values for
79 name and can_fix attributes.
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)
90def test_definition_file_patterns(
91 get_plugin: Callable[[str], BaseToolPlugin],
92) -> None:
93 """Verify CargoDenyPlugin definition includes Cargo file patterns.
95 Tests that the plugin is configured to handle Cargo.toml and deny.toml.
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")
105# --- Integration tests for cargo-deny check command ---
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.
114 Runs cargo-deny on an empty directory and verifies a result is returned
115 with zero issues.
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)], {})
124 assert_that(result).is_not_none()
125 assert_that(result.issues_count).is_equal_to(0)
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.
134 Creates a directory with a deny.toml but no Cargo.toml and verifies
135 cargo-deny skips gracefully.
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")
144 plugin = get_plugin("cargo_deny")
145 result = plugin.check([str(deny_toml)], {})
147 assert_that(result).is_not_none()
148 assert_that(result.output).contains("No Cargo.toml found")
151# --- Tests for CargoDenyPlugin.set_options method ---
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.
175 Tests that plugin timeout option can be set and retrieved correctly.
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)
188def test_invalid_timeout(
189 get_plugin: Callable[[str], BaseToolPlugin],
190) -> None:
191 """Verify CargoDenyPlugin.set_options rejects invalid timeout values.
193 Tests that invalid timeout values raise ValueError.
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)