def test_unicode_fails_with_wrong_configured_basepath(self): with override_settings(TMP_PATH=unicode(settings.TMP_PATH)): file_viewer = FileViewer(self.file) file_viewer.src = unicode_filenames with pytest.raises(UnicodeDecodeError): file_viewer.extract()
def test_unicode_fails_with_wrong_configured_basepath(self): with override_settings(TMP_PATH=unicode(settings.TMP_PATH)): file_viewer = FileViewer(self.file) file_viewer.src = unicode_filenames with pytest.raises(UnicodeDecodeError): file_viewer.extract()
def test_cleanup_extracted_file(): with freeze_time('2017-01-08 10:01:00'): viewer = FileViewer(make_file(1, get_file('webextension.xpi'))) assert '0108' in viewer.dest assert not os.path.exists(viewer.dest) viewer.extract() assert os.path.exists(viewer.dest) # Cleaning up only cleans up yesterdays files so it doesn't touch # us today... cleanup_extracted_file() assert os.path.exists(viewer.dest) # Even hours later we don't cleanup yet... with freeze_time('2017-01-08 23:59:00'): assert os.path.exists(viewer.dest) cleanup_extracted_file() assert os.path.exists(viewer.dest) # But yesterday... we'll cleanup properly with freeze_time('2017-01-07 10:01:00'): assert os.path.exists(viewer.dest) cleanup_extracted_file() assert not os.path.exists(viewer.dest)
class TestSearchEngineHelper(TestCase): fixtures = ['base/addon_4594_a9'] def setUp(self): super(TestSearchEngineHelper, self).setUp() self.left = File.objects.get(pk=25753) self.viewer = FileViewer(self.left) if not os.path.exists(os.path.dirname(self.viewer.src)): os.makedirs(os.path.dirname(self.viewer.src)) with storage.open(self.viewer.src, 'w') as f: f.write('some data\n') def tearDown(self): self.viewer.cleanup() super(TestSearchEngineHelper, self).tearDown() def test_is_search_engine(self): assert self.viewer.is_search_engine() def test_extract_search_engine(self): self.viewer.extract() assert os.path.exists(self.viewer.dest) def test_default(self): self.viewer.extract() assert self.viewer.get_default(None) == 'a9.xml' def test_default_no_files(self): self.viewer.extract() os.remove(os.path.join(self.viewer.dest, 'a9.xml')) assert self.viewer.get_default(None) is None
class TestSearchEngineHelper(TestCase): fixtures = ['base/addon_4594_a9'] def setUp(self): super(TestSearchEngineHelper, self).setUp() self.left = File.objects.get(pk=25753) self.viewer = FileViewer(self.left) if not os.path.exists(os.path.dirname(self.viewer.src)): os.makedirs(os.path.dirname(self.viewer.src)) with storage.open(self.viewer.src, 'w') as f: f.write('some data\n') def tearDown(self): self.viewer.cleanup() super(TestSearchEngineHelper, self).tearDown() def test_is_search_engine(self): assert self.viewer.is_search_engine() def test_extract_search_engine(self): self.viewer.extract() assert os.path.exists(self.viewer.dest) def test_default(self): self.viewer.extract() assert self.viewer.get_default(None) == 'a9.xml' def test_default_no_files(self): self.viewer.extract() os.remove(os.path.join(self.viewer.dest, 'a9.xml')) assert self.viewer.get_default(None) is None
class FilesBase(object): def login_as_admin(self): assert self.client.login(email='*****@*****.**') def login_as_reviewer(self): assert self.client.login(email='*****@*****.**') def setUp(self): super(FilesBase, self).setUp() self.addon = Addon.objects.get(pk=3615) self.dev = self.addon.authors.all()[0] self.regular = UserProfile.objects.get(pk=999) self.version = self.addon.versions.latest() self.file = self.version.all_files[0] p = [amo.PLATFORM_LINUX.id, amo.PLATFORM_WIN.id, amo.PLATFORM_MAC.id] self.file.update(platform=p[0]) files = [('dictionary-test.xpi', self.file), ('dictionary-test.xpi', File.objects.create(version=self.version, platform=p[1], hash='abc123', filename='dictionary-test.xpi')), ('dictionary-test-changed.xpi', File.objects.create(version=self.version, platform=p[2], hash='abc123', filename='dictionary-test.xpi'))] fixtures_base_path = os.path.join(settings.ROOT, files_fixtures) for xpi_file, file_obj in files: create_directory(os.path.dirname(file_obj.file_path)) shutil.copyfile(os.path.join(fixtures_base_path, xpi_file), file_obj.file_path) self.files = [x[1] for x in files] self.login_as_reviewer() self.file_viewer = FileViewer(self.file) def tearDown(self): self.file_viewer.cleanup() super(FilesBase, self).tearDown() def files_redirect(self, file): return reverse('files.redirect', args=[self.file.pk, file]) def files_serve(self, file): return reverse('files.serve', args=[self.file.pk, file]) def test_view_access_anon(self): self.client.logout() self.check_urls(403) def test_view_access_anon_view_source(self): # This is disallowed for now, see Bug 1353788 for more details. self.addon.update(view_source=True) self.file_viewer.extract() self.client.logout() self.check_urls(403) def test_view_access_reviewer(self): self.file_viewer.extract() self.check_urls(200) def test_view_access_reviewer_view_source(self): # This is disallowed for now, see Bug 1353788 for more details. self.addon.update(view_source=True) self.file_viewer.extract() self.check_urls(200) def test_view_access_developer(self): self.client.logout() assert self.client.login(email=self.dev.email) self.file_viewer.extract() self.check_urls(200) def test_view_access_reviewed(self): # This is disallowed for now, see Bug 1353788 for more details. self.addon.update(view_source=True) self.file_viewer.extract() self.client.logout() for status in amo.UNREVIEWED_FILE_STATUSES: self.addon.update(status=status) self.check_urls(403) for status in amo.REVIEWED_STATUSES: self.addon.update(status=status) self.check_urls(403) def test_view_access_developer_view_source(self): self.client.logout() assert self.client.login(email=self.dev.email) self.addon.update(view_source=True) self.file_viewer.extract() self.check_urls(200) def test_view_access_another_developer(self): self.client.logout() assert self.client.login(email=self.regular.email) self.file_viewer.extract() self.check_urls(403) def test_view_access_another_developer_view_source(self): # This is disallowed for now, see Bug 1353788 for more details. self.client.logout() assert self.client.login(email=self.regular.email) self.addon.update(view_source=True) self.file_viewer.extract() self.check_urls(403) def test_poll_extracted(self): self.file_viewer.extract() res = self.client.get(self.poll_url()) assert res.status_code == 200 assert json.loads(res.content)['status'] def test_poll_not_extracted(self): res = self.client.get(self.poll_url()) assert res.status_code == 200 assert not json.loads(res.content)['status'] def test_poll_extracted_anon(self): self.client.logout() res = self.client.get(self.poll_url()) assert res.status_code == 403 def test_content_headers(self): self.file_viewer.extract() res = self.client.get(self.file_url('install.js')) assert 'etag' in res._headers assert 'last-modified' in res._headers def test_content_headers_etag(self): self.file_viewer.extract() self.file_viewer.select('install.js') obj = getattr(self.file_viewer, 'left', self.file_viewer) etag = quote_etag(obj.selected.get('sha256')) res = self.client.get(self.file_url('install.js'), HTTP_IF_NONE_MATCH=etag) assert res.status_code == 304 def test_content_headers_if_modified(self): self.file_viewer.extract() self.file_viewer.select('install.js') obj = getattr(self.file_viewer, 'left', self.file_viewer) date = http_date(obj.selected.get('modified')) res = self.client.get(self.file_url('install.js'), HTTP_IF_MODIFIED_SINCE=date) assert res.status_code == 304 def test_file_header(self): self.file_viewer.extract() res = self.client.get(self.file_url(not_binary)) url = res.context['file_link']['url'] assert url == reverse('reviewers.review', args=[self.addon.slug]) def test_content_no_file(self): self.file_viewer.extract() res = self.client.get(self.file_url()) doc = pq(res.content) assert len(doc('#content')) == 0 def test_files(self): self.file_viewer.extract() res = self.client.get(self.file_url()) assert res.status_code == 200 assert 'files' in res.context def test_files_anon(self): self.client.logout() res = self.client.get(self.file_url()) assert res.status_code == 403 def test_files_file(self): self.file_viewer.extract() res = self.client.get(self.file_url(not_binary)) assert res.status_code == 200 assert 'selected' in res.context def test_files_back_link(self): self.file_viewer.extract() res = self.client.get(self.file_url(not_binary)) doc = pq(res.content) assert doc('#commands td')[-1].text_content() == 'Back to review' def test_files_back_link_anon(self): # This is disallowed for now, see Bug 1353788 for more details. self.file_viewer.extract() self.client.logout() self.addon.update(view_source=True) res = self.client.get(self.file_url(not_binary)) assert res.status_code == 403 def test_diff_redirect(self): ids = self.files[0].id, self.files[1].id res = self.client.post(self.file_url(), { 'left': ids[0], 'right': ids[1] }) self.assert3xx(res, reverse('files.compare', args=ids)) def test_browse_redirect(self): ids = self.files[0].id, res = self.client.post(self.file_url(), {'left': ids[0]}) self.assert3xx(res, reverse('files.list', args=ids)) def test_browse_404(self): res = self.client.get('/files/browse/file/dont/exist.png', follow=True) assert res.status_code == 404 def test_invalid_redirect(self): res = self.client.post(self.file_url(), {}) self.assert3xx(res, self.file_url()) def test_file_chooser(self): res = self.client.get(self.file_url()) doc = pq(res.content) left = doc('#id_left') assert len(left) == 1 ver = left('optgroup') assert len(ver) == 1 assert ver.attr('label') == self.version.version files = ver('option') assert len(files) == 2 def test_file_chooser_coalescing(self): res = self.client.get(self.file_url()) doc = pq(res.content) unreviewed_file = doc('#id_left > optgroup > option.status-unreviewed') public_file = doc('#id_left > optgroup > option.status-public') assert public_file.text() == str(self.files[0].get_platform_display()) assert unreviewed_file.text() == ( '%s, %s' % (self.files[1].get_platform_display(), self.files[2].get_platform_display())) assert public_file.attr('value') == str(self.files[0].id) assert unreviewed_file.attr('value') == str(self.files[1].id) def test_file_chooser_disabled_coalescing(self): self.files[1].update(status=amo.STATUS_DISABLED) res = self.client.get(self.file_url()) doc = pq(res.content) disabled_file = doc('#id_left > optgroup > option.status-disabled') assert disabled_file.attr('value') == str(self.files[2].id) def test_files_for_unlisted_addon_returns_404(self): """Files browsing isn't allowed for unlisted addons.""" self.make_addon_unlisted(self.addon) assert self.client.get(self.file_url()).status_code == 404 def test_files_for_unlisted_addon_with_admin(self): """Files browsing is allowed for unlisted addons if you're admin.""" self.login_as_admin() self.make_addon_unlisted(self.addon) assert self.client.get(self.file_url()).status_code == 200 def test_all_versions_shown_for_admin(self): self.login_as_admin() listed_ver = version_factory(addon=self.addon, channel=amo.RELEASE_CHANNEL_LISTED, version='4.0', created=self.days_ago(1)) unlisted_ver = version_factory(addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED, version='5.0') assert self.addon.versions.count() == 3 res = self.client.get(self.file_url()) doc = pq(res.content) left_select = doc('#id_left') assert left_select('optgroup').attr('label') == self.version.version file_options = left_select('option.status-public') assert len(file_options) == 3, left_select.html() # Check the files in the list are the two we added and the default. assert file_options.eq(0).attr('value') == str( unlisted_ver.all_files[0].pk) assert file_options.eq(1).attr('value') == str( listed_ver.all_files[0].pk) assert file_options.eq(2).attr('value') == str(self.file.pk) # Check there are prefixes on the labels for the channels assert file_options.eq(0).text().endswith('[Self]') assert file_options.eq(1).text().endswith('[AMO]') assert file_options.eq(2).text().endswith('[AMO]') def test_channel_prefix_not_shown_when_no_mixed_channels(self): self.login_as_admin() version_factory(addon=self.addon, channel=amo.RELEASE_CHANNEL_LISTED) assert self.addon.versions.count() == 2 res = self.client.get(self.file_url()) doc = pq(res.content) left_select = doc('#id_left') assert left_select('optgroup').attr('label') == self.version.version # Check there are NO prefixes on the labels for the channels file_options = left_select('option.status-public') assert not file_options.eq(0).text().endswith('[Self]') assert not file_options.eq(1).text().endswith('[AMO]') assert not file_options.eq(2).text().endswith('[AMO]') def test_only_listed_versions_shown_for_reviewer(self): listed_ver = version_factory(addon=self.addon, channel=amo.RELEASE_CHANNEL_LISTED, version='4.0') version_factory(addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED, version='5.0') assert self.addon.versions.count() == 3 res = self.client.get(self.file_url()) doc = pq(res.content) left_select = doc('#id_left') assert left_select('optgroup').attr('label') == self.version.version # Check the files in the list are just the listed, and the default. file_options = left_select('option.status-public') assert len(file_options) == 2, left_select.html() assert file_options.eq(0).attr('value') == str( listed_ver.all_files[0].pk) assert file_options.eq(1).attr('value') == str(self.file.pk) # Check there are NO prefixes on the labels for the channels assert not file_options.eq(0).text().endswith('[AMO]') assert not file_options.eq(1).text().endswith('[AMO]')
class TestFileViewer(TestCase): def setUp(self): super(TestFileViewer, self).setUp() self.viewer = FileViewer(make_file(1, get_file('dictionary-test.xpi'))) def tearDown(self): self.viewer.cleanup() super(TestFileViewer, self).tearDown() def test_files_not_extracted(self): assert not self.viewer.is_extracted() def test_files_extracted(self): self.viewer.extract() assert self.viewer.is_extracted() def test_recurse_extract(self): self.viewer.src = get_file('recurse.xpi') self.viewer.extract() assert self.viewer.is_extracted() def test_recurse_contents(self): self.viewer.src = get_file('recurse.xpi') self.viewer.extract() files = self.viewer.get_files() # We do not extract nested .zip or .xpi files anymore assert list(files.keys()) == [ u'recurse', u'recurse/chrome', u'recurse/chrome/test-root.txt', u'recurse/chrome/test.jar', u'recurse/notazip.jar', u'recurse/recurse.xpi', u'recurse/somejar.jar' ] def test_locked(self): self.viewer.src = get_file('dictionary-test.xpi') # Lock was successfully attained assert self.viewer.extract() lock = flufl.lock.Lock( os.path.join(settings.TMP_PATH, 'file-viewer-%s.lock' % self.viewer.file.pk)) assert not lock.is_locked lock.lock() assert lock.is_locked # Not extracting, the viewer is locked, lock could not be attained assert not self.viewer.extract() def test_extract_file_locked_message(self): self.viewer.src = get_file('dictionary-test.xpi') assert not self.viewer.is_extracted() lock = flufl.lock.Lock( os.path.join(settings.TMP_PATH, 'file-viewer-%s.lock' % self.viewer.file.pk)) assert not lock.is_locked lock.lock() assert lock.is_locked msg = extract_file(self.viewer) assert str(msg.get()).startswith(u'File viewer is locked') msg.delete() def test_cleanup(self): self.viewer.extract() self.viewer.cleanup() assert not self.viewer.is_extracted() @freeze_time('2017-01-08 02:01:00') def test_dest(self): viewer = FileViewer(make_file(1, get_file('webextension.xpi'))) assert viewer.dest == os.path.join(settings.TMP_PATH, 'file_viewer', '0108', str(self.viewer.file.pk)) def test_isbinary(self): binary = self.viewer._is_binary for f in [ 'foo.rdf', 'foo.xml', 'foo.js', 'foo.py' 'foo.html', 'foo.txt', 'foo.dtd', 'foo.xul', 'foo.sh', 'foo.properties', 'foo.json', 'foo.src', 'CHANGELOG' ]: m, encoding = mimetypes.guess_type(f) assert not binary(m, f), '%s should not be binary' % f for f in ['foo.png', 'foo.gif', 'foo.exe', 'foo.swf']: m, encoding = mimetypes.guess_type(f) assert binary(m, f), '%s should be binary' % f filename = os.path.join(settings.TMP_PATH, 'test_isbinary') for txt in ['#!/usr/bin/python', '#python', u'\0x2']: open(filename, 'w').write(txt) m, encoding = mimetypes.guess_type(filename) assert not binary(m, filename), '%s should not be binary' % txt for txt in ['MZ']: open(filename, 'w').write(txt) m, encoding = mimetypes.guess_type(filename) assert binary(m, filename), '%s should be binary' % txt os.remove(filename) def test_truncate(self): truncate = self.viewer.truncate for x, y in ( ['foo.rdf', 'foo.rdf'], ['somelongfilename.rdf', 'somelongfilenam...rdf'], [u'unicode삮.txt', u'unicode\uc0ae.txt'], [u'unicodesomelong삮.txt', u'unicodesomelong...txt'], [ 'somelongfilename.somelongextension', 'somelongfilenam...somelonge..' ], ): assert truncate(x) == y def test_get_files_not_extracted_runs_extraction(self): self.viewer.src = get_file('dictionary-test.xpi') assert not self.viewer.is_extracted() self.viewer.get_files() assert self.viewer.is_extracted() def test_get_files_size(self): self.viewer.extract() files = self.viewer.get_files() assert len(files) == 14 def test_get_files_directory(self): self.viewer.extract() files = self.viewer.get_files() assert not files['install.js']['directory'] assert not files['install.js']['binary'] assert files['__MACOSX']['directory'] assert not files['__MACOSX']['binary'] def test_get_files_depth(self): self.viewer.extract() files = self.viewer.get_files() assert files['dictionaries/license.txt']['depth'] == 1 def test_bom(self): dest = os.path.join(settings.TMP_PATH, 'test_bom') with open(dest, 'wb') as f: f.write('foo'.encode('utf-16')) self.viewer.select('foo') self.viewer.selected = {'full': dest, 'size': 1} assert self.viewer.read_file() == u'foo' os.remove(dest) def test_syntax(self): for filename, syntax in [('foo.rdf', 'xml'), ('foo.xul', 'xml'), ('foo.json', 'js'), ('foo.jsm', 'js'), ('foo.htm', 'html'), ('foo.bar', 'plain'), ('foo.diff', 'plain')]: assert self.viewer.get_syntax(filename) == syntax def test_file_order(self): self.viewer.extract() dest = self.viewer.dest open(os.path.join(dest, 'chrome.manifest'), 'w') subdir = os.path.join(dest, 'chrome') os.mkdir(subdir) open(os.path.join(subdir, 'foo'), 'w') cache.delete(self.viewer._cache_key()) files = list(self.viewer.get_files().keys()) rt = files.index(u'chrome') assert files[rt:rt + 3] == [u'chrome', u'chrome/foo', u'dictionaries'] @patch.object(settings, 'FILE_VIEWER_SIZE_LIMIT', 5) def test_file_size(self): self.viewer.extract() self.viewer.get_files() self.viewer.select('install.js') res = self.viewer.read_file() assert res == '' assert self.viewer.selected['msg'].startswith('File size is') @pytest.mark.needs_locales_compilation @patch.object(settings, 'FILE_VIEWER_SIZE_LIMIT', 5) def test_file_size_unicode(self): with self.activate(locale='he'): self.viewer.extract() self.viewer.get_files() self.viewer.select('install.js') res = self.viewer.read_file() assert res == '' assert (self.viewer.selected['msg'].startswith(u'גודל הקובץ חורג')) @patch.object(settings, 'FILE_UNZIP_SIZE_LIMIT', 5) def test_contents_size(self): self.assertRaises(forms.ValidationError, self.viewer.extract) def test_default(self): self.viewer.extract() assert self.viewer.get_default(None) == 'install.rdf' def test_default_webextension(self): viewer = FileViewer(make_file(2, get_file('webextension.xpi'))) viewer.extract() assert viewer.get_default(None) == 'manifest.json' def test_default_webextension_zip(self): viewer = FileViewer(make_file(2, get_file('webextension_no_id.zip'))) viewer.extract() assert viewer.get_default(None) == 'manifest.json' def test_default_webextension_crx(self): viewer = FileViewer(make_file(2, get_file('webextension.crx'))) viewer.extract() assert viewer.get_default(None) == 'manifest.json' def test_default_package_json(self): viewer = FileViewer(make_file(3, get_file('new-format-0.0.1.xpi'))) viewer.extract() assert viewer.get_default(None) == 'package.json' def test_delete_mid_read(self): self.viewer.extract() self.viewer.select('install.js') os.remove(os.path.join(self.viewer.dest, 'install.js')) res = self.viewer.read_file() assert res == '' assert self.viewer.selected['msg'].startswith('That file no') @patch('olympia.files.file_viewer.get_sha256') def test_delete_mid_tree(self, get_sha256): get_sha256.side_effect = IOError('ow') self.viewer.extract() with self.assertRaises(IOError): self.viewer.get_files() @patch('olympia.files.file_viewer.os.fsync') def test_verify_files_doesnt_call_fsync_regularly(self, fsync): self.viewer.extract() assert not fsync.called @patch('olympia.files.file_viewer.os.fsync') def test_verify_files_calls_fsync_on_differences(self, fsync): self.viewer.extract() assert not fsync.called files_to_verify = get_all_files(self.viewer.dest) files_to_verify.pop() module_path = 'olympia.files.file_viewer.get_all_files' with patch(module_path) as get_all_files_mck: get_all_files_mck.return_value = files_to_verify with pytest.raises(ValueError): # We don't put things back into place after fsync # so a `ValueError` is raised self.viewer._verify_files(files_to_verify) assert len(fsync.call_args_list) == len(files_to_verify) + 1
def test_default_package_json(self): viewer = FileViewer(make_file(3, get_file('new-format-0.0.1.xpi'))) viewer.extract() assert viewer.get_default(None) == 'package.json'
def test_default_webextension_crx(self): viewer = FileViewer(make_file(2, get_file('webextension.crx'))) viewer.extract() assert viewer.get_default(None) == 'manifest.json'
class FilesBase(object): def login_as_admin(self): assert self.client.login(email='*****@*****.**') def login_as_reviewer(self): assert self.client.login(email='*****@*****.**') def setUp(self): super(FilesBase, self).setUp() self.addon = Addon.objects.get(pk=3615) self.dev = self.addon.authors.all()[0] self.regular = UserProfile.objects.get(pk=999) self.version = self.addon.versions.latest() self.file = self.version.all_files[0] p = [amo.PLATFORM_LINUX.id, amo.PLATFORM_WIN.id, amo.PLATFORM_MAC.id] self.file.update(platform=p[0]) files = [ ( 'dictionary-test.xpi', self.file), ( 'dictionary-test.xpi', File.objects.create( version=self.version, platform=p[1], hash='abc123', filename='dictionary-test.xpi')), ( 'dictionary-test-changed.xpi', File.objects.create( version=self.version, platform=p[2], hash='abc123', filename='dictionary-test.xpi'))] fixtures_base_path = os.path.join(settings.ROOT, files_fixtures) for xpi_file, file_obj in files: create_directory(os.path.dirname(file_obj.file_path)) shutil.copyfile( os.path.join(fixtures_base_path, xpi_file), file_obj.file_path) self.files = [x[1] for x in files] self.login_as_reviewer() self.file_viewer = FileViewer(self.file) def tearDown(self): self.file_viewer.cleanup() super(FilesBase, self).tearDown() def files_redirect(self, file): return reverse('files.redirect', args=[self.file.pk, file]) def files_serve(self, file): return reverse('files.serve', args=[self.file.pk, file]) def test_view_access_anon(self): self.client.logout() self.check_urls(403) def test_view_access_anon_view_source(self): # This is disallowed for now, see Bug 1353788 for more details. self.addon.update(view_source=True) self.file_viewer.extract() self.client.logout() self.check_urls(403) def test_view_access_reviewer(self): self.file_viewer.extract() self.check_urls(200) def test_view_access_reviewer_view_source(self): # This is disallowed for now, see Bug 1353788 for more details. self.addon.update(view_source=True) self.file_viewer.extract() self.check_urls(200) def test_view_access_developer(self): self.client.logout() assert self.client.login(email=self.dev.email) self.file_viewer.extract() self.check_urls(200) def test_view_access_reviewed(self): # This is disallowed for now, see Bug 1353788 for more details. self.addon.update(view_source=True) self.file_viewer.extract() self.client.logout() for status in amo.UNREVIEWED_FILE_STATUSES: self.addon.update(status=status) self.check_urls(403) for status in amo.REVIEWED_STATUSES: self.addon.update(status=status) self.check_urls(403) def test_view_access_developer_view_source(self): self.client.logout() assert self.client.login(email=self.dev.email) self.addon.update(view_source=True) self.file_viewer.extract() self.check_urls(200) def test_view_access_another_developer(self): self.client.logout() assert self.client.login(email=self.regular.email) self.file_viewer.extract() self.check_urls(403) def test_view_access_another_developer_view_source(self): # This is disallowed for now, see Bug 1353788 for more details. self.client.logout() assert self.client.login(email=self.regular.email) self.addon.update(view_source=True) self.file_viewer.extract() self.check_urls(403) def test_poll_extracted(self): self.file_viewer.extract() res = self.client.get(self.poll_url()) assert res.status_code == 200 assert json.loads(res.content)['status'] def test_poll_not_extracted(self): res = self.client.get(self.poll_url()) assert res.status_code == 200 assert not json.loads(res.content)['status'] def test_poll_extracted_anon(self): self.client.logout() res = self.client.get(self.poll_url()) assert res.status_code == 403 def test_content_headers(self): self.file_viewer.extract() res = self.client.get(self.file_url('install.js')) assert 'etag' in res._headers assert 'last-modified' in res._headers def test_content_headers_etag(self): self.file_viewer.extract() self.file_viewer.select('install.js') obj = getattr(self.file_viewer, 'left', self.file_viewer) etag = quote_etag(obj.selected.get('sha256')) res = self.client.get(self.file_url('install.js'), HTTP_IF_NONE_MATCH=etag) assert res.status_code == 304 def test_content_headers_if_modified(self): self.file_viewer.extract() self.file_viewer.select('install.js') obj = getattr(self.file_viewer, 'left', self.file_viewer) date = http_date(obj.selected.get('modified')) res = self.client.get(self.file_url('install.js'), HTTP_IF_MODIFIED_SINCE=date) assert res.status_code == 304 def test_file_header(self): self.file_viewer.extract() res = self.client.get(self.file_url(not_binary)) url = res.context['file_link']['url'] assert url == reverse('reviewers.review', args=[self.addon.slug]) def test_content_no_file(self): self.file_viewer.extract() res = self.client.get(self.file_url()) doc = pq(res.content) assert len(doc('#content')) == 0 def test_files(self): self.file_viewer.extract() res = self.client.get(self.file_url()) assert res.status_code == 200 assert 'files' in res.context def test_files_anon(self): self.client.logout() res = self.client.get(self.file_url()) assert res.status_code == 403 def test_files_file(self): self.file_viewer.extract() res = self.client.get(self.file_url(not_binary)) assert res.status_code == 200 assert 'selected' in res.context def test_files_back_link(self): self.file_viewer.extract() res = self.client.get(self.file_url(not_binary)) doc = pq(res.content) assert doc('#commands td')[-1].text_content() == 'Back to review' def test_files_back_link_anon(self): # This is disallowed for now, see Bug 1353788 for more details. self.file_viewer.extract() self.client.logout() self.addon.update(view_source=True) res = self.client.get(self.file_url(not_binary)) assert res.status_code == 403 def test_diff_redirect(self): ids = self.files[0].id, self.files[1].id res = self.client.post(self.file_url(), {'left': ids[0], 'right': ids[1]}) self.assert3xx(res, reverse('files.compare', args=ids)) def test_browse_redirect(self): ids = self.files[0].id, res = self.client.post(self.file_url(), {'left': ids[0]}) self.assert3xx(res, reverse('files.list', args=ids)) def test_browse_404(self): res = self.client.get('/files/browse/file/dont/exist.png', follow=True) assert res.status_code == 404 def test_invalid_redirect(self): res = self.client.post(self.file_url(), {}) self.assert3xx(res, self.file_url()) def test_file_chooser(self): res = self.client.get(self.file_url()) doc = pq(res.content) left = doc('#id_left') assert len(left) == 1 ver = left('optgroup') assert len(ver) == 1 assert ver.attr('label') == self.version.version files = ver('option') assert len(files) == 2 def test_file_chooser_coalescing(self): res = self.client.get(self.file_url()) doc = pq(res.content) unreviewed_file = doc('#id_left > optgroup > option.status-unreviewed') public_file = doc('#id_left > optgroup > option.status-public') assert public_file.text() == str(self.files[0].get_platform_display()) assert unreviewed_file.text() == ( '%s, %s' % (self.files[1].get_platform_display(), self.files[2].get_platform_display())) assert public_file.attr('value') == str(self.files[0].id) assert unreviewed_file.attr('value') == str(self.files[1].id) def test_file_chooser_disabled_coalescing(self): self.files[1].update(status=amo.STATUS_DISABLED) res = self.client.get(self.file_url()) doc = pq(res.content) disabled_file = doc('#id_left > optgroup > option.status-disabled') assert disabled_file.attr('value') == str(self.files[2].id) def test_files_for_unlisted_addon_returns_404(self): """Files browsing isn't allowed for unlisted addons.""" self.make_addon_unlisted(self.addon) assert self.client.get(self.file_url()).status_code == 404 def test_files_for_unlisted_addon_with_admin(self): """Files browsing is allowed for unlisted addons if you're admin.""" self.login_as_admin() self.make_addon_unlisted(self.addon) assert self.client.get(self.file_url()).status_code == 200 def test_all_versions_shown_for_admin(self): self.login_as_admin() listed_ver = version_factory( addon=self.addon, channel=amo.RELEASE_CHANNEL_LISTED, version='4.0', created=self.days_ago(1)) unlisted_ver = version_factory( addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED, version='5.0') assert self.addon.versions.count() == 3 res = self.client.get(self.file_url()) doc = pq(res.content) left_select = doc('#id_left') assert left_select('optgroup').attr('label') == self.version.version file_options = left_select('option.status-public') assert len(file_options) == 3, left_select.html() # Check the files in the list are the two we added and the default. assert file_options.eq(0).attr('value') == str( unlisted_ver.all_files[0].pk) assert file_options.eq(1).attr('value') == str( listed_ver.all_files[0].pk) assert file_options.eq(2).attr('value') == str(self.file.pk) # Check there are prefixes on the labels for the channels assert file_options.eq(0).text().endswith('[Self]') assert file_options.eq(1).text().endswith('[AMO]') assert file_options.eq(2).text().endswith('[AMO]') def test_channel_prefix_not_shown_when_no_mixed_channels(self): self.login_as_admin() version_factory(addon=self.addon, channel=amo.RELEASE_CHANNEL_LISTED) assert self.addon.versions.count() == 2 res = self.client.get(self.file_url()) doc = pq(res.content) left_select = doc('#id_left') assert left_select('optgroup').attr('label') == self.version.version # Check there are NO prefixes on the labels for the channels file_options = left_select('option.status-public') assert not file_options.eq(0).text().endswith('[Self]') assert not file_options.eq(1).text().endswith('[AMO]') assert not file_options.eq(2).text().endswith('[AMO]') def test_only_listed_versions_shown_for_reviewer(self): listed_ver = version_factory( addon=self.addon, channel=amo.RELEASE_CHANNEL_LISTED, version='4.0') version_factory( addon=self.addon, channel=amo.RELEASE_CHANNEL_UNLISTED, version='5.0') assert self.addon.versions.count() == 3 res = self.client.get(self.file_url()) doc = pq(res.content) left_select = doc('#id_left') assert left_select('optgroup').attr('label') == self.version.version # Check the files in the list are just the listed, and the default. file_options = left_select('option.status-public') assert len(file_options) == 2, left_select.html() assert file_options.eq(0).attr('value') == str( listed_ver.all_files[0].pk) assert file_options.eq(1).attr('value') == str(self.file.pk) # Check there are NO prefixes on the labels for the channels assert not file_options.eq(0).text().endswith('[AMO]') assert not file_options.eq(1).text().endswith('[AMO]')
class TestFileViewer(TestCase): def setUp(self): super(TestFileViewer, self).setUp() self.viewer = FileViewer(make_file(1, get_file('dictionary-test.xpi'))) def tearDown(self): self.viewer.cleanup() super(TestFileViewer, self).tearDown() def test_files_not_extracted(self): assert not self.viewer.is_extracted() def test_files_extracted(self): self.viewer.extract() assert self.viewer.is_extracted() def test_recurse_extract(self): self.viewer.src = get_file('recurse.xpi') self.viewer.extract() assert self.viewer.is_extracted() def test_recurse_contents(self): self.viewer.src = get_file('recurse.xpi') self.viewer.extract() files = self.viewer.get_files() # We do not extract nested .zip or .xpi files anymore assert files.keys() == [ u'recurse', u'recurse/chrome', u'recurse/chrome/test-root.txt', u'recurse/chrome/test.jar', u'recurse/notazip.jar', u'recurse/recurse.xpi', u'recurse/somejar.jar'] def test_locked(self): self.viewer.src = get_file('dictionary-test.xpi') # Lock was successfully attained assert self.viewer.extract() lock = flufl.lock.Lock(os.path.join( settings.TMP_PATH, 'file-viewer-%s.lock' % self.viewer.file.pk )) assert not lock.is_locked lock.lock() assert lock.is_locked # Not extracting, the viewer is locked, lock could not be attained assert not self.viewer.extract() def test_extract_file_locked_message(self): self.viewer.src = get_file('dictionary-test.xpi') assert not self.viewer.is_extracted() lock = flufl.lock.Lock(os.path.join( settings.TMP_PATH, 'file-viewer-%s.lock' % self.viewer.file.pk )) assert not lock.is_locked lock.lock() assert lock.is_locked msg = extract_file(self.viewer) assert str(msg.get()).startswith(u'File viewer is locked') msg.delete() def test_cleanup(self): self.viewer.extract() self.viewer.cleanup() assert not self.viewer.is_extracted() @freeze_time('2017-01-08 02:01:00') def test_dest(self): viewer = FileViewer(make_file(1, get_file('webextension.xpi'))) assert viewer.dest == os.path.join( settings.TMP_PATH, 'file_viewer', '0108', str(self.viewer.file.pk)) def test_isbinary(self): binary = self.viewer._is_binary for f in ['foo.rdf', 'foo.xml', 'foo.js', 'foo.py' 'foo.html', 'foo.txt', 'foo.dtd', 'foo.xul', 'foo.sh', 'foo.properties', 'foo.json', 'foo.src', 'CHANGELOG']: m, encoding = mimetypes.guess_type(f) assert not binary(m, f), '%s should not be binary' % f for f in ['foo.png', 'foo.gif', 'foo.exe', 'foo.swf']: m, encoding = mimetypes.guess_type(f) assert binary(m, f), '%s should be binary' % f filename = os.path.join(settings.TMP_PATH, 'test_isbinary') for txt in ['#!/usr/bin/python', '#python', u'\0x2']: open(filename, 'w').write(txt) m, encoding = mimetypes.guess_type(filename) assert not binary(m, filename), '%s should not be binary' % txt for txt in ['MZ']: open(filename, 'w').write(txt) m, encoding = mimetypes.guess_type(filename) assert binary(m, filename), '%s should be binary' % txt os.remove(filename) def test_truncate(self): truncate = self.viewer.truncate for x, y in (['foo.rdf', 'foo.rdf'], ['somelongfilename.rdf', 'somelongfilenam...rdf'], [u'unicode삮.txt', u'unicode\uc0ae.txt'], [u'unicodesomelong삮.txt', u'unicodesomelong...txt'], ['somelongfilename.somelongextension', 'somelongfilenam...somelonge..'],): assert truncate(x) == y def test_get_files_not_extracted_runs_extraction(self): self.viewer.src = get_file('dictionary-test.xpi') assert not self.viewer.is_extracted() self.viewer.get_files() assert self.viewer.is_extracted() def test_get_files_size(self): self.viewer.extract() files = self.viewer.get_files() assert len(files) == 14 def test_get_files_directory(self): self.viewer.extract() files = self.viewer.get_files() assert not files['install.js']['directory'] assert not files['install.js']['binary'] assert files['__MACOSX']['directory'] assert not files['__MACOSX']['binary'] def test_get_files_depth(self): self.viewer.extract() files = self.viewer.get_files() assert files['dictionaries/license.txt']['depth'] == 1 def test_bom(self): dest = os.path.join(settings.TMP_PATH, 'test_bom') open(dest, 'w').write('foo'.encode('utf-16')) self.viewer.select('foo') self.viewer.selected = {'full': dest, 'size': 1} assert self.viewer.read_file() == u'foo' os.remove(dest) def test_syntax(self): for filename, syntax in [('foo.rdf', 'xml'), ('foo.xul', 'xml'), ('foo.json', 'js'), ('foo.jsm', 'js'), ('foo.htm', 'html'), ('foo.bar', 'plain'), ('foo.diff', 'plain')]: assert self.viewer.get_syntax(filename) == syntax def test_file_order(self): self.viewer.extract() dest = self.viewer.dest open(os.path.join(dest, 'chrome.manifest'), 'w') subdir = os.path.join(dest, 'chrome') os.mkdir(subdir) open(os.path.join(subdir, 'foo'), 'w') cache.clear() files = self.viewer.get_files().keys() rt = files.index(u'chrome') assert files[rt:rt + 3] == [u'chrome', u'chrome/foo', u'dictionaries'] @patch.object(settings, 'FILE_VIEWER_SIZE_LIMIT', 5) def test_file_size(self): self.viewer.extract() self.viewer.get_files() self.viewer.select('install.js') res = self.viewer.read_file() assert res == '' assert self.viewer.selected['msg'].startswith('File size is') @pytest.mark.needs_locales_compilation @patch.object(settings, 'FILE_VIEWER_SIZE_LIMIT', 5) def test_file_size_unicode(self): with self.activate(locale='he'): self.viewer.extract() self.viewer.get_files() self.viewer.select('install.js') res = self.viewer.read_file() assert res == '' assert ( self.viewer.selected['msg'].startswith(u'גודל הקובץ חורג')) @patch.object(settings, 'FILE_UNZIP_SIZE_LIMIT', 5) def test_contents_size(self): self.assertRaises(forms.ValidationError, self.viewer.extract) def test_default(self): self.viewer.extract() assert self.viewer.get_default(None) == 'install.rdf' def test_default_webextension(self): viewer = FileViewer(make_file(2, get_file('webextension.xpi'))) viewer.extract() assert viewer.get_default(None) == 'manifest.json' def test_default_webextension_zip(self): viewer = FileViewer(make_file(2, get_file('webextension_no_id.zip'))) viewer.extract() assert viewer.get_default(None) == 'manifest.json' def test_default_webextension_crx(self): viewer = FileViewer(make_file(2, get_file('webextension.crx'))) viewer.extract() assert viewer.get_default(None) == 'manifest.json' def test_default_package_json(self): viewer = FileViewer(make_file(3, get_file('new-format-0.0.1.xpi'))) viewer.extract() assert viewer.get_default(None) == 'package.json' def test_delete_mid_read(self): self.viewer.extract() self.viewer.select('install.js') os.remove(os.path.join(self.viewer.dest, 'install.js')) res = self.viewer.read_file() assert res == '' assert self.viewer.selected['msg'].startswith('That file no') @patch('olympia.files.file_viewer.get_sha256') def test_delete_mid_tree(self, get_sha256): get_sha256.side_effect = IOError('ow') self.viewer.extract() with self.assertRaises(IOError): self.viewer.get_files() @patch('olympia.files.file_viewer.os.fsync') def test_verify_files_doesnt_call_fsync_regularly(self, fsync): self.viewer.extract() assert not fsync.called @patch('olympia.files.file_viewer.os.fsync') def test_verify_files_calls_fsync_on_differences(self, fsync): self.viewer.extract() assert not fsync.called files_to_verify = get_all_files(self.viewer.dest) files_to_verify.pop() module_path = 'olympia.files.file_viewer.get_all_files' with patch(module_path) as get_all_files_mck: get_all_files_mck.return_value = files_to_verify with pytest.raises(ValueError): # We don't put things back into place after fsync # so a `ValueError` is raised self.viewer._verify_files(files_to_verify) assert len(fsync.call_args_list) == len(files_to_verify) + 1
def test_default_package_json(self): viewer = FileViewer(make_file(3, get_file('new-format-0.0.1.xpi'))) viewer.extract() assert viewer.get_default(None) == 'package.json'
def test_default_webextension_crx(self): viewer = FileViewer(make_file(2, get_file('webextension.crx'))) viewer.extract() assert viewer.get_default(None) == 'manifest.json'