187 lines
6.3 KiB
Python
187 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Tests for license_checker.py — Pipeline 5.4"""
|
|
|
|
import json
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
# Add scripts dir to path
|
|
sys.path.insert(0, os.path.dirname(__file__))
|
|
|
|
from license_checker import (
|
|
normalize_license,
|
|
check_compatibility,
|
|
parse_requirements_txt,
|
|
parse_package_json,
|
|
parse_pyproject_toml,
|
|
parse_go_mod,
|
|
detect_project_license,
|
|
scan_dep_files,
|
|
generate_report,
|
|
format_text,
|
|
Severity,
|
|
DepLicense,
|
|
)
|
|
|
|
|
|
class TestNormalizeLicense(unittest.TestCase):
|
|
def test_mit_aliases(self):
|
|
for alias in ["mit", "MIT License", "The MIT License", "MIT license"]:
|
|
self.assertEqual(normalize_license(alias), "MIT")
|
|
|
|
def test_apache_aliases(self):
|
|
for alias in ["Apache 2.0", "Apache-2.0", "apache software license"]:
|
|
self.assertEqual(normalize_license(alias), "Apache-2.0")
|
|
|
|
def test_gpl_aliases(self):
|
|
self.assertEqual(normalize_license("GPL-3.0"), "GPL-3.0")
|
|
self.assertEqual(normalize_license("gplv3"), "GPL-3.0")
|
|
|
|
def test_unknown(self):
|
|
self.assertEqual(normalize_license(""), "UNKNOWN")
|
|
self.assertEqual(normalize_license("UNKNOWN"), "UNKNOWN")
|
|
|
|
def test_already_spdx(self):
|
|
self.assertEqual(normalize_license("BSD-3-Clause"), "BSD-3-Clause")
|
|
|
|
|
|
class TestCheckCompatibility(unittest.TestCase):
|
|
def test_permissive_ok(self):
|
|
sev, msg = check_compatibility("MIT", "MIT")
|
|
self.assertEqual(sev, Severity.OK)
|
|
|
|
def test_gpl_in_mit_error(self):
|
|
sev, msg = check_compatibility("GPL-3.0", "MIT")
|
|
self.assertEqual(sev, Severity.ERROR)
|
|
|
|
def test_unknown_warning(self):
|
|
sev, msg = check_compatibility("UNKNOWN", "MIT")
|
|
self.assertEqual(sev, Severity.WARNING)
|
|
|
|
def test_apache_in_mit_ok(self):
|
|
sev, msg = check_compatibility("Apache-2.0", "MIT")
|
|
self.assertEqual(sev, Severity.OK)
|
|
|
|
def test_lgpl_in_mit_error(self):
|
|
sev, msg = check_compatibility("LGPL-3.0", "MIT")
|
|
self.assertEqual(sev, Severity.ERROR)
|
|
|
|
|
|
class TestParseRequirements(unittest.TestCase):
|
|
def test_basic(self):
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
|
|
f.write("requests>=2.28.0\nflask==2.3.0\n# comment\npytest\n")
|
|
f.flush()
|
|
deps = parse_requirements_txt(f.name)
|
|
os.unlink(f.name)
|
|
names = [d.name for d in deps]
|
|
self.assertIn("requests", names)
|
|
self.assertIn("flask", names)
|
|
self.assertIn("pytest", names)
|
|
self.assertEqual(len(deps), 3)
|
|
|
|
def test_skip_flags(self):
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".txt", delete=False) as f:
|
|
f.write("-r other.txt\n--index-url https://pypi.org\nreal-dep\n")
|
|
f.flush()
|
|
deps = parse_requirements_txt(f.name)
|
|
os.unlink(f.name)
|
|
self.assertEqual(len(deps), 1)
|
|
self.assertEqual(deps[0].name, "real-dep")
|
|
|
|
|
|
class TestParsePackageJson(unittest.TestCase):
|
|
def test_basic(self):
|
|
data = {
|
|
"dependencies": {"express": "^4.18.0", "lodash": "^4.17.21"},
|
|
"devDependencies": {"jest": "^29.0.0"},
|
|
}
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as f:
|
|
json.dump(data, f)
|
|
f.flush()
|
|
deps = parse_package_json(f.name)
|
|
os.unlink(f.name)
|
|
names = [d.name for d in deps]
|
|
self.assertIn("express", names)
|
|
self.assertIn("jest", names)
|
|
self.assertEqual(len(deps), 3)
|
|
|
|
|
|
class TestParseGoMod(unittest.TestCase):
|
|
def test_basic(self):
|
|
content = """module example.com/mymod
|
|
|
|
go 1.21
|
|
|
|
require (
|
|
github.com/gin-gonic/gin v1.9.1
|
|
github.com/stretchr/testify v1.8.4
|
|
)
|
|
"""
|
|
with tempfile.NamedTemporaryFile(mode="w", suffix=".mod", delete=False) as f:
|
|
f.write(content)
|
|
f.flush()
|
|
deps = parse_go_mod(f.name)
|
|
os.unlink(f.name)
|
|
self.assertEqual(len(deps), 2)
|
|
self.assertEqual(deps[0].name, "github.com/gin-gonic/gin")
|
|
|
|
|
|
class TestDetectProjectLicense(unittest.TestCase):
|
|
def test_mit_file(self):
|
|
with tempfile.TemporaryDirectory() as d:
|
|
with open(os.path.join(d, "LICENSE"), "w") as f:
|
|
f.write("MIT License\n\nCopyright (c) 2024...\n")
|
|
self.assertEqual(detect_project_license(d), "MIT")
|
|
|
|
def test_apache_file(self):
|
|
with tempfile.TemporaryDirectory() as d:
|
|
with open(os.path.join(d, "LICENSE"), "w") as f:
|
|
f.write("Apache License Version 2.0...")
|
|
self.assertEqual(detect_project_license(d), "Apache-2.0")
|
|
|
|
def test_no_license(self):
|
|
with tempfile.TemporaryDirectory() as d:
|
|
self.assertEqual(detect_project_license(d), "UNKNOWN")
|
|
|
|
|
|
class TestScanDeps(unittest.TestCase):
|
|
def test_multi_ecosystem(self):
|
|
with tempfile.TemporaryDirectory() as d:
|
|
with open(os.path.join(d, "requirements.txt"), "w") as f:
|
|
f.write("flask\nrequests\n")
|
|
with open(os.path.join(d, "package.json"), "w") as f:
|
|
json.dump({"dependencies": {"express": "^4.0.0"}}, f)
|
|
deps = scan_dep_files(d)
|
|
names = [d.name for d in deps]
|
|
self.assertIn("flask", names)
|
|
self.assertIn("express", names)
|
|
|
|
|
|
class TestGenerateReport(unittest.TestCase):
|
|
def test_basic(self):
|
|
deps = [
|
|
DepLicense(name="flask", license="BSD-3-Clause", source="requirements.txt"),
|
|
DepLicense(name="gpl-pkg", license="GPL-3.0", source="requirements.txt"),
|
|
DepLicense(name="unknown-pkg", license="UNKNOWN", source="requirements.txt"),
|
|
]
|
|
report = generate_report(deps, "MIT")
|
|
self.assertEqual(report.summary["ok"], 1)
|
|
self.assertEqual(report.summary["error"], 1)
|
|
self.assertEqual(report.summary["warning"], 1)
|
|
self.assertEqual(len(report.errors), 1)
|
|
self.assertIn("gpl-pkg", report.errors[0])
|
|
|
|
def test_format_text(self):
|
|
deps = [DepLicense(name="flask", license="BSD-3-Clause", source="requirements.txt")]
|
|
report = generate_report(deps, "MIT")
|
|
text = format_text(report)
|
|
self.assertIn("LICENSE COMPATIBILITY REPORT", text)
|
|
self.assertIn("flask", text)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|