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

1"""Unit tests for cargo-audit parser.""" 

2 

3from __future__ import annotations 

4 

5import pytest 

6from assertpy import assert_that 

7 

8from lintro.parsers.cargo_audit.cargo_audit_parser import parse_cargo_audit_output 

9 

10 

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. 

24 

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) 

31 

32 

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) 

42 

43 assert_that(result).is_length(0) 

44 

45 

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) 

69 

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") 

76 

77 

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) 

110 

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") 

116 

117 

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) 

139 

140 assert_that(result).is_length(1) 

141 assert_that(result[0].severity).is_equal_to("MEDIUM") 

142 

143 

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) 

165 

166 assert_that(result).is_length(1) 

167 assert_that(result[0].severity).is_equal_to("LOW") 

168 

169 

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) 

174 

175 assert_that(result).is_length(0) 

176 

177 

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) 

194 

195 # Should skip entries without advisory data 

196 assert_that(result).is_length(0) 

197 

198 

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) 

222 

223 assert_that(result).is_length(1) 

224 assert_that(result[0].advisory_id).is_equal_to("RUSTSEC-2021-0001")