Skip to content

Commit 70d1500

Browse files
committed
add unit tests
Signed-off-by: tdruez <tdruez@aboutcode.org>
1 parent c70ac81 commit 70d1500

2 files changed

Lines changed: 146 additions & 3 deletions

File tree

product_portfolio/tests/test_importers.py

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
from product_portfolio.models import ProductPackage
4141
from product_portfolio.models import ProductRelationStatus
4242
from product_portfolio.models import ScanCodeProject
43+
from vulnerabilities.models import VulnerabilityAnalysis
4344

4445

4546
class ProductRelationImporterTestCase(TestCase):
@@ -1414,3 +1415,145 @@ def test_product_portfolio_import_packages_from_scio_importer_vex(
14141415
self.assertEqual("code_not_present", analysis.justification)
14151416
self.assertEqual("AAAA", analysis.detail)
14161417
self.assertEqual(["can_not_fix", "update"], analysis.responses)
1418+
1419+
@mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_project_dependencies")
1420+
@mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_project_packages")
1421+
def test_product_portfolio_import_packages_from_scio_importer_is_reachable(
1422+
self, mock_fetch_packages, mock_fetch_dependencies
1423+
):
1424+
def make_vulnerability_entry(advisory_id, is_reachable):
1425+
return {
1426+
"advisory_uid": f"github_osv/{advisory_id}",
1427+
"summary": "A vulnerability",
1428+
"is_reachable": is_reachable,
1429+
"cdx_vulnerability_data": {
1430+
"analysis": {"state": "in_triage", "detail": "Under review"},
1431+
},
1432+
}
1433+
1434+
mock_fetch_packages.return_value = [
1435+
{
1436+
"purl": "pkg:maven/abc/abc@1.0",
1437+
"type": "maven",
1438+
"namespace": "abc",
1439+
"name": "abc",
1440+
"version": "1.0",
1441+
"affected_by_vulnerabilities": [
1442+
make_vulnerability_entry("GHSA-yes", "yes"),
1443+
make_vulnerability_entry("GHSA-no", "no"),
1444+
make_vulnerability_entry("GHSA-unknown", "unknown"),
1445+
],
1446+
}
1447+
]
1448+
mock_fetch_dependencies.return_value = []
1449+
1450+
importer = ImportPackageFromScanCodeIO(
1451+
user=self.super_user,
1452+
project_uuid=uuid.uuid4(),
1453+
product=self.product1,
1454+
)
1455+
importer.save()
1456+
1457+
yes_analysis = VulnerabilityAnalysis.objects.get(
1458+
vulnerability__advisory_uid="github_osv/GHSA-yes"
1459+
)
1460+
no_analysis = VulnerabilityAnalysis.objects.get(
1461+
vulnerability__advisory_uid="github_osv/GHSA-no"
1462+
)
1463+
unknown_analysis = VulnerabilityAnalysis.objects.get(
1464+
vulnerability__advisory_uid="github_osv/GHSA-unknown"
1465+
)
1466+
1467+
self.assertTrue(yes_analysis.is_reachable)
1468+
self.assertFalse(no_analysis.is_reachable)
1469+
self.assertIsNone(unknown_analysis.is_reachable)
1470+
1471+
@mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_project_dependencies")
1472+
@mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_project_packages")
1473+
def test_product_portfolio_import_packages_from_scio_importer_is_reachable_not_overwritten(
1474+
self, mock_fetch_packages, mock_fetch_dependencies
1475+
):
1476+
mock_fetch_packages.return_value = [
1477+
{
1478+
"purl": "pkg:maven/abc/abc@1.0",
1479+
"type": "maven",
1480+
"namespace": "abc",
1481+
"name": "abc",
1482+
"version": "1.0",
1483+
"affected_by_vulnerabilities": [
1484+
{
1485+
"advisory_uid": "github_osv/GHSA-existing",
1486+
"summary": "A vulnerability",
1487+
"is_reachable": "no",
1488+
"cdx_vulnerability_data": {
1489+
"analysis": {"state": "in_triage", "detail": "Under review"},
1490+
},
1491+
}
1492+
],
1493+
}
1494+
]
1495+
mock_fetch_dependencies.return_value = []
1496+
1497+
importer = ImportPackageFromScanCodeIO(
1498+
user=self.super_user,
1499+
project_uuid=uuid.uuid4(),
1500+
product=self.product1,
1501+
)
1502+
importer.save()
1503+
1504+
analysis = VulnerabilityAnalysis.objects.get(
1505+
vulnerability__advisory_uid="github_osv/GHSA-existing"
1506+
)
1507+
self.assertFalse(analysis.is_reachable)
1508+
1509+
# A second import with a conflicting value must not overwrite the existing one.
1510+
mock_fetch_packages.return_value[0]["affected_by_vulnerabilities"][0]["is_reachable"] = (
1511+
"yes"
1512+
)
1513+
importer2 = ImportPackageFromScanCodeIO(
1514+
user=self.super_user,
1515+
project_uuid=uuid.uuid4(),
1516+
product=self.product1,
1517+
)
1518+
importer2.save()
1519+
1520+
analysis.refresh_from_db()
1521+
self.assertFalse(analysis.is_reachable)
1522+
1523+
@mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_project_dependencies")
1524+
@mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_project_packages")
1525+
def test_product_portfolio_import_packages_from_scio_importer_is_reachable_without_cdx(
1526+
self, mock_fetch_packages, mock_fetch_dependencies
1527+
):
1528+
# When cdx_vulnerability_data is absent, a minimal VulnerabilityAnalysis is still
1529+
# created to record the is_reachable value from the scan.
1530+
mock_fetch_packages.return_value = [
1531+
{
1532+
"purl": "pkg:maven/abc/abc@1.0",
1533+
"type": "maven",
1534+
"namespace": "abc",
1535+
"name": "abc",
1536+
"version": "1.0",
1537+
"affected_by_vulnerabilities": [
1538+
{
1539+
"advisory_uid": "github_osv/GHSA-no-cdx",
1540+
"summary": "A vulnerability",
1541+
"is_reachable": "yes",
1542+
}
1543+
],
1544+
}
1545+
]
1546+
mock_fetch_dependencies.return_value = []
1547+
1548+
importer = ImportPackageFromScanCodeIO(
1549+
user=self.super_user,
1550+
project_uuid=uuid.uuid4(),
1551+
product=self.product1,
1552+
)
1553+
importer.save()
1554+
1555+
analysis = VulnerabilityAnalysis.objects.get(
1556+
vulnerability__advisory_uid="github_osv/GHSA-no-cdx"
1557+
)
1558+
self.assertTrue(analysis.is_reachable)
1559+
self.assertIsNone(analysis.state)

vulnerabilities/triage/tests/test_models.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ def setUp(self):
2929
self.dataspace = Dataspace.objects.create(name="nexB")
3030

3131
def test_save_requires_at_least_one_content_field(self):
32-
# AnalysisPreset shares its `save` validation with VulnerabilityAnalysis through
33-
# VulnerabilityAnalysisContentMixin: a preset that only sets `is_reachable` has no
34-
# content to apply and must be rejected the same way a bare analysis would be.
32+
# A preset that only sets is_reachable has no content to apply to an analysis
33+
# and must be rejected. Unlike VulnerabilityAnalysis, is_reachable alone is not
34+
# sufficient for AnalysisPreset because the preset's purpose is to carry content.
3535
preset = AnalysisPreset(dataspace=self.dataspace, name="No content", is_reachable=True)
3636
with self.assertRaises(ValueError):
3737
preset.save()

0 commit comments

Comments
 (0)