Coverage for tests / unit / parsers / test_cargo_audit_parser.py: 100%
52 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"""Unit tests for cargo-audit parser."""
3from __future__ import annotations
5import pytest
6from assertpy import assert_that
8from lintro.parsers.cargo_audit.cargo_audit_parser import parse_cargo_audit_output
11@pytest.mark.parametrize(
12 ("output", "expected_count"),
13 [
14 pytest.param(None, 0, id="none_input"),
15 pytest.param("", 0, id="empty_string"),
16 pytest.param(" \n\n ", 0, id="whitespace_only"),
17 ],
18)
19def test_parse_cargo_audit_output_empty_cases(
20 output: str | None,
21 expected_count: int,
22) -> None:
23 """Parser returns empty list for empty/None input.
25 Args:
26 output: The input to parse.
27 expected_count: Expected number of issues.
28 """
29 result = parse_cargo_audit_output(output)
30 assert_that(result).is_length(expected_count)
33def test_parse_cargo_audit_output_no_vulnerabilities() -> None:
34 """Parser returns empty list when no vulnerabilities found."""
35 output = """{
36 "vulnerabilities": {
37 "count": 0,
38 "list": []
39 }
40 }"""
41 result = parse_cargo_audit_output(output)
43 assert_that(result).is_length(0)
46def test_parse_cargo_audit_output_single_vulnerability() -> None:
47 """Parser extracts single vulnerability correctly."""
48 output = """{
49 "vulnerabilities": {
50 "count": 1,
51 "list": [
52 {
53 "advisory": {
54 "id": "RUSTSEC-2021-0124",
55 "title": "Data race in crossbeam-deque",
56 "description": "A data race can occur in crossbeam-deque.",
57 "severity": "HIGH",
58 "url": "https://rustsec.org/advisories/RUSTSEC-2021-0124"
59 },
60 "package": {
61 "name": "crossbeam-deque",
62 "version": "0.7.3"
63 }
64 }
65 ]
66 }
67 }"""
68 result = parse_cargo_audit_output(output)
70 assert_that(result).is_length(1)
71 assert_that(result[0].advisory_id).is_equal_to("RUSTSEC-2021-0124")
72 assert_that(result[0].package_name).is_equal_to("crossbeam-deque")
73 assert_that(result[0].package_version).is_equal_to("0.7.3")
74 assert_that(result[0].severity).is_equal_to("HIGH")
75 assert_that(result[0].file).is_equal_to("Cargo.lock")
78def test_parse_cargo_audit_output_multiple_vulnerabilities() -> None:
79 """Parser handles multiple vulnerabilities."""
80 output = """{
81 "vulnerabilities": {
82 "count": 2,
83 "list": [
84 {
85 "advisory": {
86 "id": "RUSTSEC-2021-0001",
87 "title": "First vulnerability",
88 "severity": "MEDIUM"
89 },
90 "package": {
91 "name": "crate-a",
92 "version": "1.0.0"
93 }
94 },
95 {
96 "advisory": {
97 "id": "RUSTSEC-2022-0002",
98 "title": "Second vulnerability",
99 "severity": "CRITICAL"
100 },
101 "package": {
102 "name": "crate-b",
103 "version": "2.0.0"
104 }
105 }
106 ]
107 }
108 }"""
109 result = parse_cargo_audit_output(output)
111 assert_that(result).is_length(2)
112 assert_that(result[0].advisory_id).is_equal_to("RUSTSEC-2021-0001")
113 assert_that(result[0].severity).is_equal_to("MEDIUM")
114 assert_that(result[1].advisory_id).is_equal_to("RUSTSEC-2022-0002")
115 assert_that(result[1].severity).is_equal_to("CRITICAL")
118def test_parse_cargo_audit_output_normalizes_severity() -> None:
119 """Parser normalizes severity levels."""
120 output = """{
121 "vulnerabilities": {
122 "count": 1,
123 "list": [
124 {
125 "advisory": {
126 "id": "RUSTSEC-2021-0001",
127 "title": "Test",
128 "severity": "moderate"
129 },
130 "package": {
131 "name": "test",
132 "version": "1.0.0"
133 }
134 }
135 ]
136 }
137 }"""
138 result = parse_cargo_audit_output(output)
140 assert_that(result).is_length(1)
141 assert_that(result[0].severity).is_equal_to("MEDIUM")
144def test_parse_cargo_audit_output_none_severity() -> None:
145 """Parser handles RustSec 'none' severity level."""
146 output = """{
147 "vulnerabilities": {
148 "count": 1,
149 "list": [
150 {
151 "advisory": {
152 "id": "RUSTSEC-2021-0001",
153 "title": "Informational advisory",
154 "severity": "none"
155 },
156 "package": {
157 "name": "test",
158 "version": "1.0.0"
159 }
160 }
161 ]
162 }
163 }"""
164 result = parse_cargo_audit_output(output)
166 assert_that(result).is_length(1)
167 assert_that(result[0].severity).is_equal_to("LOW")
170def test_parse_cargo_audit_output_invalid_json() -> None:
171 """Parser handles invalid JSON gracefully."""
172 output = "{invalid json}"
173 result = parse_cargo_audit_output(output)
175 assert_that(result).is_length(0)
178def test_parse_cargo_audit_output_missing_advisory() -> None:
179 """Parser handles missing advisory data gracefully."""
180 output = """{
181 "vulnerabilities": {
182 "count": 1,
183 "list": [
184 {
185 "package": {
186 "name": "test",
187 "version": "1.0.0"
188 }
189 }
190 ]
191 }
192 }"""
193 result = parse_cargo_audit_output(output)
195 # Should skip entries without advisory data
196 assert_that(result).is_length(0)
199def test_parse_cargo_audit_output_json_with_extra_text() -> None:
200 """Parser extracts JSON from output with extra text."""
201 output = """Fetching advisory database...
202Loading Cargo.lock...
203{
204 "vulnerabilities": {
205 "count": 1,
206 "list": [
207 {
208 "advisory": {
209 "id": "RUSTSEC-2021-0001",
210 "title": "Test"
211 },
212 "package": {
213 "name": "test",
214 "version": "1.0.0"
215 }
216 }
217 ]
218 }
219}
220Done."""
221 result = parse_cargo_audit_output(output)
223 assert_that(result).is_length(1)
224 assert_that(result[0].advisory_id).is_equal_to("RUSTSEC-2021-0001")