81 lines
3.9 KiB
Python
81 lines
3.9 KiB
Python
"""ISO smoke must bind exact signed APK bytes, not Gradle's base version."""
|
|
import hashlib
|
|
import importlib.util
|
|
import json
|
|
from pathlib import Path
|
|
import tempfile
|
|
import unittest
|
|
from unittest import mock
|
|
|
|
MODULE = Path(__file__).resolve().parents[1] / 'iso_companion_check.py'
|
|
spec = importlib.util.spec_from_file_location('iso_companion_check', MODULE)
|
|
check = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(check)
|
|
COMMIT = 'a' * 40
|
|
|
|
|
|
class IsoCompanionCheck(unittest.TestCase):
|
|
def setUp(self):
|
|
self.temp = tempfile.TemporaryDirectory()
|
|
self.addCleanup(self.temp.cleanup)
|
|
self.web = Path(self.temp.name)
|
|
(self.web / 'packages').mkdir()
|
|
self.apk = self.web / 'packages/archipelago-companion.apk'
|
|
self.meta = self.web / 'packages/archipelago-companion.json'
|
|
self.apk.write_bytes(b'synthetic signed APK fixture')
|
|
self.qualified = {
|
|
'sha256': hashlib.sha256(self.apk.read_bytes()).hexdigest(),
|
|
'certificate_sha256': 'b' * 64,
|
|
'package_name': 'com.archipelago.app.debug',
|
|
'versionName': '0.5.40-bump28-debug', 'versionCode': 60,
|
|
}
|
|
self.meta.write_text(json.dumps({'versionName': '0.5.40-bump28-debug', 'versionCode': 60}))
|
|
|
|
def test_accepts_qualified_variant_suffix_and_exact_apk(self):
|
|
result = check.check(self.web, COMMIT, self.qualified)
|
|
self.assertEqual(result['versionName'], '0.5.40-bump28-debug')
|
|
self.assertEqual(result['apk_sha256'], self.qualified['sha256'])
|
|
|
|
def test_rejects_swapped_apk_even_with_matching_metadata(self):
|
|
self.apk.write_bytes(b'different APK with same claimed metadata')
|
|
with self.assertRaisesRegex(ValueError, 'differs from signed source APK'):
|
|
check.check(self.web, COMMIT, self.qualified)
|
|
|
|
def test_rejects_metadata_disagreeing_with_qualified_apk(self):
|
|
self.meta.write_text(json.dumps({'versionName': '0.5.40', 'versionCode': 60}))
|
|
with self.assertRaisesRegex(ValueError, 'metadata differs'):
|
|
check.check(self.web, COMMIT, self.qualified)
|
|
self.meta.write_text(json.dumps({'versionName': '0.5.40-bump28-debug', 'versionCode': 61}))
|
|
with self.assertRaisesRegex(ValueError, 'metadata differs'):
|
|
check.check(self.web, COMMIT, self.qualified)
|
|
|
|
def test_rejects_symlink_apk_and_unqualified_identity(self):
|
|
self.apk.unlink()
|
|
self.apk.symlink_to(self.meta)
|
|
with self.assertRaisesRegex(ValueError, 'unsafe Companion file'):
|
|
check.check(self.web, COMMIT, self.qualified)
|
|
self.apk.unlink()
|
|
self.apk.write_bytes(b'synthetic signed APK fixture')
|
|
with self.assertRaisesRegex(ValueError, 'package differs'):
|
|
check.check(self.web, COMMIT, dict(self.qualified, package_name='other.app'))
|
|
with self.assertRaisesRegex(ValueError, 'version missing'):
|
|
check.check(self.web, COMMIT, dict(self.qualified, versionCode=None))
|
|
|
|
def test_qualify_calls_release_source_and_signed_apk_verifier(self):
|
|
responses = [COMMIT + '\n', json.dumps(self.qualified)]
|
|
with mock.patch.object(check.subprocess, 'check_output', side_effect=responses) as call:
|
|
source, identity = check.qualify(Path('/qualified/repo'), '2.1.3-beta')
|
|
self.assertEqual(source, COMMIT)
|
|
self.assertEqual(identity, self.qualified)
|
|
self.assertEqual(call.call_count, 2)
|
|
self.assertEqual(call.call_args_list[0].args[0][-5:],
|
|
['source', '--version', '2.1.3-beta', '--root', '/qualified/repo'])
|
|
self.assertEqual(call.call_args_list[1].args[0][-3:],
|
|
['companion', '--root', '/qualified/repo'])
|
|
self.assertEqual([item.kwargs['cwd'] for item in call.call_args_list],
|
|
[Path('/qualified/repo'), Path('/qualified/repo')])
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|