Skip to content

Commit f55e9bd

Browse files
committed
add unit test for the new rules
Signed-off-by: tdruez <tdruez@aboutcode.org>
1 parent c7e9765 commit f55e9bd

2 files changed

Lines changed: 251 additions & 0 deletions

File tree

dje/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,7 @@ def get_exclude_candidates_fields(self):
11431143
field.related_model is Dataspace,
11441144
isinstance(field, models.AutoField),
11451145
isinstance(field, models.UUIDField),
1146+
getattr(field, "auto_now_add", False) or getattr(field, "auto_now", False),
11461147
not field.null and not field.blank and not field.has_default(),
11471148
field.name in ALWAYS_EXCLUDE,
11481149
]

policy/tests/test_rules.py

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
#
2+
# Copyright (c) nexB Inc. and others. All rights reserved.
3+
# DejaCode is a trademark of nexB Inc.
4+
# SPDX-License-Identifier: AGPL-3.0-only
5+
# See https://github.com/aboutcode-org/dejacode for support or download.
6+
# See https://aboutcode.org for more information about AboutCode FOSS projects.
7+
#
8+
9+
from datetime import timedelta
10+
11+
from django.contrib.contenttypes.models import ContentType
12+
from django.test import TestCase
13+
from django.utils import timezone
14+
15+
from component_catalog.models import Package
16+
from component_catalog.models import PackageAffectedByVulnerability
17+
from component_catalog.tests import make_package
18+
from dje.models import Dataspace
19+
from license_library.models import License
20+
from license_library.tests import make_license
21+
from policy.models import UsagePolicy
22+
from policy.rules import LicenseCoverageGapRule
23+
from policy.rules import LicensePolicyErrorRule
24+
from policy.rules import LicensePolicyWarningRule
25+
from policy.rules import StaleVulnerabilityRule
26+
from policy.rules import UnresolvedVulnerabilityCountRule
27+
from policy.rules import UsagePolicyErrorRule
28+
from policy.rules import UsagePolicyWarningRule
29+
from policy.rules import VulnerabilityDetectedRule
30+
from product_portfolio.tests import make_product
31+
from product_portfolio.tests import make_product_package
32+
from vulnerabilities.tests import make_vulnerability
33+
from vulnerabilities.tests import make_vulnerability_analysis
34+
35+
36+
class UsagePolicyErrorRuleTestCase(TestCase):
37+
def setUp(self):
38+
self.dataspace = Dataspace.objects.create(name="nexB")
39+
self.product = make_product(self.dataspace)
40+
self.policy = UsagePolicy.objects.create(
41+
label="Prohibited",
42+
icon="icon",
43+
content_type=ContentType.objects.get_for_model(Package),
44+
compliance_alert=UsagePolicy.Compliance.ERROR,
45+
dataspace=self.dataspace,
46+
)
47+
48+
def test_counts_packages_with_error_usage_policy(self):
49+
package = make_package(self.dataspace, usage_policy=self.policy)
50+
make_product_package(self.product, package=package)
51+
make_product_package(self.product, package=make_package(self.dataspace))
52+
count = UsagePolicyErrorRule().count_violations(self.product, 0, {})
53+
self.assertEqual(1, count)
54+
55+
def test_returns_zero_when_no_matching_packages(self):
56+
make_product_package(self.product, package=make_package(self.dataspace))
57+
count = UsagePolicyErrorRule().count_violations(self.product, 0, {})
58+
self.assertEqual(0, count)
59+
60+
61+
class UsagePolicyWarningRuleTestCase(TestCase):
62+
def setUp(self):
63+
self.dataspace = Dataspace.objects.create(name="nexB")
64+
self.product = make_product(self.dataspace)
65+
self.policy = UsagePolicy.objects.create(
66+
label="Restricted",
67+
icon="icon",
68+
content_type=ContentType.objects.get_for_model(Package),
69+
compliance_alert=UsagePolicy.Compliance.WARNING,
70+
dataspace=self.dataspace,
71+
)
72+
73+
def test_counts_packages_with_warning_usage_policy(self):
74+
package = make_package(self.dataspace, usage_policy=self.policy)
75+
make_product_package(self.product, package=package)
76+
count = UsagePolicyWarningRule().count_violations(self.product, 0, {})
77+
self.assertEqual(1, count)
78+
79+
80+
class LicensePolicyErrorRuleTestCase(TestCase):
81+
def setUp(self):
82+
self.dataspace = Dataspace.objects.create(name="nexB")
83+
self.product = make_product(self.dataspace)
84+
self.policy = UsagePolicy.objects.create(
85+
label="Prohibited",
86+
icon="icon",
87+
content_type=ContentType.objects.get_for_model(License),
88+
compliance_alert=UsagePolicy.Compliance.ERROR,
89+
dataspace=self.dataspace,
90+
)
91+
92+
def test_counts_packages_with_license_error_policy(self):
93+
license_ = make_license(self.dataspace, key="gpl-3.0", usage_policy=self.policy)
94+
package = make_package(self.dataspace)
95+
package.licenses.add(license_, through_defaults={"dataspace": self.dataspace})
96+
make_product_package(self.product, package=package)
97+
count = LicensePolicyErrorRule().count_violations(self.product, 0, {})
98+
self.assertEqual(1, count)
99+
100+
def test_returns_zero_when_license_has_no_policy(self):
101+
license_ = make_license(self.dataspace, key="mit")
102+
package = make_package(self.dataspace)
103+
package.licenses.add(license_, through_defaults={"dataspace": self.dataspace})
104+
make_product_package(self.product, package=package)
105+
count = LicensePolicyErrorRule().count_violations(self.product, 0, {})
106+
self.assertEqual(0, count)
107+
108+
109+
class LicensePolicyWarningRuleTestCase(TestCase):
110+
def setUp(self):
111+
self.dataspace = Dataspace.objects.create(name="nexB")
112+
self.product = make_product(self.dataspace)
113+
self.policy = UsagePolicy.objects.create(
114+
label="Restricted",
115+
icon="icon",
116+
content_type=ContentType.objects.get_for_model(License),
117+
compliance_alert=UsagePolicy.Compliance.WARNING,
118+
dataspace=self.dataspace,
119+
)
120+
121+
def test_counts_packages_with_license_warning_policy(self):
122+
license_ = make_license(self.dataspace, key="lgpl-2.1", usage_policy=self.policy)
123+
package = make_package(self.dataspace)
124+
package.licenses.add(license_, through_defaults={"dataspace": self.dataspace})
125+
make_product_package(self.product, package=package)
126+
count = LicensePolicyWarningRule().count_violations(self.product, 0, {})
127+
self.assertEqual(1, count)
128+
129+
130+
class LicenseCoverageGapRuleTestCase(TestCase):
131+
def setUp(self):
132+
self.dataspace = Dataspace.objects.create(name="nexB")
133+
self.product = make_product(self.dataspace)
134+
135+
def test_counts_packages_with_no_license_expression(self):
136+
make_product_package(
137+
self.product, package=make_package(self.dataspace, license_expression="")
138+
)
139+
make_product_package(
140+
self.product, package=make_package(self.dataspace, license_expression="mit")
141+
)
142+
count = LicenseCoverageGapRule().count_violations(self.product, 0, {})
143+
self.assertEqual(1, count)
144+
145+
def test_returns_zero_when_all_packages_have_license(self):
146+
make_product_package(
147+
self.product, package=make_package(self.dataspace, license_expression="mit")
148+
)
149+
count = LicenseCoverageGapRule().count_violations(self.product, 0, {})
150+
self.assertEqual(0, count)
151+
152+
153+
class VulnerabilityDetectedRuleTestCase(TestCase):
154+
def setUp(self):
155+
self.dataspace = Dataspace.objects.create(name="nexB")
156+
self.product = make_product(self.dataspace)
157+
158+
def test_counts_packages_with_any_vulnerability(self):
159+
package = make_package(self.dataspace)
160+
make_vulnerability(self.dataspace, affecting=package, risk_score=5.0)
161+
make_product_package(self.product, package=package)
162+
make_product_package(self.product, package=make_package(self.dataspace))
163+
count = VulnerabilityDetectedRule().count_violations(self.product, 0, {})
164+
self.assertEqual(1, count)
165+
166+
def test_min_risk_score_excludes_packages_below_threshold(self):
167+
package = make_package(self.dataspace)
168+
make_vulnerability(self.dataspace, affecting=package, risk_score=4.0)
169+
make_product_package(self.product, package=package)
170+
count = VulnerabilityDetectedRule().count_violations(
171+
self.product, 0, {"min_risk_score": 7.0}
172+
)
173+
self.assertEqual(0, count)
174+
175+
def test_min_risk_score_includes_packages_at_or_above_threshold(self):
176+
package = make_package(self.dataspace)
177+
make_vulnerability(self.dataspace, affecting=package, risk_score=9.0)
178+
make_product_package(self.product, package=package)
179+
count = VulnerabilityDetectedRule().count_violations(
180+
self.product, 0, {"min_risk_score": 7.0}
181+
)
182+
self.assertEqual(1, count)
183+
184+
185+
class UnresolvedVulnerabilityCountRuleTestCase(TestCase):
186+
def setUp(self):
187+
self.dataspace = Dataspace.objects.create(name="nexB")
188+
self.product = make_product(self.dataspace)
189+
190+
def test_counts_unanalyzed_package_vulnerability_links(self):
191+
package = make_package(self.dataspace)
192+
make_vulnerability(self.dataspace, affecting=package)
193+
make_product_package(self.product, package=package)
194+
count = UnresolvedVulnerabilityCountRule().count_violations(self.product, 0, {})
195+
self.assertEqual(1, count)
196+
197+
def test_does_not_count_links_with_terminal_analysis(self):
198+
package = make_package(self.dataspace)
199+
vulnerability = make_vulnerability(self.dataspace, affecting=package)
200+
product_package = make_product_package(self.product, package=package)
201+
make_vulnerability_analysis(product_package, vulnerability, state="resolved")
202+
count = UnresolvedVulnerabilityCountRule().count_violations(self.product, 0, {})
203+
self.assertEqual(0, count)
204+
205+
206+
class StaleVulnerabilityRuleTestCase(TestCase):
207+
def setUp(self):
208+
self.dataspace = Dataspace.objects.create(name="nexB")
209+
self.product = make_product(self.dataspace)
210+
211+
def test_counts_old_unresolved_high_risk_links(self):
212+
package = make_package(self.dataspace)
213+
vulnerability = make_vulnerability(self.dataspace, affecting=package, risk_score=9.0)
214+
make_product_package(self.product, package=package)
215+
old_date = timezone.now() - timedelta(days=60)
216+
PackageAffectedByVulnerability.objects.filter(
217+
package=package, vulnerability=vulnerability
218+
).update(detected_date=old_date)
219+
count = StaleVulnerabilityRule().count_violations(self.product, 0, {})
220+
self.assertEqual(1, count)
221+
222+
def test_does_not_count_recent_links(self):
223+
package = make_package(self.dataspace)
224+
make_vulnerability(self.dataspace, affecting=package, risk_score=9.0)
225+
make_product_package(self.product, package=package)
226+
count = StaleVulnerabilityRule().count_violations(self.product, 0, {})
227+
self.assertEqual(0, count)
228+
229+
def test_does_not_count_links_below_min_risk_score(self):
230+
package = make_package(self.dataspace)
231+
vulnerability = make_vulnerability(self.dataspace, affecting=package, risk_score=3.0)
232+
make_product_package(self.product, package=package)
233+
old_date = timezone.now() - timedelta(days=60)
234+
PackageAffectedByVulnerability.objects.filter(
235+
package=package, vulnerability=vulnerability
236+
).update(detected_date=old_date)
237+
count = StaleVulnerabilityRule().count_violations(self.product, 0, {})
238+
self.assertEqual(0, count)
239+
240+
def test_does_not_count_stale_links_with_terminal_analysis(self):
241+
package = make_package(self.dataspace)
242+
vulnerability = make_vulnerability(self.dataspace, affecting=package, risk_score=9.0)
243+
product_package = make_product_package(self.product, package=package)
244+
old_date = timezone.now() - timedelta(days=60)
245+
PackageAffectedByVulnerability.objects.filter(
246+
package=package, vulnerability=vulnerability
247+
).update(detected_date=old_date)
248+
make_vulnerability_analysis(product_package, vulnerability, state="resolved")
249+
count = StaleVulnerabilityRule().count_violations(self.product, 0, {})
250+
self.assertEqual(0, count)

0 commit comments

Comments
 (0)