974 строки
39 KiB
Python
974 строки
39 KiB
Python
# -*- coding: utf-8 -*-
|
|
import hashlib
|
|
|
|
from datetime import datetime, timedelta
|
|
from django.conf import settings
|
|
from django.core.files.storage import default_storage as storage
|
|
|
|
import mock
|
|
from nose.tools import eq_
|
|
from pyquery import PyQuery
|
|
import waffle
|
|
|
|
import amo
|
|
import amo.tests
|
|
from amo.tests import addon_factory
|
|
from amo.urlresolvers import reverse
|
|
from addons.models import Addon, CompatOverride, CompatOverrideRange
|
|
from addons.tests.test_views import TestMobile
|
|
from applications.models import AppVersion, Application
|
|
from devhub.models import ActivityLog
|
|
from files.models import File, Platform
|
|
from files.tests.test_models import UploadTest
|
|
from users.models import UserProfile
|
|
from versions import views
|
|
from versions.models import Version, ApplicationsVersions
|
|
from versions.compare import (MAXVERSION, version_int, dict_from_int,
|
|
version_dict)
|
|
|
|
|
|
def test_version_int():
|
|
"""Tests that version_int. Corrects our versions."""
|
|
eq_(version_int('3.5.0a1pre2'), 3050000001002)
|
|
eq_(version_int(''), 200100)
|
|
eq_(version_int('0'), 200100)
|
|
eq_(version_int('*'), 99000000200100)
|
|
eq_(version_int(MAXVERSION), MAXVERSION)
|
|
eq_(version_int(MAXVERSION + 1), MAXVERSION)
|
|
eq_(version_int('9999999'), MAXVERSION)
|
|
|
|
|
|
def test_version_int_compare():
|
|
eq_(version_int('3.6.*'), version_int('3.6.99'))
|
|
assert version_int('3.6.*') > version_int('3.6.8')
|
|
|
|
|
|
def test_version_asterix_compare():
|
|
eq_(version_int('*'), version_int('99'))
|
|
assert version_int('98.*') < version_int('*')
|
|
eq_(version_int('5.*'), version_int('5.99'))
|
|
assert version_int('5.*') > version_int('5.0.*')
|
|
|
|
|
|
def test_version_dict():
|
|
eq_(version_dict('5.0'),
|
|
{'major': 5,
|
|
'minor1': 0,
|
|
'minor2': None,
|
|
'minor3': None,
|
|
'alpha': None,
|
|
'alpha_ver': None,
|
|
'pre': None,
|
|
'pre_ver': None})
|
|
|
|
|
|
def test_version_int_unicode():
|
|
eq_(version_int(u'\u2322 ugh stephend'), 200100)
|
|
|
|
|
|
def test_dict_from_int():
|
|
d = dict_from_int(3050000001002)
|
|
eq_(d['major'], 3)
|
|
eq_(d['minor1'], 5)
|
|
eq_(d['minor2'], 0)
|
|
eq_(d['minor3'], 0)
|
|
eq_(d['alpha'], 'a')
|
|
eq_(d['alpha_ver'], 1)
|
|
eq_(d['pre'], 'pre')
|
|
eq_(d['pre_ver'], 2)
|
|
|
|
|
|
class TestVersion(amo.tests.TestCase):
|
|
fixtures = ['base/apps', 'base/addon_3615', 'base/admin',
|
|
'base/platforms']
|
|
|
|
def setUp(self):
|
|
self.version = Version.objects.get(pk=81551)
|
|
|
|
def named_plat(self, ids):
|
|
return [amo.PLATFORMS[i].shortname for i in ids]
|
|
|
|
def target_mobile(self):
|
|
app = Application.objects.get(pk=amo.MOBILE.id)
|
|
app_vr = AppVersion.objects.create(application=app, version='1.0')
|
|
ApplicationsVersions.objects.create(version=self.version,
|
|
application=app,
|
|
min=app_vr, max=app_vr)
|
|
|
|
def test_compatible_apps(self):
|
|
v = Version.objects.get(pk=81551)
|
|
|
|
assert amo.FIREFOX in v.compatible_apps, "Missing Firefox >_<"
|
|
|
|
def test_supported_platforms(self):
|
|
v = Version.objects.get(pk=81551)
|
|
assert amo.PLATFORM_ALL in v.supported_platforms
|
|
|
|
def test_mobile_version_supports_only_mobile_platforms(self):
|
|
self.version.apps.all().delete()
|
|
self.target_mobile()
|
|
eq_(sorted(self.named_plat(self.version.compatible_platforms())),
|
|
['allmobile', u'android', u'maemo'])
|
|
|
|
def test_mixed_version_supports_all_platforms(self):
|
|
self.target_mobile()
|
|
eq_(sorted(self.named_plat(self.version.compatible_platforms())),
|
|
['all', 'allmobile', 'android', 'linux', 'mac', 'maemo',
|
|
'windows'])
|
|
|
|
def test_non_mobile_version_supports_non_mobile_platforms(self):
|
|
eq_(sorted(self.named_plat(self.version.compatible_platforms())),
|
|
['all', 'linux', 'mac', 'windows'])
|
|
|
|
def test_major_minor(self):
|
|
"""Check that major/minor/alpha is getting set."""
|
|
v = Version(version='3.0.12b2')
|
|
eq_(v.major, 3)
|
|
eq_(v.minor1, 0)
|
|
eq_(v.minor2, 12)
|
|
eq_(v.minor3, None)
|
|
eq_(v.alpha, 'b')
|
|
eq_(v.alpha_ver, 2)
|
|
|
|
v = Version(version='3.6.1apre2+')
|
|
eq_(v.major, 3)
|
|
eq_(v.minor1, 6)
|
|
eq_(v.minor2, 1)
|
|
eq_(v.alpha, 'a')
|
|
eq_(v.pre, 'pre')
|
|
eq_(v.pre_ver, 2)
|
|
|
|
v = Version(version='')
|
|
eq_(v.major, None)
|
|
eq_(v.minor1, None)
|
|
eq_(v.minor2, None)
|
|
eq_(v.minor3, None)
|
|
|
|
def test_has_files(self):
|
|
v = Version.objects.get(pk=81551)
|
|
assert v.has_files, 'Version with files not recognized.'
|
|
|
|
v.files.all().delete()
|
|
v = Version.objects.get(pk=81551)
|
|
assert not v.has_files, 'Version without files not recognized.'
|
|
|
|
def _get_version(self, status):
|
|
v = Version()
|
|
v.all_files = [mock.Mock()]
|
|
v.all_files[0].status = status
|
|
return v
|
|
|
|
def test_is_unreviewed(self):
|
|
assert self._get_version(amo.STATUS_UNREVIEWED).is_unreviewed
|
|
assert self._get_version(amo.STATUS_PENDING).is_unreviewed
|
|
assert not self._get_version(amo.STATUS_PUBLIC).is_unreviewed
|
|
|
|
def test_version_delete(self):
|
|
version = Version.objects.get(pk=81551)
|
|
version.delete()
|
|
|
|
addon = Addon.objects.no_cache().get(pk=3615)
|
|
assert not Version.objects.filter(addon=addon).exists()
|
|
assert not Version.with_deleted.filter(addon=addon).exists()
|
|
|
|
@mock.patch('versions.models.settings.MARKETPLACE', True)
|
|
@mock.patch('versions.models.storage')
|
|
def test_version_delete_marketplace(self, storage_mock):
|
|
version = Version.objects.get(pk=81551)
|
|
version.delete()
|
|
addon = Addon.objects.no_cache().get(pk=3615)
|
|
assert addon
|
|
|
|
assert not Version.objects.filter(addon=addon).exists()
|
|
assert Version.with_deleted.filter(addon=addon).exists()
|
|
|
|
assert not storage_mock.delete.called
|
|
|
|
@mock.patch('versions.models.settings.MARKETPLACE', True)
|
|
@mock.patch('versions.models.storage')
|
|
def test_packaged_version_delete_marketplace(self, storage_mock):
|
|
addon = Addon.objects.no_cache().get(pk=3615)
|
|
addon.update(is_packaged=True)
|
|
version = Version.objects.get(pk=81551)
|
|
version.delete()
|
|
|
|
assert not Version.objects.filter(addon=addon).exists()
|
|
assert Version.with_deleted.filter(addon=addon).exists()
|
|
|
|
assert storage_mock.delete.called
|
|
|
|
def test_version_delete_files(self):
|
|
version = Version.objects.get(pk=81551)
|
|
eq_(version.files.count(), 1)
|
|
version.delete()
|
|
eq_(version.files.count(), 0)
|
|
|
|
def test_version_delete_logs(self):
|
|
user = UserProfile.objects.get(pk=55021)
|
|
amo.set_user(user)
|
|
# The transform don't know bout my users.
|
|
version = Version.objects.get(pk=81551)
|
|
eq_(ActivityLog.objects.count(), 0)
|
|
version.delete()
|
|
eq_(ActivityLog.objects.count(), 2)
|
|
|
|
def test_version_is_allowed_upload(self):
|
|
version = Version.objects.get(pk=81551)
|
|
version.files.all().delete()
|
|
# The transform don't know bout my deletions.
|
|
version = Version.objects.get(pk=81551)
|
|
assert version.is_allowed_upload()
|
|
|
|
def test_version_is_not_allowed_upload(self):
|
|
version = Version.objects.get(pk=81551)
|
|
version.files.all().delete()
|
|
for platform in [amo.PLATFORM_LINUX.id,
|
|
amo.PLATFORM_WIN.id,
|
|
amo.PLATFORM_BSD.id]:
|
|
file = File(platform_id=platform, version=version)
|
|
file.save()
|
|
version = Version.objects.get(pk=81551)
|
|
assert version.is_allowed_upload()
|
|
file = File(platform_id=amo.PLATFORM_MAC.id, version=version)
|
|
file.save()
|
|
# The transform don't know bout my new files.
|
|
version = Version.objects.no_cache().get(pk=81551)
|
|
assert not version.is_allowed_upload()
|
|
|
|
def test_version_is_not_allowed_upload_full(self):
|
|
version = Version.objects.get(pk=81551)
|
|
version.files.all().delete()
|
|
for platform in [amo.PLATFORM_LINUX.id,
|
|
amo.PLATFORM_WIN.id,
|
|
amo.PLATFORM_MAC.id]:
|
|
file = File(platform_id=platform, version=version)
|
|
file.save()
|
|
# The transform don't know bout my new files.
|
|
version = Version.objects.get(pk=81551)
|
|
assert not version.is_allowed_upload()
|
|
|
|
def test_version_is_allowed_upload_search(self):
|
|
version = Version.objects.get(pk=81551)
|
|
version.addon.type = amo.ADDON_SEARCH
|
|
version.addon.save()
|
|
version.files.all()[0].delete()
|
|
# The transform don't know bout my deletions.
|
|
version = Version.objects.get(pk=81551)
|
|
assert version.is_allowed_upload()
|
|
|
|
def test_version_is_not_allowed_upload_search(self):
|
|
version = Version.objects.get(pk=81551)
|
|
version.addon.type = amo.ADDON_SEARCH
|
|
version.addon.save()
|
|
assert not version.is_allowed_upload()
|
|
|
|
def test_version_is_allowed_upload_all(self):
|
|
version = Version.objects.get(pk=81551)
|
|
assert not version.is_allowed_upload()
|
|
|
|
def test_mobile_all_version_is_not_allowed_upload(self):
|
|
self.target_mobile()
|
|
self.version.files.all().update(platform=amo.PLATFORM_ALL_MOBILE.id)
|
|
assert not self.version.is_allowed_upload()
|
|
|
|
@mock.patch('files.models.File.hide_disabled_file')
|
|
def test_new_version_disable_old_unreviewed(self, hide_mock):
|
|
addon = Addon.objects.get(id=3615)
|
|
# The status doesn't change for public files.
|
|
qs = File.objects.filter(version=addon.current_version)
|
|
eq_(qs.all()[0].status, amo.STATUS_PUBLIC)
|
|
Version.objects.create(addon=addon)
|
|
eq_(qs.all()[0].status, amo.STATUS_PUBLIC)
|
|
assert not hide_mock.called
|
|
|
|
qs.update(status=amo.STATUS_UNREVIEWED)
|
|
version = Version.objects.create(addon=addon)
|
|
version.disable_old_files()
|
|
eq_(qs.all()[0].status, amo.STATUS_DISABLED)
|
|
addon.current_version.all_files[0]
|
|
assert hide_mock.called
|
|
|
|
def test_new_version_beta(self):
|
|
addon = Addon.objects.get(id=3615)
|
|
qs = File.objects.filter(version=addon.current_version)
|
|
qs.update(status=amo.STATUS_UNREVIEWED)
|
|
|
|
version = Version.objects.create(addon=addon)
|
|
File.objects.create(version=version, status=amo.STATUS_BETA)
|
|
version.disable_old_files()
|
|
eq_(qs.all()[0].status, amo.STATUS_UNREVIEWED)
|
|
|
|
def test_version_int(self):
|
|
version = Version.objects.get(pk=81551)
|
|
version.save()
|
|
eq_(version.version_int, 2017200200100)
|
|
|
|
def test_large_version_int(self):
|
|
# This version will fail to be written to the version_int
|
|
# table because the resulting int is bigger than mysql bigint.
|
|
version = Version.objects.get(pk=81551)
|
|
version.version = '1237.2319.32161734.2383290.34'
|
|
version.save()
|
|
eq_(version.version_int, None)
|
|
|
|
def test_version_update_info(self):
|
|
addon = Addon.objects.get(pk=3615)
|
|
r = self.client.get(reverse('addons.versions.update_info',
|
|
args=(addon.slug, self.version.version)))
|
|
eq_(r.status_code, 200)
|
|
eq_(r['Content-Type'], 'application/xhtml+xml')
|
|
eq_(PyQuery(r.content)('p').html(), 'Fix for an important bug')
|
|
|
|
# Test update info in another language.
|
|
with self.activate(locale='fr'):
|
|
r = self.client.get(reverse('addons.versions.update_info',
|
|
args=(addon.slug,
|
|
self.version.version)))
|
|
eq_(r.status_code, 200)
|
|
eq_(r['Content-Type'], 'application/xhtml+xml')
|
|
assert '<br/>' in r.content, (
|
|
'Should be using XHTML self-closing tags!')
|
|
eq_(PyQuery(r.content)('p').html(),
|
|
u"Quelque chose en français.<br/><br/>Quelque chose d'autre.")
|
|
|
|
def test_version_update_info_legacy_redirect(self):
|
|
r = self.client.get('/versions/updateInfo/%s' % self.version.id,
|
|
follow=True)
|
|
url = reverse('addons.versions.update_info',
|
|
args=(self.version.addon.slug, self.version.version))
|
|
self.assertRedirects(r, url, 301)
|
|
|
|
def test_is_compatible(self):
|
|
# Base test for fixture before the rest.
|
|
addon = Addon.objects.get(id=3615)
|
|
version = amo.tests.version_factory(addon=addon)
|
|
eq_(version.is_compatible[0], True)
|
|
eq_(version.is_compatible_app(amo.FIREFOX), True)
|
|
|
|
def test_is_compatible_type(self):
|
|
# Only ADDON_EXTENSIONs should be compatible.
|
|
addon = Addon.objects.get(id=3615)
|
|
version = amo.tests.version_factory(addon=addon)
|
|
addon.update(type=amo.ADDON_PERSONA)
|
|
eq_(version.is_compatible[0], False)
|
|
eq_(version.is_compatible_app(amo.FIREFOX), True)
|
|
|
|
def test_is_compatible_strict_opt_in(self):
|
|
# Add-ons opting into strict compatibility should not be compatible.
|
|
addon = Addon.objects.get(id=3615)
|
|
version = amo.tests.version_factory(addon=addon)
|
|
file = version.all_files[0]
|
|
file.update(strict_compatibility=True)
|
|
eq_(version.is_compatible[0], False)
|
|
assert 'strict compatibility' in ''.join(version.is_compatible[1])
|
|
eq_(version.is_compatible_app(amo.FIREFOX), True)
|
|
|
|
def test_is_compatible_binary_components(self):
|
|
# Add-ons using binary components should not be compatible.
|
|
addon = Addon.objects.get(id=3615)
|
|
version = amo.tests.version_factory(addon=addon)
|
|
file = version.all_files[0]
|
|
file.update(binary_components=True)
|
|
eq_(version.is_compatible[0], False)
|
|
assert 'binary components' in ''.join(version.is_compatible[1])
|
|
eq_(version.is_compatible_app(amo.FIREFOX), True)
|
|
|
|
def test_is_compatible_app_max_version(self):
|
|
# Add-ons with max app version < 4.0 should not be compatible.
|
|
addon = Addon.objects.get(id=3615)
|
|
version = amo.tests.version_factory(addon=addon, max_app_version='3.5')
|
|
eq_(version.is_compatible_app(amo.FIREFOX), False)
|
|
# An app that isn't supported should also be False.
|
|
eq_(version.is_compatible_app(amo.THUNDERBIRD), False)
|
|
# An app that can't do d2c should also be False.
|
|
eq_(version.is_compatible_app(amo.UNKNOWN_APP), False)
|
|
|
|
def test_compat_override_app_versions(self):
|
|
app = Application.objects.get(pk=1)
|
|
addon = Addon.objects.get(id=3615)
|
|
version = amo.tests.version_factory(addon=addon)
|
|
co = CompatOverride.objects.create(addon=addon)
|
|
CompatOverrideRange.objects.create(compat=co, app=app, min_version='0',
|
|
max_version=version.version,
|
|
min_app_version='10.0a1',
|
|
max_app_version='10.*')
|
|
eq_(version.compat_override_app_versions(), [('10.0a1', '10.*')])
|
|
|
|
def test_compat_override_app_versions_wildcard(self):
|
|
app = Application.objects.get(pk=1)
|
|
addon = Addon.objects.get(id=3615)
|
|
version = amo.tests.version_factory(addon=addon)
|
|
co = CompatOverride.objects.create(addon=addon)
|
|
CompatOverrideRange.objects.create(compat=co, app=app, min_version='0',
|
|
max_version='*',
|
|
min_app_version='10.0a1',
|
|
max_app_version='10.*')
|
|
eq_(version.compat_override_app_versions(), [('10.0a1', '10.*')])
|
|
|
|
@mock.patch('addons.models.Addon.invalidate_d2c_versions')
|
|
def test_invalidate_d2c_version_signals_on_delete(self, inv_mock):
|
|
version = Addon.objects.get(pk=3615).current_version
|
|
version.delete()
|
|
assert inv_mock.called
|
|
|
|
@mock.patch('addons.models.Addon.invalidate_d2c_versions')
|
|
def test_invalidate_d2c_version_signals_on_save(self, inv_mock):
|
|
addon = Addon.objects.get(pk=3615)
|
|
amo.tests.version_factory(addon=addon)
|
|
assert inv_mock.called
|
|
|
|
def test_app_feature_creation_app(self):
|
|
app = Addon.objects.create(type=amo.ADDON_WEBAPP)
|
|
ver = Version.objects.create(addon=app)
|
|
assert ver.features, 'AppFeatures was not created with version.'
|
|
|
|
|
|
class TestViews(amo.tests.TestCase):
|
|
fixtures = ['addons/eula+contrib-addon', 'base/apps']
|
|
|
|
def setUp(self):
|
|
self.old_perpage = views.PER_PAGE
|
|
views.PER_PAGE = 1
|
|
self.addon = Addon.objects.get(id=11730)
|
|
|
|
def tearDown(self):
|
|
views.PER_PAGE = self.old_perpage
|
|
|
|
def test_version_detail(self):
|
|
base = '/en-US/firefox/addon/%s/versions/' % self.addon.slug
|
|
urls = [(v.version, reverse('addons.versions',
|
|
args=[self.addon.slug, v.version]))
|
|
for v in self.addon.versions.all()]
|
|
|
|
version, url = urls[0]
|
|
r = self.client.get(url, follow=True)
|
|
self.assertRedirects(r, base + '?page=1#version-%s' % version)
|
|
|
|
version, url = urls[1]
|
|
r = self.client.get(url, follow=True)
|
|
self.assertRedirects(r, base + '?page=2#version-%s' % version)
|
|
|
|
def test_version_detail_404(self):
|
|
r = self.client.get(reverse('addons.versions',
|
|
args=[self.addon.slug, 2]))
|
|
eq_(r.status_code, 404)
|
|
|
|
def get_content(self):
|
|
url = reverse('addons.versions', args=[self.addon.slug])
|
|
return PyQuery(self.client.get(url).content)
|
|
|
|
def test_version_source(self):
|
|
self.addon.update(view_source=True)
|
|
eq_(len(self.get_content()('a.source-code')), 1)
|
|
|
|
def test_version_no_source_one(self):
|
|
eq_(len(self.get_content()('a.source-code')), 0)
|
|
|
|
def test_version_no_source_two(self):
|
|
self.addon.update(view_source=True, status=amo.STATUS_NULL)
|
|
eq_(len(self.get_content()('a.source-code')), 0)
|
|
|
|
def test_version_link(self):
|
|
addon = Addon.objects.get(id=11730)
|
|
version = addon.current_version.version
|
|
url = reverse('addons.versions', args=[addon.slug])
|
|
doc = PyQuery(self.client.get(url).content)
|
|
link = doc('.version h3 > a').attr('href')
|
|
eq_(link, reverse('addons.versions', args=[addon.slug, version]))
|
|
eq_(doc('.version').attr('id'), 'version-%s' % version)
|
|
|
|
|
|
class TestFeeds(amo.tests.TestCase):
|
|
fixtures = ['addons/eula+contrib-addon', 'base/apps']
|
|
|
|
def test_feed_elements_present(self):
|
|
"""specific elements are present and reasonably well formed"""
|
|
url = reverse('addons.versions.rss', args=['a11730'])
|
|
r = self.client.get(url, follow=True)
|
|
doc = PyQuery(r.content)
|
|
eq_(doc('rss channel title')[0].text,
|
|
'IPv6 Google Search Version History')
|
|
assert doc('rss channel link')[0].text.endswith('/en-US/firefox/')
|
|
# assert <description> is present
|
|
assert len(doc('rss channel description')[0].text) > 0
|
|
# description doesn not contain the default object to string
|
|
desc_elem = doc('rss channel description')[0]
|
|
assert 'Content-Type:' not in desc_elem
|
|
# title present
|
|
assert len(doc('rss channel item title')[0].text) > 0
|
|
# link present and well formed
|
|
item_link = doc('rss channel item link')[0]
|
|
assert item_link.text.endswith('/addon/a11730/versions/20090521')
|
|
# guid present
|
|
assert len(doc('rss channel item guid')[0].text) > 0
|
|
# proper date format for item
|
|
item_pubdate = doc('rss channel item pubDate')[0]
|
|
assert item_pubdate.text == 'Thu, 21 May 2009 05:37:15 -0700'
|
|
|
|
|
|
class TestDownloadsBase(amo.tests.TestCase):
|
|
fixtures = ['base/apps', 'base/addon_5299_gcal', 'base/users']
|
|
|
|
def setUp(self):
|
|
self.addon = Addon.objects.get(id=5299)
|
|
self.file = File.objects.get(id=33046)
|
|
self.beta_file = File.objects.get(id=64874)
|
|
self.file_url = reverse('downloads.file', args=[self.file.id])
|
|
self.latest_url = reverse('downloads.latest', args=[self.addon.slug])
|
|
|
|
def assert_served_by_host(self, response, host, file_=None):
|
|
if not file_:
|
|
file_ = self.file
|
|
eq_(response.status_code, 302)
|
|
eq_(response['Location'],
|
|
'%s/%s/%s' % (host, self.addon.id, file_.filename))
|
|
eq_(response['X-Target-Digest'], file_.hash)
|
|
|
|
def assert_served_internally(self, response):
|
|
eq_(response.status_code, 200)
|
|
eq_(response[settings.XSENDFILE_HEADER], self.file.guarded_file_path)
|
|
|
|
def assert_served_locally(self, response, file_=None, attachment=False):
|
|
host = settings.LOCAL_MIRROR_URL
|
|
if attachment:
|
|
host += '/_attachments'
|
|
self.assert_served_by_host(response, host, file_)
|
|
|
|
def assert_served_by_mirror(self, response, file_=None):
|
|
self.assert_served_by_host(response, settings.MIRROR_URL, file_)
|
|
|
|
|
|
class TestDownloads(TestDownloadsBase):
|
|
|
|
def test_file_404(self):
|
|
r = self.client.get(reverse('downloads.file', args=[234]))
|
|
eq_(r.status_code, 404)
|
|
|
|
def test_public(self):
|
|
eq_(self.addon.status, amo.STATUS_PUBLIC)
|
|
eq_(self.file.status, amo.STATUS_PUBLIC)
|
|
self.assert_served_by_mirror(self.client.get(self.file_url))
|
|
|
|
def test_public_addon_unreviewed_file(self):
|
|
self.file.status = amo.STATUS_UNREVIEWED
|
|
self.file.save()
|
|
self.assert_served_locally(self.client.get(self.file_url))
|
|
|
|
def test_unreviewed_addon(self):
|
|
self.addon.status = amo.STATUS_PENDING
|
|
self.addon.save()
|
|
self.assert_served_locally(self.client.get(self.file_url))
|
|
|
|
def test_admin_disabled_404(self):
|
|
self.addon.update(status=amo.STATUS_DISABLED)
|
|
eq_(self.client.get(self.file_url).status_code, 404)
|
|
|
|
def test_user_disabled_404(self):
|
|
self.addon.update(disabled_by_user=True)
|
|
eq_(self.client.get(self.file_url).status_code, 404)
|
|
|
|
def test_file_disabled_anon_404(self):
|
|
self.file.update(status=amo.STATUS_DISABLED)
|
|
eq_(self.client.get(self.file_url).status_code, 404)
|
|
|
|
def test_file_disabled_unprivileged_404(self):
|
|
assert self.client.login(username='regular@mozilla.com',
|
|
password='password')
|
|
self.file.update(status=amo.STATUS_DISABLED)
|
|
eq_(self.client.get(self.file_url).status_code, 404)
|
|
|
|
def test_file_disabled_ok_for_author(self):
|
|
self.file.update(status=amo.STATUS_DISABLED)
|
|
assert self.client.login(username='g@gmail.com', password='password')
|
|
self.assert_served_internally(self.client.get(self.file_url))
|
|
|
|
def test_file_disabled_ok_for_editor(self):
|
|
self.file.update(status=amo.STATUS_DISABLED)
|
|
self.client.login(username='editor@mozilla.com', password='password')
|
|
self.assert_served_internally(self.client.get(self.file_url))
|
|
|
|
def test_file_disabled_ok_for_admin(self):
|
|
self.file.update(status=amo.STATUS_DISABLED)
|
|
self.client.login(username='admin@mozilla.com', password='password')
|
|
self.assert_served_internally(self.client.get(self.file_url))
|
|
|
|
def test_admin_disabled_ok_for_author(self):
|
|
# downloads_controller.php claims that add-on authors should be able to
|
|
# download their disabled files.
|
|
self.addon.update(status=amo.STATUS_DISABLED)
|
|
assert self.client.login(username='g@gmail.com', password='password')
|
|
self.assert_served_internally(self.client.get(self.file_url))
|
|
|
|
def test_admin_disabled_ok_for_admin(self):
|
|
self.addon.update(status=amo.STATUS_DISABLED)
|
|
self.client.login(username='admin@mozilla.com', password='password')
|
|
self.assert_served_internally(self.client.get(self.file_url))
|
|
|
|
def test_user_disabled_ok_for_author(self):
|
|
self.addon.update(disabled_by_user=True)
|
|
assert self.client.login(username='g@gmail.com', password='password')
|
|
self.assert_served_internally(self.client.get(self.file_url))
|
|
|
|
def test_user_disabled_ok_for_admin(self):
|
|
self.addon.update(disabled_by_user=True)
|
|
self.client.login(username='admin@mozilla.com', password='password')
|
|
self.assert_served_internally(self.client.get(self.file_url))
|
|
|
|
def test_type_attachment(self):
|
|
self.assert_served_by_mirror(self.client.get(self.file_url))
|
|
url = reverse('downloads.file', args=[self.file.id, 'attachment'])
|
|
self.assert_served_locally(self.client.get(url), attachment=True)
|
|
|
|
def test_nonbrowser_app(self):
|
|
url = self.file_url.replace('firefox', 'thunderbird')
|
|
self.assert_served_locally(self.client.get(url), attachment=True)
|
|
|
|
def test_mirror_delay(self):
|
|
self.file.datestatuschanged = datetime.now()
|
|
self.file.save()
|
|
self.assert_served_locally(self.client.get(self.file_url))
|
|
|
|
t = datetime.now() - timedelta(minutes=settings.MIRROR_DELAY + 10)
|
|
self.file.datestatuschanged = t
|
|
self.file.save()
|
|
self.assert_served_by_mirror(self.client.get(self.file_url))
|
|
|
|
def test_trailing_filename(self):
|
|
url = self.file_url + self.file.filename
|
|
self.assert_served_by_mirror(self.client.get(url))
|
|
|
|
def test_beta_file(self):
|
|
url = reverse('downloads.file', args=[self.beta_file.id])
|
|
self.assert_served_by_mirror(self.client.get(url),
|
|
file_=self.beta_file)
|
|
|
|
def test_null_datestatuschanged(self):
|
|
self.file.update(datestatuschanged=None)
|
|
self.assert_served_locally(self.client.get(self.file_url))
|
|
|
|
def test_public_addon_beta_file(self):
|
|
self.file.update(status=amo.STATUS_BETA)
|
|
self.addon.update(status=amo.STATUS_PUBLIC)
|
|
self.assert_served_by_mirror(self.client.get(self.file_url))
|
|
|
|
def test_beta_addon_beta_file(self):
|
|
self.addon.update(status=amo.STATUS_BETA)
|
|
self.file.update(status=amo.STATUS_BETA)
|
|
self.assert_served_locally(self.client.get(self.file_url))
|
|
|
|
def test_no_premium(self):
|
|
self.addon.update(premium_type=amo.ADDON_PREMIUM)
|
|
eq_(self.client.get(self.file_url).status_code, 403)
|
|
|
|
|
|
class TestDownloadsLatest(TestDownloadsBase):
|
|
|
|
def setUp(self):
|
|
super(TestDownloadsLatest, self).setUp()
|
|
self.platform = Platform.objects.create(id=5)
|
|
|
|
def assert_served_by_mirror(self, response):
|
|
# Follow one more hop to hit the downloads.files view.
|
|
r = self.client.get(response['Location'])
|
|
super(TestDownloadsLatest, self).assert_served_by_mirror(r)
|
|
|
|
def assert_served_locally(self, response, file_=None, attachment=False):
|
|
# Follow one more hop to hit the downloads.files view.
|
|
r = self.client.get(response['Location'])
|
|
super(TestDownloadsLatest, self).assert_served_locally(
|
|
r, file_, attachment)
|
|
|
|
def test_404(self):
|
|
url = reverse('downloads.latest', args=[123])
|
|
eq_(self.client.get(url).status_code, 404)
|
|
|
|
def test_type_none(self):
|
|
r = self.client.get(self.latest_url)
|
|
eq_(r.status_code, 302)
|
|
url = self.file_url + '/' + self.file.filename
|
|
assert r['Location'].endswith(url), r['Location']
|
|
|
|
def test_success(self):
|
|
assert self.addon.current_version
|
|
self.assert_served_by_mirror(self.client.get(self.latest_url))
|
|
|
|
def test_platform(self):
|
|
# We still match PLATFORM_ALL.
|
|
url = reverse('downloads.latest',
|
|
kwargs={'addon_id': self.addon.slug, 'platform': 5})
|
|
self.assert_served_by_mirror(self.client.get(url))
|
|
|
|
# And now we match the platform in the url.
|
|
self.file.platform = self.platform
|
|
self.file.save()
|
|
self.assert_served_by_mirror(self.client.get(url))
|
|
|
|
# But we can't match platform=3.
|
|
url = reverse('downloads.latest',
|
|
kwargs={'addon_id': self.addon.slug, 'platform': 3})
|
|
eq_(self.client.get(url).status_code, 404)
|
|
|
|
def test_type(self):
|
|
url = reverse('downloads.latest', kwargs={'addon_id': self.addon.slug,
|
|
'type': 'attachment'})
|
|
self.assert_served_locally(self.client.get(url), attachment=True)
|
|
|
|
def test_platform_and_type(self):
|
|
url = reverse('downloads.latest',
|
|
kwargs={'addon_id': self.addon.slug, 'platform': 5,
|
|
'type': 'attachment'})
|
|
self.assert_served_locally(self.client.get(url), attachment=True)
|
|
|
|
def test_trailing_filename(self):
|
|
url = reverse('downloads.latest',
|
|
kwargs={'addon_id': self.addon.slug, 'platform': 5,
|
|
'type': 'attachment'})
|
|
url += self.file.filename
|
|
self.assert_served_locally(self.client.get(url), attachment=True)
|
|
|
|
def test_platform_multiple_objects(self):
|
|
p = Platform.objects.create(id=3)
|
|
f = File.objects.create(platform=p, version=self.file.version,
|
|
filename='unst.xpi')
|
|
url = reverse('downloads.latest',
|
|
kwargs={'addon_id': self.addon.slug, 'platform': 3})
|
|
self.assert_served_locally(self.client.get(url), file_=f)
|
|
|
|
def test_query_params(self):
|
|
url = self.latest_url + '?src=xxx'
|
|
r = self.client.get(url)
|
|
eq_(r.status_code, 302)
|
|
assert r['Location'].endswith('?src=xxx'), r['Location']
|
|
|
|
def test_premium_redirects(self):
|
|
self.addon.update(premium_type=amo.ADDON_PREMIUM)
|
|
eq_(self.client.get(self.latest_url).status_code, 302)
|
|
|
|
|
|
class TestVersionFromUpload(UploadTest, amo.tests.TestCase):
|
|
fixtures = ['base/apps', 'base/addon_3615', 'base/users',
|
|
'base/platforms']
|
|
|
|
def setUp(self):
|
|
super(TestVersionFromUpload, self).setUp()
|
|
self.upload = self.get_upload(self.filename)
|
|
self.addon = Addon.objects.get(id=3615)
|
|
self.addon.update(guid='guid@xpi')
|
|
self.platform = Platform.objects.get(id=amo.PLATFORM_MAC.id)
|
|
for version in ('3.0', '3.6.*'):
|
|
AppVersion.objects.create(application_id=1, version=version)
|
|
|
|
|
|
class TestExtensionVersionFromUpload(TestVersionFromUpload):
|
|
filename = 'extension.xpi'
|
|
|
|
def test_carry_over_old_license(self):
|
|
version = Version.from_upload(self.upload, self.addon,
|
|
[self.platform])
|
|
eq_(version.license_id, self.addon.current_version.license_id)
|
|
|
|
def test_carry_over_license_no_version(self):
|
|
self.addon.versions.all().delete()
|
|
version = Version.from_upload(self.upload, self.addon,
|
|
[self.platform])
|
|
eq_(version.license_id, None)
|
|
|
|
def test_app_versions(self):
|
|
version = Version.from_upload(self.upload, self.addon,
|
|
[self.platform])
|
|
assert amo.FIREFOX in version.compatible_apps
|
|
app = version.compatible_apps[amo.FIREFOX]
|
|
eq_(app.min.version, '3.0')
|
|
eq_(app.max.version, '3.6.*')
|
|
|
|
def test_version_number(self):
|
|
version = Version.from_upload(self.upload, self.addon,
|
|
[self.platform])
|
|
eq_(version.version, '0.1')
|
|
|
|
def test_file_platform(self):
|
|
version = Version.from_upload(self.upload, self.addon,
|
|
[self.platform])
|
|
files = version.all_files
|
|
eq_(len(files), 1)
|
|
eq_(files[0].platform, self.platform)
|
|
|
|
def test_file_name(self):
|
|
version = Version.from_upload(self.upload, self.addon,
|
|
[self.platform])
|
|
files = version.all_files
|
|
eq_(files[0].filename, u'delicious_bookmarks-0.1-fx-mac.xpi')
|
|
|
|
def test_file_name_platform_all(self):
|
|
version = Version.from_upload(self.upload, self.addon,
|
|
[Platform.objects.get(pk=amo.PLATFORM_ALL.id)])
|
|
files = version.all_files
|
|
eq_(files[0].filename, u'delicious_bookmarks-0.1-fx.xpi')
|
|
|
|
def test_mobile_all_creates_platform_files(self):
|
|
all_mobile = Platform.objects.get(id=amo.PLATFORM_ALL_MOBILE.id)
|
|
version = Version.from_upload(self.upload, self.addon, [all_mobile])
|
|
files = version.all_files
|
|
eq_(sorted(amo.PLATFORMS[f.platform.id].shortname for f in files),
|
|
['android', 'maemo'])
|
|
|
|
def test_mobile_all_desktop_all_creates_all(self):
|
|
all_desktop = Platform.objects.get(id=amo.PLATFORM_ALL.id)
|
|
all_mobile = Platform.objects.get(id=amo.PLATFORM_ALL_MOBILE.id)
|
|
version = Version.from_upload(self.upload, self.addon, [all_desktop,
|
|
all_mobile])
|
|
files = version.all_files
|
|
eq_(sorted(amo.PLATFORMS[f.platform.id].shortname for f in files),
|
|
['all'])
|
|
|
|
def test_desktop_all_with_mixed_mobile_creates_platform_files(self):
|
|
all_desktop = Platform.objects.get(id=amo.PLATFORM_ALL.id)
|
|
android = Platform.objects.get(id=amo.PLATFORM_ANDROID.id)
|
|
version = Version.from_upload(self.upload, self.addon, [all_desktop,
|
|
android])
|
|
files = version.all_files
|
|
eq_(sorted(amo.PLATFORMS[f.platform.id].shortname for f in files),
|
|
['android', 'linux', 'mac', 'windows'])
|
|
|
|
def test_mobile_all_with_mixed_desktop_creates_platform_files(self):
|
|
all_mobile = Platform.objects.get(id=amo.PLATFORM_ALL_MOBILE.id)
|
|
linux = Platform.objects.get(id=amo.PLATFORM_LINUX.id)
|
|
version = Version.from_upload(self.upload, self.addon, [linux,
|
|
all_mobile])
|
|
files = version.all_files
|
|
eq_(sorted(amo.PLATFORMS[f.platform.id].shortname for f in files),
|
|
['android', 'linux', 'maemo'])
|
|
|
|
def test_multiple_platforms(self):
|
|
platforms = [Platform.objects.get(pk=amo.PLATFORM_LINUX.id),
|
|
Platform.objects.get(pk=amo.PLATFORM_MAC.id)]
|
|
assert storage.exists(self.upload.path)
|
|
with storage.open(self.upload.path) as f:
|
|
uploaded_hash = hashlib.md5(f.read()).hexdigest()
|
|
version = Version.from_upload(self.upload, self.addon, platforms)
|
|
assert not storage.exists(self.upload.path), (
|
|
"Expected original upload to move but it still exists.")
|
|
files = version.all_files
|
|
eq_(len(files), 2)
|
|
eq_(sorted([f.platform.id for f in files]),
|
|
sorted([p.id for p in platforms]))
|
|
eq_(sorted([f.filename for f in files]),
|
|
[u'delicious_bookmarks-0.1-fx-%s.xpi' % (
|
|
amo.PLATFORM_LINUX.shortname),
|
|
u'delicious_bookmarks-0.1-fx-%s.xpi' % (
|
|
amo.PLATFORM_MAC.shortname)])
|
|
for file in files:
|
|
with storage.open(file.file_path) as f:
|
|
eq_(uploaded_hash,
|
|
hashlib.md5(f.read()).hexdigest(),
|
|
"md5 hash of %r does not match uploaded file" %
|
|
file.file_path)
|
|
|
|
|
|
class TestSearchVersionFromUpload(TestVersionFromUpload):
|
|
filename = 'search.xml'
|
|
|
|
def setUp(self):
|
|
super(TestSearchVersionFromUpload, self).setUp()
|
|
self.addon.versions.all().delete()
|
|
self.addon.update(type=amo.ADDON_SEARCH)
|
|
self.now = datetime.now().strftime('%Y%m%d')
|
|
|
|
def test_version_number(self):
|
|
version = Version.from_upload(self.upload, self.addon,
|
|
[self.platform])
|
|
eq_(version.version, self.now)
|
|
|
|
def test_file_name(self):
|
|
version = Version.from_upload(self.upload, self.addon,
|
|
[self.platform])
|
|
files = version.all_files
|
|
eq_(files[0].filename,
|
|
u'delicious_bookmarks-%s.xml' % self.now)
|
|
|
|
def test_file_platform_is_always_all(self):
|
|
version = Version.from_upload(self.upload, self.addon,
|
|
[self.platform])
|
|
files = version.all_files
|
|
eq_(len(files), 1)
|
|
eq_(files[0].platform.id, amo.PLATFORM_ALL.id)
|
|
|
|
|
|
class TestStatusFromUpload(TestVersionFromUpload):
|
|
filename = 'extension.xpi'
|
|
|
|
def setUp(self):
|
|
super(TestStatusFromUpload, self).setUp()
|
|
self.current = self.addon.current_version
|
|
# We need one public file to stop the addon update signal
|
|
# moving the addon away from public. Only public addons check
|
|
# for beta status on from_upload.
|
|
self.current.files.all().update(status=amo.STATUS_UNREVIEWED)
|
|
File.objects.create(version=self.current, status=amo.STATUS_PUBLIC)
|
|
self.addon.update(status=amo.STATUS_PUBLIC)
|
|
|
|
def test_status(self):
|
|
qs = File.objects.filter(version=self.current)
|
|
Version.from_upload(self.upload, self.addon, [self.platform])
|
|
eq_(sorted([q.status for q in qs.all()]),
|
|
[amo.STATUS_PUBLIC, amo.STATUS_DISABLED])
|
|
|
|
@mock.patch('files.utils.parse_addon')
|
|
def test_status_beta(self, parse_addon):
|
|
parse_addon.return_value = {'version': u'0.1beta'}
|
|
|
|
qs = File.objects.filter(version=self.current)
|
|
Version.from_upload(self.upload, self.addon, [self.platform])
|
|
eq_(sorted([q.status for q in qs.all()]),
|
|
[amo.STATUS_UNREVIEWED, amo.STATUS_PUBLIC])
|
|
|
|
|
|
class TestMobileVersions(TestMobile):
|
|
|
|
def test_versions(self):
|
|
r = self.client.get(reverse('addons.versions', args=['a3615']))
|
|
eq_(r.status_code, 200)
|
|
self.assertTemplateUsed(r, 'versions/mobile/version_list.html')
|
|
|
|
|
|
class TestApplicationsVersions(amo.tests.TestCase):
|
|
|
|
def setUp(self):
|
|
waffle.models.Switch.objects.create(name='d2c-buttons', active=True)
|
|
self.version_kw = dict(min_app_version='5.0', max_app_version='6.*')
|
|
|
|
def test_repr_when_compatible(self):
|
|
addon = addon_factory(version_kw=self.version_kw)
|
|
version = addon.current_version
|
|
eq_(version.apps.all()[0].__unicode__(), 'Firefox 5.0 and later')
|
|
|
|
def test_repr_when_strict(self):
|
|
addon = addon_factory(version_kw=self.version_kw,
|
|
file_kw=dict(strict_compatibility=True))
|
|
version = addon.current_version
|
|
eq_(version.apps.all()[0].__unicode__(), 'Firefox 5.0 - 6.*')
|
|
|
|
def test_repr_when_binary(self):
|
|
addon = addon_factory(version_kw=self.version_kw,
|
|
file_kw=dict(binary_components=True))
|
|
version = addon.current_version
|
|
eq_(version.apps.all()[0].__unicode__(), 'Firefox 5.0 - 6.*')
|
|
|
|
def test_repr_when_not_extension(self):
|
|
addon = addon_factory(type=amo.ADDON_THEME,
|
|
version_kw=self.version_kw)
|
|
version = addon.current_version
|
|
eq_(version.apps.all()[0].__unicode__(), 'Firefox 5.0 - 6.*')
|
|
|
|
def test_repr_when_low_app_support(self):
|
|
addon = addon_factory(version_kw=dict(min_app_version='3.0',
|
|
max_app_version='3.5'))
|
|
version = addon.current_version
|
|
eq_(version.apps.all()[0].__unicode__(), 'Firefox 3.0 - 3.5')
|
|
|
|
def test_repr_when_unicode(self):
|
|
addon = addon_factory(version_kw=dict(min_app_version=u'ك',
|
|
max_app_version=u'ك'))
|
|
version = addon.current_version
|
|
eq_(unicode(version.apps.all()[0]), u'Firefox ك - ك')
|