chiark / gitweb /
build: log vcs tools version on every build attempt
[fdroidserver.git] / tests / index.TestCase
1 #!/usr/bin/env python3
2
3 import inspect
4 import optparse
5 import os
6 import sys
7 import unittest
8 import zipfile
9 from unittest.mock import patch
10 import requests
11 import tempfile
12 import json
13 import shutil
14
15 localmodule = os.path.realpath(
16     os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..'))
17 print('localmodule: ' + localmodule)
18 if localmodule not in sys.path:
19     sys.path.insert(0, localmodule)
20
21 import fdroidserver.common
22 import fdroidserver.index
23 import fdroidserver.signindex
24 import fdroidserver.publish
25 from testcommon import TmpCwd
26
27
28 GP_FINGERPRINT = 'B7C2EEFD8DAC7806AF67DFCD92EB18126BC08312A7F2D6F3862E46013C7A6135'
29
30
31 class IndexTest(unittest.TestCase):
32
33     def setUp(self):
34         fdroidserver.common.config = None
35         config = fdroidserver.common.read_config(fdroidserver.common.options)
36         config['jarsigner'] = fdroidserver.common.find_sdk_tools_cmd('jarsigner')
37         fdroidserver.common.config = config
38         fdroidserver.signindex.config = config
39
40     @staticmethod
41     def test_verify_jar_signature_succeeds():
42         basedir = os.path.dirname(__file__)
43         source_dir = os.path.join(basedir, 'signindex')
44         for f in ('testy.jar', 'guardianproject.jar'):
45             testfile = os.path.join(source_dir, f)
46             fdroidserver.common.verify_jar_signature(testfile)
47
48     def test_verify_jar_signature_fails(self):
49         basedir = os.path.dirname(__file__)
50         source_dir = os.path.join(basedir, 'signindex')
51         testfile = os.path.join(source_dir, 'unsigned.jar')
52         with self.assertRaises(fdroidserver.index.VerificationException):
53             fdroidserver.common.verify_jar_signature(testfile)
54
55     def test_get_public_key_from_jar_succeeds(self):
56         basedir = os.path.dirname(__file__)
57         source_dir = os.path.join(basedir, 'signindex')
58         for f in ('testy.jar', 'guardianproject.jar'):
59             testfile = os.path.join(source_dir, f)
60             jar = zipfile.ZipFile(testfile)
61             _, fingerprint = fdroidserver.index.get_public_key_from_jar(jar)
62             # comparing fingerprints should be sufficient
63             if f == 'testy.jar':
64                 self.assertTrue(fingerprint ==
65                                 '818E469465F96B704E27BE2FEE4C63AB' +
66                                 '9F83DDF30E7A34C7371A4728D83B0BC1')
67             if f == 'guardianproject.jar':
68                 self.assertTrue(fingerprint == GP_FINGERPRINT)
69
70     def test_get_public_key_from_jar_fails(self):
71         basedir = os.path.dirname(__file__)
72         source_dir = os.path.join(basedir, 'signindex')
73         testfile = os.path.join(source_dir, 'unsigned.jar')
74         jar = zipfile.ZipFile(testfile)
75         with self.assertRaises(fdroidserver.index.VerificationException):
76             fdroidserver.index.get_public_key_from_jar(jar)
77
78     def test_download_repo_index_no_fingerprint(self):
79         with self.assertRaises(fdroidserver.index.VerificationException):
80             fdroidserver.index.download_repo_index("http://example.org")
81
82     def test_download_repo_index_no_jar(self):
83         with self.assertRaises(requests.exceptions.HTTPError):
84             fdroidserver.index.download_repo_index("http://example.org?fingerprint=nope")
85
86     @patch('requests.head')
87     def test_download_repo_index_same_etag(self, head):
88         url = 'http://example.org?fingerprint=test'
89         etag = '"4de5-54d840ce95cb9"'
90
91         head.return_value.headers = {'ETag': etag}
92         index, new_etag = fdroidserver.index.download_repo_index(url, etag=etag)
93
94         self.assertIsNone(index)
95         self.assertEqual(etag, new_etag)
96
97     @patch('requests.get')
98     @patch('requests.head')
99     def test_download_repo_index_new_etag(self, head, get):
100         url = 'http://example.org?fingerprint=' + GP_FINGERPRINT
101         etag = '"4de5-54d840ce95cb9"'
102
103         # fake HTTP answers
104         head.return_value.headers = {'ETag': 'new_etag'}
105         get.return_value.headers = {'ETag': 'new_etag'}
106         get.return_value.status_code = 200
107         testfile = os.path.join(os.path.dirname(__file__), 'signindex', 'guardianproject-v1.jar')
108         with open(testfile, 'rb') as file:
109             get.return_value.content = file.read()
110
111         index, new_etag = fdroidserver.index.download_repo_index(url, etag=etag)
112
113         # assert that the index was retrieved properly
114         self.assertEqual('Guardian Project Official Releases', index['repo']['name'])
115         self.assertEqual(GP_FINGERPRINT, index['repo']['fingerprint'])
116         self.assertTrue(len(index['repo']['pubkey']) > 500)
117         self.assertEqual(10, len(index['apps']))
118         self.assertEqual(10, len(index['packages']))
119         self.assertEqual('new_etag', new_etag)
120
121     def test_v1_sort_packages(self):
122
123         i = [{'packageName': 'org.smssecure.smssecure',
124               'apkName': 'org.smssecure.smssecure_134.apk',
125               'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
126               'versionCode': 134},
127              {'packageName': 'org.smssecure.smssecure',
128               'apkName': 'org.smssecure.smssecure_134_b30bb97.apk',
129               'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
130               'versionCode': 134},
131              {'packageName': 'b075b32b4ef1e8a869e00edb136bd48e34a0382b85ced8628f164d1199584e4e'},
132              {'packageName': '43af70d1aca437c2f9974c4634cc5abe45bdc4d5d71529ac4e553488d3bb3ff6'},
133              {'packageName': 'org.smssecure.smssecure',
134               'apkName': 'org.smssecure.smssecure_135_b30bb97.apk',
135               'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
136               'versionCode': 135},
137              {'packageName': 'org.smssecure.smssecure',
138               'apkName': 'org.smssecure.smssecure_135.apk',
139               'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
140               'versionCode': 135},
141              {'packageName': 'org.smssecure.smssecure',
142               'apkName': 'org.smssecure.smssecure_133.apk',
143               'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
144               'versionCode': 133},
145              {'packageName': 'org.smssecure.smssecure',
146               'apkName': 'smssecure-weird-version.apk',
147               'signer': '99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff',
148               'versionCode': 133},
149              {'packageName': 'org.smssecure.smssecure',
150               'apkName': 'smssecure-custom.apk',
151               'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
152               'versionCode': 133},
153              {'packageName': 'org.smssecure.smssecure',
154               'apkName': 'smssecure-new-custom.apk',
155               'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
156               'versionCode': 135}]
157
158         o = [{'packageName': '43af70d1aca437c2f9974c4634cc5abe45bdc4d5d71529ac4e553488d3bb3ff6'},
159              {'packageName': 'b075b32b4ef1e8a869e00edb136bd48e34a0382b85ced8628f164d1199584e4e'},
160              # app test data
161              # # packages with reproducible developer signature
162              {'packageName': 'org.smssecure.smssecure',
163               'apkName': 'org.smssecure.smssecure_135_b30bb97.apk',
164               'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
165               'versionCode': 135},
166              {'packageName': 'org.smssecure.smssecure',
167               'apkName': 'org.smssecure.smssecure_134_b30bb97.apk',
168               'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
169               'versionCode': 134},
170              # # packages build and signed by fdroid
171              {'packageName': 'org.smssecure.smssecure',
172               'apkName': 'org.smssecure.smssecure_135.apk',
173               'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
174               'versionCode': 135},
175              {'packageName': 'org.smssecure.smssecure',
176               'apkName': 'org.smssecure.smssecure_134.apk',
177               'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
178               'versionCode': 134},
179              {'packageName': 'org.smssecure.smssecure',
180               'apkName': 'org.smssecure.smssecure_133.apk',
181               'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
182               'versionCode': 133},
183              # # packages signed with unkown keys
184              {'packageName': 'org.smssecure.smssecure',
185               'apkName': 'smssecure-new-custom.apk',
186               'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
187               'versionCode': 135},
188              {'packageName': 'org.smssecure.smssecure',
189               'apkName': 'smssecure-custom.apk',
190               'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
191               'versionCode': 133},
192              {'packageName': 'org.smssecure.smssecure',
193               'apkName': 'smssecure-weird-version.apk',
194               'signer': '99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff',
195               'versionCode': 133}]
196
197         fdroidserver.common.config = {}
198         fdroidserver.common.fill_config_defaults(fdroidserver.common.config)
199         fdroidserver.publish.config = fdroidserver.common.config
200         fdroidserver.publish.config['keystorepass'] = '123456'
201         fdroidserver.publish.config['keypass'] = '123456'
202         fdroidserver.publish.config['keystore'] = os.path.join(os.getcwd(),
203                                                                'dummy-keystore.jks')
204         fdroidserver.publish.config['repo_keyalias'] = 'repokey'
205
206         testsmetadir = os.path.join(os.getcwd(), 'metadata')
207         with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
208             shutil.copytree(testsmetadir, 'metadata')
209             sigkeyfps = {
210                 "org.smssecure.smssecure": {
211                     "signer": "b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6"
212                 }
213             }
214             os.makedirs('stats')
215             jarfile = 'stats/publishsigkeys.jar'
216             with zipfile.ZipFile(jarfile, 'w', zipfile.ZIP_DEFLATED) as jar:
217                 jar.writestr('publishsigkeys.json', json.dumps(sigkeyfps))
218             fdroidserver.publish.sign_sig_key_fingerprint_list(jarfile)
219             with open('config.py', 'w'):
220                 pass
221
222             fdroidserver.index.v1_sort_packages(
223                 i, 'repo', fdroidserver.common.load_stats_fdroid_signing_key_fingerprints())
224             self.maxDiff = None
225             self.assertEqual(json.dumps(i, indent=2), json.dumps(o, indent=2))
226
227
228 if __name__ == "__main__":
229     if os.path.basename(os.getcwd()) != 'tests' and os.path.isdir('tests'):
230         os.chdir('tests')
231
232     parser = optparse.OptionParser()
233     parser.add_option("-v", "--verbose", action="store_true", default=False,
234                       help="Spew out even more information than normal")
235     (fdroidserver.common.options, args) = parser.parse_args(['--verbose'])
236
237     newSuite = unittest.TestSuite()
238     newSuite.addTest(unittest.makeSuite(IndexTest))
239     unittest.main(failfast=False)