Files
archy/scripts/tests/test_iso_companion_check.py

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()