fdroid-server/tests/index.TestCase

263 lines
12 KiB
Python
Executable File

#!/usr/bin/env python3
import datetime
import inspect
import logging
import optparse
import os
import sys
import unittest
import zipfile
from unittest.mock import patch
import requests
import tempfile
import json
import shutil
localmodule = os.path.realpath(
os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..'))
print('localmodule: ' + localmodule)
if localmodule not in sys.path:
sys.path.insert(0, localmodule)
import fdroidserver.common
import fdroidserver.index
import fdroidserver.signindex
import fdroidserver.publish
from testcommon import TmpCwd
GP_FINGERPRINT = 'B7C2EEFD8DAC7806AF67DFCD92EB18126BC08312A7F2D6F3862E46013C7A6135'
class Options:
nosign = True
pretty = False
verbose = False
class IndexTest(unittest.TestCase):
def setUp(self):
logging.basicConfig(level=logging.DEBUG)
self.basedir = os.path.join(localmodule, 'tests')
os.chmod(os.path.join(self.basedir, 'config.py'), 0o600)
self.tmpdir = os.path.abspath(os.path.join(self.basedir, '..', '.testfiles'))
if not os.path.exists(self.tmpdir):
os.makedirs(self.tmpdir)
os.chdir(self.basedir)
fdroidserver.common.config = None
fdroidserver.common.options = Options
config = fdroidserver.common.read_config(fdroidserver.common.options)
config['jarsigner'] = fdroidserver.common.find_sdk_tools_cmd('jarsigner')
fdroidserver.common.config = config
fdroidserver.signindex.config = config
def test_get_public_key_from_jar_succeeds(self):
source_dir = os.path.join(self.basedir, 'signindex')
for f in ('testy.jar', 'guardianproject.jar'):
testfile = os.path.join(source_dir, f)
jar = zipfile.ZipFile(testfile)
_, fingerprint = fdroidserver.index.get_public_key_from_jar(jar)
# comparing fingerprints should be sufficient
if f == 'testy.jar':
self.assertEqual(fingerprint,
'818E469465F96B704E27BE2FEE4C63AB'
+ '9F83DDF30E7A34C7371A4728D83B0BC1')
if f == 'guardianproject.jar':
self.assertTrue(fingerprint == GP_FINGERPRINT)
def test_get_public_key_from_jar_fails(self):
source_dir = os.path.join(self.basedir, 'signindex')
testfile = os.path.join(source_dir, 'unsigned.jar')
jar = zipfile.ZipFile(testfile)
with self.assertRaises(fdroidserver.index.VerificationException):
fdroidserver.index.get_public_key_from_jar(jar)
def test_download_repo_index_no_fingerprint(self):
with self.assertRaises(fdroidserver.index.VerificationException):
fdroidserver.index.download_repo_index("http://example.org")
def test_download_repo_index_no_jar(self):
with self.assertRaises(requests.exceptions.RequestException):
fdroidserver.index.download_repo_index("http://example.org?fingerprint=nope")
@patch('requests.head')
def test_download_repo_index_same_etag(self, head):
url = 'http://example.org?fingerprint=test'
etag = '"4de5-54d840ce95cb9"'
head.return_value.headers = {'ETag': etag}
index, new_etag = fdroidserver.index.download_repo_index(url, etag=etag)
self.assertIsNone(index)
self.assertEqual(etag, new_etag)
@patch('requests.get')
@patch('requests.head')
def test_download_repo_index_new_etag(self, head, get):
url = 'http://example.org?fingerprint=' + GP_FINGERPRINT
etag = '"4de5-54d840ce95cb9"'
# fake HTTP answers
head.return_value.headers = {'ETag': 'new_etag'}
get.return_value.headers = {'ETag': 'new_etag'}
get.return_value.status_code = 200
testfile = os.path.join('signindex', 'guardianproject-v1.jar')
with open(testfile, 'rb') as file:
get.return_value.content = file.read()
index, new_etag = fdroidserver.index.download_repo_index(url, etag=etag)
# assert that the index was retrieved properly
self.assertEqual('Guardian Project Official Releases', index['repo']['name'])
self.assertEqual(GP_FINGERPRINT, index['repo']['fingerprint'])
self.assertTrue(len(index['repo']['pubkey']) > 500)
self.assertEqual(10, len(index['apps']))
self.assertEqual(10, len(index['packages']))
self.assertEqual('new_etag', new_etag)
def test_v1_sort_packages(self):
i = [{'packageName': 'org.smssecure.smssecure',
'apkName': 'org.smssecure.smssecure_134.apk',
'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
'versionCode': 134},
{'packageName': 'org.smssecure.smssecure',
'apkName': 'org.smssecure.smssecure_134_b30bb97.apk',
'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
'versionCode': 134},
{'packageName': 'b075b32b4ef1e8a869e00edb136bd48e34a0382b85ced8628f164d1199584e4e'},
{'packageName': '43af70d1aca437c2f9974c4634cc5abe45bdc4d5d71529ac4e553488d3bb3ff6'},
{'packageName': 'org.smssecure.smssecure',
'apkName': 'org.smssecure.smssecure_135_b30bb97.apk',
'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
'versionCode': 135},
{'packageName': 'org.smssecure.smssecure',
'apkName': 'org.smssecure.smssecure_135.apk',
'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
'versionCode': 135},
{'packageName': 'org.smssecure.smssecure',
'apkName': 'org.smssecure.smssecure_133.apk',
'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
'versionCode': 133},
{'packageName': 'org.smssecure.smssecure',
'apkName': 'smssecure-weird-version.apk',
'signer': '99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff',
'versionCode': 133},
{'packageName': 'org.smssecure.smssecure',
'apkName': 'smssecure-custom.apk',
'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
'versionCode': 133},
{'packageName': 'org.smssecure.smssecure',
'apkName': 'smssecure-new-custom.apk',
'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
'versionCode': 135}]
o = [{'packageName': '43af70d1aca437c2f9974c4634cc5abe45bdc4d5d71529ac4e553488d3bb3ff6'},
{'packageName': 'b075b32b4ef1e8a869e00edb136bd48e34a0382b85ced8628f164d1199584e4e'},
# app test data
# # packages with reproducible developer signature
{'packageName': 'org.smssecure.smssecure',
'apkName': 'org.smssecure.smssecure_135_b30bb97.apk',
'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
'versionCode': 135},
{'packageName': 'org.smssecure.smssecure',
'apkName': 'org.smssecure.smssecure_134_b30bb97.apk',
'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
'versionCode': 134},
# # packages build and signed by fdroid
{'packageName': 'org.smssecure.smssecure',
'apkName': 'org.smssecure.smssecure_135.apk',
'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
'versionCode': 135},
{'packageName': 'org.smssecure.smssecure',
'apkName': 'org.smssecure.smssecure_134.apk',
'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
'versionCode': 134},
{'packageName': 'org.smssecure.smssecure',
'apkName': 'org.smssecure.smssecure_133.apk',
'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
'versionCode': 133},
# # packages signed with unkown keys
{'packageName': 'org.smssecure.smssecure',
'apkName': 'smssecure-new-custom.apk',
'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
'versionCode': 135},
{'packageName': 'org.smssecure.smssecure',
'apkName': 'smssecure-custom.apk',
'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
'versionCode': 133},
{'packageName': 'org.smssecure.smssecure',
'apkName': 'smssecure-weird-version.apk',
'signer': '99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff',
'versionCode': 133}]
fdroidserver.common.config = {}
fdroidserver.common.fill_config_defaults(fdroidserver.common.config)
fdroidserver.publish.config = fdroidserver.common.config
fdroidserver.publish.config['keystorepass'] = '123456'
fdroidserver.publish.config['keypass'] = '123456'
fdroidserver.publish.config['keystore'] = os.path.join(os.getcwd(),
'dummy-keystore.jks')
fdroidserver.publish.config['repo_keyalias'] = 'repokey'
testsmetadir = os.path.join(os.getcwd(), 'metadata')
with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
shutil.copytree(testsmetadir, 'metadata')
sigkeyfps = {
"org.smssecure.smssecure": {
"signer": "b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6"
}
}
os.makedirs('stats')
jarfile = 'stats/publishsigkeys.jar'
with zipfile.ZipFile(jarfile, 'w', zipfile.ZIP_DEFLATED) as jar:
jar.writestr('publishsigkeys.json', json.dumps(sigkeyfps))
fdroidserver.publish.sign_sig_key_fingerprint_list(jarfile)
with open('config.py', 'w'):
pass
fdroidserver.index.v1_sort_packages(
i, fdroidserver.common.load_stats_fdroid_signing_key_fingerprints())
self.maxDiff = None
self.assertEqual(json.dumps(i, indent=2), json.dumps(o, indent=2))
def test_make_v0(self):
tmptestsdir = tempfile.mkdtemp(prefix=inspect.currentframe().f_code.co_name,
dir=self.tmpdir)
os.chdir(tmptestsdir)
os.mkdir('repo')
repo_icons_dir = os.path.join('repo', 'icons')
self.assertFalse(os.path.isdir(repo_icons_dir))
repodict = {
'address': 'https://example.com/fdroid/repo',
'description': 'This is just a test',
'icon': 'blahblah',
'name': 'test',
'timestamp': datetime.datetime.now(),
'version': 12,
}
requestsdict = {'install': [], 'uninstall': []}
fdroidserver.common.config['repo_pubkey'] = 'ffffffffffffffffffffffffffffffffff'
fdroidserver.index.make_v0({}, [], 'repo', repodict, requestsdict, [])
self.assertTrue(os.path.isdir(repo_icons_dir))
self.assertTrue(os.path.exists(os.path.join(repo_icons_dir,
fdroidserver.common.default_config['repo_icon'])))
self.assertTrue(os.path.exists(os.path.join('repo', 'index.xml')))
if __name__ == "__main__":
os.chdir(os.path.dirname(__file__))
parser = optparse.OptionParser()
parser.add_option("-v", "--verbose", action="store_true", default=False,
help="Spew out even more information than normal")
(options, args) = parser.parse_args()
Options.verbose = options.verbose
newSuite = unittest.TestSuite()
newSuite.addTest(unittest.makeSuite(IndexTest))
unittest.main(failfast=False)