47 lines
1.8 KiB
Python
47 lines
1.8 KiB
Python
import re
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
NAME_VERSION = re.compile(r'^([A-Za-z0-9_.-]+)==([^\s\\]+)', re.MULTILINE)
|
|
|
|
|
|
def normalized(name):
|
|
return re.sub(r'[-_.]+', '-', name).lower()
|
|
|
|
|
|
class DependencyLockTests(unittest.TestCase):
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
cls.direct_text = (ROOT / 'local/requirements.txt').read_text()
|
|
cls.lock_text = (ROOT / 'local/requirements.lock').read_text()
|
|
|
|
def test_every_direct_pin_matches_lock(self):
|
|
direct = {normalized(name): version for name, version in
|
|
NAME_VERSION.findall(self.direct_text)}
|
|
locked = {normalized(name): version for name, version in
|
|
NAME_VERSION.findall(self.lock_text)}
|
|
self.assertTrue(direct)
|
|
self.assertEqual({name: locked.get(name) for name in direct}, direct)
|
|
|
|
def test_every_locked_package_has_sha256_hash(self):
|
|
matches = list(NAME_VERSION.finditer(self.lock_text))
|
|
self.assertTrue(matches)
|
|
for index, match in enumerate(matches):
|
|
end = matches[index + 1].start() if index + 1 < len(matches) else len(self.lock_text)
|
|
block = self.lock_text[match.start():end]
|
|
hashes = re.findall(r'--hash=sha256:([0-9a-f]{64})(?:\s|\\)', block)
|
|
self.assertTrue(hashes, f'{match.group(1)} has no SHA-256 artifact hash')
|
|
|
|
def test_image_build_requires_the_lock_and_hashes(self):
|
|
dockerfile = (ROOT / 'local/Dockerfile').read_text()
|
|
self.assertIn('requirements.lock', dockerfile)
|
|
self.assertIn('--require-hashes -r local/requirements.lock', dockerfile)
|
|
|
|
def test_reproducible_generator_is_recorded(self):
|
|
self.assertIn('./local/lock_dependencies.sh', self.lock_text[:300])
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|