Serve JSON update manifests instead of RDF, but to Firefox only (#8271)
Serve JSON update manifests instead of RDF, but to Firefox only
This commit is contained in:
Родитель
968c7636bf
Коммит
131c8f22db
|
@ -1,3 +1,4 @@
|
||||||
|
import json
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
|
@ -18,8 +19,7 @@ except ImportError:
|
||||||
from olympia.constants import applications, base
|
from olympia.constants import applications, base
|
||||||
import olympia.core.logger
|
import olympia.core.logger
|
||||||
|
|
||||||
from utils import (
|
from utils import get_cdn_url, log_configure, PLATFORM_NAMES_TO_CONSTANTS
|
||||||
APP_GUIDS, get_cdn_url, log_configure, PLATFORMS)
|
|
||||||
|
|
||||||
|
|
||||||
# Go configure the log.
|
# Go configure the log.
|
||||||
|
@ -81,6 +81,15 @@ class Update(object):
|
||||||
self.data['row'] = {}
|
self.data['row'] = {}
|
||||||
self.version_int = 0
|
self.version_int = 0
|
||||||
self.compat_mode = compat_mode
|
self.compat_mode = compat_mode
|
||||||
|
self.use_json = self.should_use_json()
|
||||||
|
|
||||||
|
def should_use_json(self):
|
||||||
|
# We serve JSON manifests to Firefox and Firefox for Android only,
|
||||||
|
# because we've seen issues with Seamonkey and Thunderbird.
|
||||||
|
# https://github.com/mozilla/addons-server/issues/7223
|
||||||
|
app = applications.APP_GUIDS.get(self.data.get('appID'))
|
||||||
|
return app and app.id in (applications.FIREFOX.id,
|
||||||
|
applications.ANDROID.id)
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
# If you accessing this from unit tests, then before calling
|
# If you accessing this from unit tests, then before calling
|
||||||
|
@ -96,10 +105,12 @@ class Update(object):
|
||||||
if field not in data:
|
if field not in data:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
data['app_id'] = APP_GUIDS.get(data['appID'])
|
app = applications.APP_GUIDS.get(data['appID'])
|
||||||
if not data['app_id']:
|
if not app:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
data['app_id'] = app.id
|
||||||
|
|
||||||
sql = """SELECT id, status, addontype_id, guid FROM addons
|
sql = """SELECT id, status, addontype_id, guid FROM addons
|
||||||
WHERE guid = %(guid)s AND
|
WHERE guid = %(guid)s AND
|
||||||
inactive = 0 AND
|
inactive = 0 AND
|
||||||
|
@ -118,7 +129,7 @@ class Update(object):
|
||||||
data['version_int'] = version_int(data['appVersion'])
|
data['version_int'] = version_int(data['appVersion'])
|
||||||
|
|
||||||
if 'appOS' in data:
|
if 'appOS' in data:
|
||||||
for k, v in PLATFORMS.items():
|
for k, v in PLATFORM_NAMES_TO_CONSTANTS.items():
|
||||||
if k in data['appOS']:
|
if k in data['appOS']:
|
||||||
data['appOS'] = v
|
data['appOS'] = v
|
||||||
break
|
break
|
||||||
|
@ -243,27 +254,68 @@ class Update(object):
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_bad_rdf(self):
|
def get_output(self):
|
||||||
return bad_rdf
|
|
||||||
|
|
||||||
def get_rdf(self):
|
|
||||||
if self.is_valid():
|
if self.is_valid():
|
||||||
if self.get_update():
|
if self.get_update():
|
||||||
rdf = self.get_good_rdf()
|
contents = self.get_success_output()
|
||||||
else:
|
else:
|
||||||
rdf = self.get_no_updates_rdf()
|
contents = self.get_no_updates_output()
|
||||||
else:
|
else:
|
||||||
rdf = self.get_bad_rdf()
|
contents = self.get_error_output()
|
||||||
self.cursor.close()
|
self.cursor.close()
|
||||||
if self.conn:
|
if self.conn:
|
||||||
self.conn.close()
|
self.conn.close()
|
||||||
return rdf
|
return json.dumps(contents) if self.use_json else contents
|
||||||
|
|
||||||
def get_no_updates_rdf(self):
|
def get_error_output(self):
|
||||||
name = base.ADDON_SLUGS_UPDATE[self.data['type']]
|
return {} if self.use_json else bad_rdf
|
||||||
return no_updates_rdf % ({'guid': self.data['guid'], 'type': name})
|
|
||||||
|
|
||||||
def get_good_rdf(self):
|
def get_no_updates_output(self):
|
||||||
|
if self.use_json:
|
||||||
|
return {
|
||||||
|
'addons': {
|
||||||
|
self.data['guid']: {
|
||||||
|
'updates': []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
name = base.ADDON_SLUGS_UPDATE[self.data['type']]
|
||||||
|
return no_updates_rdf % ({'guid': self.data['guid'], 'type': name})
|
||||||
|
|
||||||
|
def get_success_output(self):
|
||||||
|
if self.use_json:
|
||||||
|
return self.get_success_output_json()
|
||||||
|
else:
|
||||||
|
return self.get_success_output_rdf()
|
||||||
|
|
||||||
|
def get_success_output_json(self):
|
||||||
|
data = self.data['row']
|
||||||
|
update = {
|
||||||
|
'version': data['version'],
|
||||||
|
'update_link': data['url'],
|
||||||
|
'applications': {
|
||||||
|
'gecko': {
|
||||||
|
'strict_min_version': data['min']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if data['strict_compat']:
|
||||||
|
update['applications']['gecko']['strict_max_version'] = data['max']
|
||||||
|
if data['hash']:
|
||||||
|
update['update_hash'] = data['hash']
|
||||||
|
if data['releasenotes']:
|
||||||
|
update['update_info_url'] = '%s%s%s/%%APP_LOCALE%%/' % (
|
||||||
|
settings.SITE_URL, '/versions/updateInfo/', data['version_id'])
|
||||||
|
return {
|
||||||
|
'addons': {
|
||||||
|
self.data['guid']: {
|
||||||
|
'updates': [update]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_success_output_rdf(self):
|
||||||
data = self.data['row']
|
data = self.data['row']
|
||||||
data['if_hash'] = ''
|
data['if_hash'] = ''
|
||||||
if data['hash']:
|
if data['hash']:
|
||||||
|
@ -283,7 +335,8 @@ class Update(object):
|
||||||
return '%s GMT' % formatdate(time() + secs)[:25]
|
return '%s GMT' % formatdate(time() + secs)[:25]
|
||||||
|
|
||||||
def get_headers(self, length):
|
def get_headers(self, length):
|
||||||
return [('Content-Type', 'text/xml'),
|
content_type = 'application/json' if self.use_json else 'text/xml'
|
||||||
|
return [('Content-Type', content_type),
|
||||||
('Cache-Control', 'public, max-age=3600'),
|
('Cache-Control', 'public, max-age=3600'),
|
||||||
('Last-Modified', self.format_date(0)),
|
('Last-Modified', self.format_date(0)),
|
||||||
('Expires', self.format_date(3600)),
|
('Expires', self.format_date(3600)),
|
||||||
|
@ -302,7 +355,7 @@ def application(environ, start_response):
|
||||||
compat_mode = data.pop('compatMode', 'strict')
|
compat_mode = data.pop('compatMode', 'strict')
|
||||||
try:
|
try:
|
||||||
update = Update(data, compat_mode)
|
update = Update(data, compat_mode)
|
||||||
output = force_bytes(update.get_rdf())
|
output = force_bytes(update.get_output())
|
||||||
start_response(status, update.get_headers(len(output)))
|
start_response(status, update.get_headers(len(output)))
|
||||||
except Exception:
|
except Exception:
|
||||||
log_exception(data)
|
log_exception(data)
|
||||||
|
|
|
@ -17,7 +17,6 @@ from olympia.lib.log_settings_base import formatters, handlers
|
||||||
# Ugh. But this avoids any olympia models or django imports at all.
|
# Ugh. But this avoids any olympia models or django imports at all.
|
||||||
# Perhaps we can import these without any problems and we can
|
# Perhaps we can import these without any problems and we can
|
||||||
# remove all this.
|
# remove all this.
|
||||||
from olympia.constants.applications import APPS_ALL
|
|
||||||
from olympia.constants.platforms import PLATFORMS
|
from olympia.constants.platforms import PLATFORMS
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,8 +50,9 @@ def user_media_url(what):
|
||||||
return getattr(settings, key, default)
|
return getattr(settings, key, default)
|
||||||
|
|
||||||
|
|
||||||
APP_GUIDS = dict([(app.guid, app.id) for app in APPS_ALL.values()])
|
PLATFORM_NAMES_TO_CONSTANTS = {
|
||||||
PLATFORMS = dict([(plat.api_name, plat.id) for plat in PLATFORMS.values()])
|
platform.api_name: platform.id for platform in PLATFORMS.values()
|
||||||
|
}
|
||||||
|
|
||||||
ADDON_SLUGS_UPDATE = {
|
ADDON_SLUGS_UPDATE = {
|
||||||
1: 'extension',
|
1: 'extension',
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import json
|
||||||
|
import mock
|
||||||
|
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from email import utils
|
from email import utils
|
||||||
|
|
||||||
|
import rdflib
|
||||||
|
|
||||||
from django.db import connection
|
from django.db import connection
|
||||||
|
|
||||||
from services import update
|
from services import update
|
||||||
|
@ -17,10 +22,10 @@ from olympia.versions.models import ApplicationsVersions, Version
|
||||||
|
|
||||||
class VersionCheckMixin(object):
|
class VersionCheckMixin(object):
|
||||||
|
|
||||||
def get(self, data):
|
def get_update_instance(self, data):
|
||||||
up = update.Update(data)
|
instance = update.Update(data)
|
||||||
up.cursor = connection.cursor()
|
instance.cursor = connection.cursor()
|
||||||
return up
|
return instance
|
||||||
|
|
||||||
|
|
||||||
class TestDataValidate(VersionCheckMixin, TestCase):
|
class TestDataValidate(VersionCheckMixin, TestCase):
|
||||||
|
@ -28,7 +33,7 @@ class TestDataValidate(VersionCheckMixin, TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestDataValidate, self).setUp()
|
super(TestDataValidate, self).setUp()
|
||||||
self.good_data = {
|
self.data = {
|
||||||
'id': '{2fa4ed95-0317-4c6a-a74c-5f3e3912c1f9}',
|
'id': '{2fa4ed95-0317-4c6a-a74c-5f3e3912c1f9}',
|
||||||
'version': '2.0.58',
|
'version': '2.0.58',
|
||||||
'reqVersion': 1,
|
'reqVersion': 1,
|
||||||
|
@ -37,74 +42,74 @@ class TestDataValidate(VersionCheckMixin, TestCase):
|
||||||
}
|
}
|
||||||
|
|
||||||
def test_app_os(self):
|
def test_app_os(self):
|
||||||
data = self.good_data.copy()
|
data = self.data.copy()
|
||||||
data['appOS'] = 'something %s penguin' % amo.PLATFORM_LINUX.api_name
|
data['appOS'] = 'something %s penguin' % amo.PLATFORM_LINUX.api_name
|
||||||
form = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
assert form.is_valid()
|
assert instance.is_valid()
|
||||||
assert form.data['appOS'] == amo.PLATFORM_LINUX.id
|
assert instance.data['appOS'] == amo.PLATFORM_LINUX.id
|
||||||
|
|
||||||
def test_app_version_fails(self):
|
def test_app_version_fails(self):
|
||||||
data = self.good_data.copy()
|
data = self.data.copy()
|
||||||
del data['appID']
|
del data['appID']
|
||||||
form = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
assert not form.is_valid()
|
assert not instance.is_valid()
|
||||||
|
|
||||||
def test_app_version_wrong(self):
|
def test_app_version_wrong(self):
|
||||||
data = self.good_data.copy()
|
data = self.data.copy()
|
||||||
data['appVersion'] = '67.7'
|
data['appVersion'] = '67.7'
|
||||||
form = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
# If you pass through the wrong version that's fine
|
# If you pass through the wrong version that's fine
|
||||||
# you will just end up with no updates because your
|
# you will just end up with no updates because your
|
||||||
# version_int will be out.
|
# version_int will be out.
|
||||||
assert form.is_valid()
|
assert instance.is_valid()
|
||||||
|
|
||||||
def test_app_version(self):
|
def test_app_version(self):
|
||||||
data = self.good_data.copy()
|
data = self.data.copy()
|
||||||
form = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
assert form.is_valid()
|
assert instance.is_valid()
|
||||||
assert form.data['version_int'] == 3070000001000
|
assert instance.data['version_int'] == 3070000001000
|
||||||
|
|
||||||
def test_sql_injection(self):
|
def test_sql_injection(self):
|
||||||
data = self.good_data.copy()
|
data = self.data.copy()
|
||||||
data['id'] = "'"
|
data['id'] = "'"
|
||||||
up = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
assert not up.is_valid()
|
assert not instance.is_valid()
|
||||||
|
|
||||||
def test_inactive(self):
|
def test_inactive(self):
|
||||||
addon = Addon.objects.get(pk=3615)
|
addon = Addon.objects.get(pk=3615)
|
||||||
addon.update(disabled_by_user=True)
|
addon.update(disabled_by_user=True)
|
||||||
|
|
||||||
up = self.get(self.good_data)
|
instance = self.get_update_instance(self.data)
|
||||||
assert not up.is_valid()
|
assert not instance.is_valid()
|
||||||
|
|
||||||
def test_soft_deleted(self):
|
def test_soft_deleted(self):
|
||||||
addon = Addon.objects.get(pk=3615)
|
addon = Addon.objects.get(pk=3615)
|
||||||
addon.update(status=amo.STATUS_DELETED)
|
addon.update(status=amo.STATUS_DELETED)
|
||||||
|
|
||||||
up = self.get(self.good_data)
|
instance = self.get_update_instance(self.data)
|
||||||
assert not up.is_valid()
|
assert not instance.is_valid()
|
||||||
|
|
||||||
def test_disabled(self):
|
def test_disabled(self):
|
||||||
addon = Addon.objects.get(pk=3615)
|
addon = Addon.objects.get(pk=3615)
|
||||||
addon.update(status=amo.STATUS_DISABLED)
|
addon.update(status=amo.STATUS_DISABLED)
|
||||||
|
|
||||||
up = self.get(self.good_data)
|
instance = self.get_update_instance(self.data)
|
||||||
assert not up.is_valid()
|
assert not instance.is_valid()
|
||||||
|
|
||||||
def test_no_version(self):
|
def test_no_version(self):
|
||||||
data = self.good_data.copy()
|
data = self.data.copy()
|
||||||
del data['version']
|
del data['version']
|
||||||
up = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
assert up.is_valid()
|
assert instance.is_valid()
|
||||||
|
|
||||||
def test_unlisted_addon(self):
|
def test_unlisted_addon(self):
|
||||||
"""Add-ons with only unlisted versions are valid, they just don't
|
"""Add-ons with only unlisted versions are valid, they just don't
|
||||||
receive any updates (See TestLookup.test_no_unlisted below)."""
|
receive any updates (See TestLookinstance.test_no_unlisted below)."""
|
||||||
addon = Addon.objects.get(pk=3615)
|
addon = Addon.objects.get(pk=3615)
|
||||||
self.make_addon_unlisted(addon)
|
self.make_addon_unlisted(addon)
|
||||||
|
|
||||||
up = self.get(self.good_data)
|
instance = self.get_update_instance(self.data)
|
||||||
assert up.is_valid()
|
assert instance.is_valid()
|
||||||
|
|
||||||
|
|
||||||
class TestLookup(VersionCheckMixin, TestCase):
|
class TestLookup(VersionCheckMixin, TestCase):
|
||||||
|
@ -123,7 +128,7 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
self.version_1_2_1 = 112396
|
self.version_1_2_1 = 112396
|
||||||
self.version_1_2_2 = 115509
|
self.version_1_2_2 = 115509
|
||||||
|
|
||||||
def get(self, *args):
|
def get_update_instance(self, *args):
|
||||||
data = {
|
data = {
|
||||||
'id': self.addon.guid,
|
'id': self.addon.guid,
|
||||||
'appID': args[2].guid,
|
'appID': args[2].guid,
|
||||||
|
@ -134,12 +139,12 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
# Allow version to be optional.
|
# Allow version to be optional.
|
||||||
if args[0]:
|
if args[0]:
|
||||||
data['version'] = args[0]
|
data['version'] = args[0]
|
||||||
up = super(TestLookup, self).get(data)
|
instance = super(TestLookup, self).get_update_instance(data)
|
||||||
assert up.is_valid()
|
assert instance.is_valid()
|
||||||
up.data['version_int'] = args[1]
|
instance.data['version_int'] = args[1]
|
||||||
up.get_update()
|
instance.get_update()
|
||||||
return (up.data['row'].get('version_id'),
|
return (instance.data['row'].get('version_id'),
|
||||||
up.data['row'].get('file_id'))
|
instance.data['row'].get('file_id'))
|
||||||
|
|
||||||
def change_status(self, version, status):
|
def change_status(self, version, status):
|
||||||
version = Version.objects.get(pk=version)
|
version = Version.objects.get(pk=version)
|
||||||
|
@ -156,8 +161,8 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
Version 3.0a1 of Firefox is 3000000001100 and version 1.0.2 of the
|
Version 3.0a1 of Firefox is 3000000001100 and version 1.0.2 of the
|
||||||
add-on is returned.
|
add-on is returned.
|
||||||
"""
|
"""
|
||||||
version, file = self.get('', '3000000001100',
|
version, file = self.get_update_instance(
|
||||||
self.app, self.platform)
|
'', '3000000001100', self.app, self.platform)
|
||||||
assert version == self.version_1_0_2
|
assert version == self.version_1_0_2
|
||||||
|
|
||||||
def test_new_client(self):
|
def test_new_client(self):
|
||||||
|
@ -165,8 +170,8 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
Version 3.0.12 of Firefox is 3069900200100 and version 1.2.2 of the
|
Version 3.0.12 of Firefox is 3069900200100 and version 1.2.2 of the
|
||||||
add-on is returned.
|
add-on is returned.
|
||||||
"""
|
"""
|
||||||
version, file = self.get('', self.version_int,
|
version, file = self.get_update_instance(
|
||||||
self.app, self.platform)
|
'', self.version_int, self.app, self.platform)
|
||||||
assert version == self.version_1_2_2
|
assert version == self.version_1_2_2
|
||||||
|
|
||||||
def test_min_client(self):
|
def test_min_client(self):
|
||||||
|
@ -180,8 +185,8 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
appversion.min = AppVersion.objects.get(pk=325) # 3.7a5
|
appversion.min = AppVersion.objects.get(pk=325) # 3.7a5
|
||||||
appversion.save()
|
appversion.save()
|
||||||
|
|
||||||
version, file = self.get('', '3070000005000', # 3.7a5pre
|
version, file = self.get_update_instance(
|
||||||
self.app, self.platform)
|
'', '3070000005000', self.app, self.platform) # 3.7a5pre
|
||||||
assert version == self.version_1_1_3
|
assert version == self.version_1_1_3
|
||||||
|
|
||||||
def test_new_client_ordering(self):
|
def test_new_client_ordering(self):
|
||||||
|
@ -201,8 +206,8 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
application_version.max_id = 329
|
application_version.max_id = 329
|
||||||
application_version.save()
|
application_version.save()
|
||||||
|
|
||||||
version, file = self.get('', self.version_int,
|
version, file = self.get_update_instance(
|
||||||
self.app, self.platform)
|
'', self.version_int, self.app, self.platform)
|
||||||
assert version == self.version_1_2_2
|
assert version == self.version_1_2_2
|
||||||
|
|
||||||
def test_public(self):
|
def test_public(self):
|
||||||
|
@ -212,8 +217,8 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
self.change_status(self.version_1_2_2, amo.STATUS_PENDING)
|
self.change_status(self.version_1_2_2, amo.STATUS_PENDING)
|
||||||
self.addon.reload()
|
self.addon.reload()
|
||||||
assert self.addon.status == amo.STATUS_PUBLIC
|
assert self.addon.status == amo.STATUS_PUBLIC
|
||||||
version, file = self.get('1.2', self.version_int,
|
version, file = self.get_update_instance(
|
||||||
self.app, self.platform)
|
'1.2', self.version_int, self.app, self.platform)
|
||||||
assert version == self.version_1_2_1
|
assert version == self.version_1_2_1
|
||||||
|
|
||||||
def test_no_unlisted(self):
|
def test_no_unlisted(self):
|
||||||
|
@ -224,8 +229,8 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
channel=amo.RELEASE_CHANNEL_UNLISTED)
|
channel=amo.RELEASE_CHANNEL_UNLISTED)
|
||||||
self.addon.reload()
|
self.addon.reload()
|
||||||
assert self.addon.status == amo.STATUS_PUBLIC
|
assert self.addon.status == amo.STATUS_PUBLIC
|
||||||
version, file = self.get('1.2', self.version_int,
|
version, file = self.get_update_instance(
|
||||||
self.app, self.platform)
|
'1.2', self.version_int, self.app, self.platform)
|
||||||
assert version == self.version_1_2_1
|
assert version == self.version_1_2_1
|
||||||
|
|
||||||
def test_can_downgrade(self):
|
def test_can_downgrade(self):
|
||||||
|
@ -236,8 +241,8 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
self.change_status(self.version_1_2_0, amo.STATUS_PENDING)
|
self.change_status(self.version_1_2_0, amo.STATUS_PENDING)
|
||||||
for v in Version.objects.filter(pk__gte=self.version_1_2_1):
|
for v in Version.objects.filter(pk__gte=self.version_1_2_1):
|
||||||
v.delete()
|
v.delete()
|
||||||
version, file = self.get('1.2', self.version_int,
|
version, file = self.get_update_instance(
|
||||||
self.app, self.platform)
|
'1.2', self.version_int, self.app, self.platform)
|
||||||
|
|
||||||
assert version == self.version_1_1_3
|
assert version == self.version_1_1_3
|
||||||
|
|
||||||
|
@ -252,8 +257,8 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
self.change_status(self.version_1_2_0, amo.STATUS_PENDING)
|
self.change_status(self.version_1_2_0, amo.STATUS_PENDING)
|
||||||
self.change_version(self.version_1_2_0, '1.2beta')
|
self.change_version(self.version_1_2_0, '1.2beta')
|
||||||
|
|
||||||
version, file = self.get('1.2', self.version_int,
|
version, file = self.get_update_instance(
|
||||||
self.app, self.platform)
|
'1.2', self.version_int, self.app, self.platform)
|
||||||
|
|
||||||
assert version == self.version_1_2_1
|
assert version == self.version_1_2_1
|
||||||
|
|
||||||
|
@ -267,8 +272,8 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
self.change_version(self.version_1_2_0, '1.2beta')
|
self.change_version(self.version_1_2_0, '1.2beta')
|
||||||
Version.objects.get(pk=self.version_1_2_0).files.all().delete()
|
Version.objects.get(pk=self.version_1_2_0).files.all().delete()
|
||||||
|
|
||||||
version, file = self.get('1.2beta', self.version_int,
|
version, file = self.get_update_instance(
|
||||||
self.app, self.platform)
|
'1.2beta', self.version_int, self.app, self.platform)
|
||||||
dest = Version.objects.get(pk=self.version_1_2_2)
|
dest = Version.objects.get(pk=self.version_1_2_2)
|
||||||
assert dest.addon.status == amo.STATUS_PUBLIC
|
assert dest.addon.status == amo.STATUS_PUBLIC
|
||||||
assert dest.files.all()[0].status == amo.STATUS_PUBLIC
|
assert dest.files.all()[0].status == amo.STATUS_PUBLIC
|
||||||
|
@ -281,8 +286,8 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
"""
|
"""
|
||||||
self.change_status(self.version_1_2_2, amo.STATUS_NULL)
|
self.change_status(self.version_1_2_2, amo.STATUS_NULL)
|
||||||
self.addon.update(status=amo.STATUS_NULL)
|
self.addon.update(status=amo.STATUS_NULL)
|
||||||
version, file = self.get('1.2.1', self.version_int,
|
version, file = self.get_update_instance(
|
||||||
self.app, self.platform)
|
'1.2.1', self.version_int, self.app, self.platform)
|
||||||
assert version == self.version_1_2_1
|
assert version == self.version_1_2_1
|
||||||
|
|
||||||
def test_platform_does_not_exist(self):
|
def test_platform_does_not_exist(self):
|
||||||
|
@ -292,8 +297,8 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
file.platform = amo.PLATFORM_LINUX.id
|
file.platform = amo.PLATFORM_LINUX.id
|
||||||
file.save()
|
file.save()
|
||||||
|
|
||||||
version, file = self.get('1.2', self.version_int,
|
version, file = self.get_update_instance(
|
||||||
self.app, self.platform)
|
'1.2', self.version_int, self.app, self.platform)
|
||||||
assert version == self.version_1_2_1
|
assert version == self.version_1_2_1
|
||||||
|
|
||||||
def test_platform_exists(self):
|
def test_platform_exists(self):
|
||||||
|
@ -303,8 +308,8 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
file.platform = amo.PLATFORM_LINUX.id
|
file.platform = amo.PLATFORM_LINUX.id
|
||||||
file.save()
|
file.save()
|
||||||
|
|
||||||
version, file = self.get('1.2', self.version_int,
|
version, file = self.get_update_instance(
|
||||||
self.app, amo.PLATFORM_LINUX)
|
'1.2', self.version_int, self.app, amo.PLATFORM_LINUX)
|
||||||
assert version == self.version_1_2_2
|
assert version == self.version_1_2_2
|
||||||
|
|
||||||
def test_file_for_platform(self):
|
def test_file_for_platform(self):
|
||||||
|
@ -318,13 +323,13 @@ class TestLookup(VersionCheckMixin, TestCase):
|
||||||
platform=amo.PLATFORM_WIN.id,
|
platform=amo.PLATFORM_WIN.id,
|
||||||
status=amo.STATUS_PUBLIC)
|
status=amo.STATUS_PUBLIC)
|
||||||
file_two.save()
|
file_two.save()
|
||||||
version, file = self.get('1.2', self.version_int,
|
version, file = self.get_update_instance(
|
||||||
self.app, amo.PLATFORM_LINUX)
|
'1.2', self.version_int, self.app, amo.PLATFORM_LINUX)
|
||||||
assert version == self.version_1_2_2
|
assert version == self.version_1_2_2
|
||||||
assert file == file_one.pk
|
assert file == file_one.pk
|
||||||
|
|
||||||
version, file = self.get('1.2', self.version_int,
|
version, file = self.get_update_instance(
|
||||||
self.app, amo.PLATFORM_WIN)
|
'1.2', self.version_int, self.app, amo.PLATFORM_WIN)
|
||||||
assert version == self.version_1_2_2
|
assert version == self.version_1_2_2
|
||||||
assert file == file_two.pk
|
assert file == file_two.pk
|
||||||
|
|
||||||
|
@ -385,18 +390,18 @@ class TestDefaultToCompat(VersionCheckMixin, TestCase):
|
||||||
for file in version.files.all():
|
for file in version.files.all():
|
||||||
file.update(**kw)
|
file.update(**kw)
|
||||||
|
|
||||||
def get(self, **kw):
|
def get_update_instance(self, **kw):
|
||||||
up = super(TestDefaultToCompat, self).get({
|
instance = super(TestDefaultToCompat, self).get_update_instance({
|
||||||
'reqVersion': 1,
|
'reqVersion': 1,
|
||||||
'id': self.addon.guid,
|
'id': self.addon.guid,
|
||||||
'version': kw.get('item_version', '1.0'),
|
'version': kw.get('item_version', '1.0'),
|
||||||
'appID': self.app.guid,
|
'appID': self.app.guid,
|
||||||
'appVersion': kw.get('app_version', '3.0'),
|
'appVersion': kw.get('app_version', '3.0'),
|
||||||
})
|
})
|
||||||
assert up.is_valid()
|
assert instance.is_valid()
|
||||||
up.compat_mode = kw.get('compat_mode', 'strict')
|
instance.compat_mode = kw.get('compat_mode', 'strict')
|
||||||
up.get_update()
|
instance.get_update()
|
||||||
return up.data['row'].get('version_id')
|
return instance.data['row'].get('version_id')
|
||||||
|
|
||||||
def check(self, expected):
|
def check(self, expected):
|
||||||
"""
|
"""
|
||||||
|
@ -408,8 +413,42 @@ class TestDefaultToCompat(VersionCheckMixin, TestCase):
|
||||||
|
|
||||||
for version in versions:
|
for version in versions:
|
||||||
for mode in modes:
|
for mode in modes:
|
||||||
assert self.get(app_version=version, compat_mode=mode) == (
|
assert (
|
||||||
expected['-'.join([version, mode])])
|
self.get_update_instance(
|
||||||
|
app_version=version, compat_mode=mode) ==
|
||||||
|
expected['-'.join([version, mode])]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_application(self):
|
||||||
|
# Basic test making sure application() is returning the output of
|
||||||
|
# Update.get_output(). Have to mock Update(): otherwise, the real
|
||||||
|
# database would be hit, not the test one, because of how services
|
||||||
|
# use a different setting and database connection APIs.
|
||||||
|
environ = {
|
||||||
|
'QUERY_STRING': ''
|
||||||
|
}
|
||||||
|
self.start_response_call_count = 0
|
||||||
|
|
||||||
|
expected_headers = [
|
||||||
|
('FakeHeader', 'FakeHeaderValue')
|
||||||
|
]
|
||||||
|
|
||||||
|
expected_output = '{"fake": "output"}'
|
||||||
|
|
||||||
|
def start_response_inspector(status, headers):
|
||||||
|
self.start_response_call_count += 1
|
||||||
|
assert status == '200 OK'
|
||||||
|
assert headers == expected_headers
|
||||||
|
|
||||||
|
with mock.patch('services.update.Update') as UpdateMock:
|
||||||
|
update_instance = UpdateMock.return_value
|
||||||
|
update_instance.get_headers.return_value = expected_headers
|
||||||
|
update_instance.get_output.return_value = expected_output
|
||||||
|
output = update.application(environ, start_response_inspector)
|
||||||
|
assert self.start_response_call_count == 1
|
||||||
|
# Output is an array with a single string containing the body of the
|
||||||
|
# response.
|
||||||
|
assert output == [expected_output]
|
||||||
|
|
||||||
def test_baseline(self):
|
def test_baseline(self):
|
||||||
# Tests simple add-on (non-binary-components, non-strict).
|
# Tests simple add-on (non-binary-components, non-strict).
|
||||||
|
@ -523,7 +562,7 @@ class TestResponse(VersionCheckMixin, TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestResponse, self).setUp()
|
super(TestResponse, self).setUp()
|
||||||
self.addon_one = Addon.objects.get(pk=3615)
|
self.addon_one = Addon.objects.get(pk=3615)
|
||||||
self.good_data = {
|
self.data = {
|
||||||
'id': '{2fa4ed95-0317-4c6a-a74c-5f3e3912c1f9}',
|
'id': '{2fa4ed95-0317-4c6a-a74c-5f3e3912c1f9}',
|
||||||
'version': '2.0.58',
|
'version': '2.0.58',
|
||||||
'reqVersion': 1,
|
'reqVersion': 1,
|
||||||
|
@ -535,25 +574,35 @@ class TestResponse(VersionCheckMixin, TestCase):
|
||||||
self.win = amo.PLATFORM_WIN
|
self.win = amo.PLATFORM_WIN
|
||||||
|
|
||||||
def test_bad_guid(self):
|
def test_bad_guid(self):
|
||||||
data = self.good_data.copy()
|
self.data['id'] = 'garbage'
|
||||||
data["id"] = "garbage"
|
instance = self.get_update_instance(self.data)
|
||||||
up = self.get(data)
|
assert instance.use_json is True
|
||||||
assert up.get_rdf() == up.get_bad_rdf()
|
assert json.loads(instance.get_output()) == instance.get_error_output()
|
||||||
|
|
||||||
|
# Seamonkey should have a rdf version of 'error ouput'.
|
||||||
|
self.data['appID'] = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}'
|
||||||
|
instance = self.get_update_instance(self.data)
|
||||||
|
assert instance.use_json is False
|
||||||
|
result = instance.get_output()
|
||||||
|
assert result == instance.get_error_output()
|
||||||
|
rdflib.Graph().parse(data=result)
|
||||||
|
|
||||||
def test_no_platform(self):
|
def test_no_platform(self):
|
||||||
file = File.objects.get(pk=67442)
|
file = File.objects.get(pk=67442)
|
||||||
file.platform = self.win.id
|
file.platform = self.win.id
|
||||||
file.save()
|
file.save()
|
||||||
|
|
||||||
data = self.good_data.copy()
|
data = self.data.copy()
|
||||||
data["appOS"] = self.win.api_name
|
data['appOS'] = self.win.api_name
|
||||||
up = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
assert up.get_rdf()
|
assert instance.get_output()
|
||||||
assert up.data['row']['file_id'] == file.pk
|
assert instance.data['row']['file_id'] == file.pk
|
||||||
|
|
||||||
data["appOS"] = self.mac.api_name
|
data['appOS'] = self.mac.api_name
|
||||||
up = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
assert up.get_rdf() == up.get_no_updates_rdf()
|
assert (
|
||||||
|
json.loads(instance.get_output()) ==
|
||||||
|
instance.get_no_updates_output())
|
||||||
|
|
||||||
def test_different_platform(self):
|
def test_different_platform(self):
|
||||||
file = File.objects.get(pk=67442)
|
file = File.objects.get(pk=67442)
|
||||||
|
@ -566,70 +615,67 @@ class TestResponse(VersionCheckMixin, TestCase):
|
||||||
file.save()
|
file.save()
|
||||||
mac_file_pk = file.pk
|
mac_file_pk = file.pk
|
||||||
|
|
||||||
data = self.good_data.copy()
|
data = self.data.copy()
|
||||||
data['appOS'] = self.win.api_name
|
data['appOS'] = self.win.api_name
|
||||||
up = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
up.is_valid()
|
instance.is_valid()
|
||||||
up.get_update()
|
instance.get_update()
|
||||||
assert up.data['row']['file_id'] == file_pk
|
assert instance.data['row']['file_id'] == file_pk
|
||||||
|
|
||||||
data['appOS'] = self.mac.api_name
|
data['appOS'] = self.mac.api_name
|
||||||
up = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
up.is_valid()
|
instance.is_valid()
|
||||||
up.get_update()
|
instance.get_update()
|
||||||
assert up.data['row']['file_id'] == mac_file_pk
|
assert instance.data['row']['file_id'] == mac_file_pk
|
||||||
|
|
||||||
def test_good_version(self):
|
def test_good_version(self):
|
||||||
up = self.get(self.good_data)
|
instance = self.get_update_instance(self.data)
|
||||||
up.is_valid()
|
assert instance.use_json is True
|
||||||
up.get_update()
|
instance.is_valid()
|
||||||
assert up.data['row']['hash'].startswith('sha256:3808b13e')
|
instance.get_update()
|
||||||
assert up.data['row']['min'] == '2.0'
|
assert instance.data['row']['hash'].startswith('sha256:3808b13e')
|
||||||
assert up.data['row']['max'] == '4.0'
|
assert instance.data['row']['min'] == '2.0'
|
||||||
|
assert instance.data['row']['max'] == '4.0'
|
||||||
|
|
||||||
def test_no_app_version(self):
|
def test_no_app_version(self):
|
||||||
data = self.good_data.copy()
|
data = self.data.copy()
|
||||||
data['appVersion'] = '1.4'
|
data['appVersion'] = '1.4'
|
||||||
up = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
up.is_valid()
|
instance.is_valid()
|
||||||
assert not up.get_update()
|
assert not instance.get_update()
|
||||||
|
|
||||||
def test_low_app_version(self):
|
def test_low_app_version(self):
|
||||||
data = self.good_data.copy()
|
data = self.data.copy()
|
||||||
data['appVersion'] = '2.0'
|
data['appVersion'] = '2.0'
|
||||||
up = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
up.is_valid()
|
instance.is_valid()
|
||||||
up.get_update()
|
instance.get_update()
|
||||||
assert up.data['row']['hash'].startswith('sha256:3808b13e')
|
assert instance.data['row']['hash'].startswith('sha256:3808b13e')
|
||||||
assert up.data['row']['min'] == '2.0'
|
assert instance.data['row']['min'] == '2.0'
|
||||||
assert up.data['row']['max'] == '4.0'
|
assert instance.data['row']['max'] == '4.0'
|
||||||
|
|
||||||
def test_content_type(self):
|
def test_content_type(self):
|
||||||
up = self.get(self.good_data)
|
instance = self.get_update_instance(self.data)
|
||||||
('Content-Type', 'text/xml') in up.get_headers(1)
|
('Content-Type', 'text/xml') in instance.get_headers(1)
|
||||||
|
|
||||||
def test_cache_control(self):
|
def test_cache_control(self):
|
||||||
up = self.get(self.good_data)
|
instance = self.get_update_instance(self.data)
|
||||||
('Cache-Control', 'public, max-age=3600') in up.get_headers(1)
|
('Cache-Control', 'public, max-age=3600') in instance.get_headers(1)
|
||||||
|
|
||||||
def test_length(self):
|
def test_length(self):
|
||||||
up = self.get(self.good_data)
|
instance = self.get_update_instance(self.data)
|
||||||
('Cache-Length', '1') in up.get_headers(1)
|
('Cache-Length', '1') in instance.get_headers(1)
|
||||||
|
|
||||||
def test_expires(self):
|
def test_expires(self):
|
||||||
"""Check there are these headers and that expires is 3600 later."""
|
"""Check there are these headers and that expires is 3600 later."""
|
||||||
# We aren't bother going to test the actual time in expires, that
|
# We aren't bother going to test the actual time in expires, that
|
||||||
# way lies pain with broken tests later.
|
# way lies pain with broken tests later.
|
||||||
up = self.get(self.good_data)
|
instance = self.get_update_instance(self.data)
|
||||||
hdrs = dict(up.get_headers(1))
|
headers = dict(instance.get_headers(1))
|
||||||
lm = datetime(*utils.parsedate_tz(hdrs['Last-Modified'])[:7])
|
last_modified = datetime(
|
||||||
exp = datetime(*utils.parsedate_tz(hdrs['Expires'])[:7])
|
*utils.parsedate_tz(headers['Last-Modified'])[:7])
|
||||||
assert (exp - lm).seconds == 3600
|
expires = datetime(*utils.parsedate_tz(headers['Expires'])[:7])
|
||||||
|
assert (expires - last_modified).seconds == 3600
|
||||||
def test_appguid(self):
|
|
||||||
up = self.get(self.good_data)
|
|
||||||
rdf = up.get_rdf()
|
|
||||||
assert rdf.find(self.good_data['appID']) > -1
|
|
||||||
|
|
||||||
def get_file_url(self):
|
def get_file_url(self):
|
||||||
"""Return the file url with the hash as parameter."""
|
"""Return the file url with the hash as parameter."""
|
||||||
|
@ -638,39 +684,47 @@ class TestResponse(VersionCheckMixin, TestCase):
|
||||||
'e09fef55f2673458bc31f')
|
'e09fef55f2673458bc31f')
|
||||||
|
|
||||||
def test_url(self):
|
def test_url(self):
|
||||||
up = self.get(self.good_data)
|
instance = self.get_update_instance(self.data)
|
||||||
up.get_rdf()
|
instance.get_output()
|
||||||
assert up.data['row']['url'] == self.get_file_url()
|
assert instance.data['row']['url'] == self.get_file_url()
|
||||||
|
|
||||||
def test_url_local_recent(self):
|
def test_url_local_recent(self):
|
||||||
a_bit_ago = datetime.now() - timedelta(seconds=60)
|
a_bit_ago = datetime.now() - timedelta(seconds=60)
|
||||||
File.objects.get(pk=67442).update(datestatuschanged=a_bit_ago)
|
File.objects.get(pk=67442).update(datestatuschanged=a_bit_ago)
|
||||||
up = self.get(self.good_data)
|
instance = self.get_update_instance(self.data)
|
||||||
up.get_rdf()
|
instance.get_output()
|
||||||
assert up.data['row']['url'] == self.get_file_url()
|
assert instance.data['row']['url'] == self.get_file_url()
|
||||||
|
|
||||||
def test_hash(self):
|
def test_hash(self):
|
||||||
rdf = self.get(self.good_data).get_rdf()
|
content = self.get_update_instance(self.data).get_output()
|
||||||
assert rdf.find('updateHash') > -1
|
data = json.loads(content)
|
||||||
|
|
||||||
|
file = File.objects.get(pk=67442)
|
||||||
|
guid = '{2fa4ed95-0317-4c6a-a74c-5f3e3912c1f9}'
|
||||||
|
assert data['addons'][guid]['updates'][0]['update_hash'] == file.hash
|
||||||
|
|
||||||
file = File.objects.get(pk=67442)
|
file = File.objects.get(pk=67442)
|
||||||
file.hash = ''
|
file.hash = ''
|
||||||
file.save()
|
file.save()
|
||||||
|
|
||||||
rdf = self.get(self.good_data).get_rdf()
|
content = self.get_update_instance(self.data).get_output()
|
||||||
assert rdf.find('updateHash') == -1
|
data = json.loads(content)
|
||||||
|
assert 'update_hash' not in data['addons'][guid]['updates'][0]
|
||||||
|
|
||||||
def test_releasenotes(self):
|
def test_releasenotes(self):
|
||||||
rdf = self.get(self.good_data).get_rdf()
|
content = self.get_update_instance(self.data).get_output()
|
||||||
assert rdf.find('updateInfoURL') > -1
|
data = json.loads(content)
|
||||||
|
guid = '{2fa4ed95-0317-4c6a-a74c-5f3e3912c1f9}'
|
||||||
|
assert data['addons'][guid]['updates'][0]['update_info_url']
|
||||||
|
|
||||||
version = Version.objects.get(pk=81551)
|
version = Version.objects.get(pk=81551)
|
||||||
version.update(releasenotes=None)
|
version.update(releasenotes=None)
|
||||||
|
|
||||||
rdf = self.get(self.good_data).get_rdf()
|
content = self.get_update_instance(self.data).get_output()
|
||||||
assert rdf.find('updateInfoURL') == -1
|
data = json.loads(content)
|
||||||
|
assert 'update_info_url' not in data['addons'][guid]['updates'][0]
|
||||||
|
|
||||||
def test_sea_monkey(self):
|
def test_seamonkey_serve_rdf(self):
|
||||||
data = {
|
data = {
|
||||||
'id': 'bettergmail2@ginatrapani.org',
|
'id': 'bettergmail2@ginatrapani.org',
|
||||||
'version': '1',
|
'version': '1',
|
||||||
|
@ -678,107 +732,35 @@ class TestResponse(VersionCheckMixin, TestCase):
|
||||||
'reqVersion': 1,
|
'reqVersion': 1,
|
||||||
'appVersion': '1.0',
|
'appVersion': '1.0',
|
||||||
}
|
}
|
||||||
up = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
rdf = up.get_rdf()
|
result = instance.get_output()
|
||||||
assert up.data['row']['hash'].startswith('sha256:9d9a389')
|
assert instance.data['row']['hash'].startswith('sha256:9d9a389')
|
||||||
assert up.data['row']['min'] == '1.0'
|
assert instance.data['row']['min'] == '1.0'
|
||||||
assert up.data['row']['version'] == '0.5.2'
|
assert instance.data['row']['version'] == '0.5.2'
|
||||||
assert rdf.find(data['appID']) > -1
|
|
||||||
|
# Result should be a valid rdf.
|
||||||
|
rdflib.Graph().parse(data=result)
|
||||||
|
|
||||||
def test_no_updates_at_all(self):
|
def test_no_updates_at_all(self):
|
||||||
self.addon_one.versions.all().delete()
|
self.addon_one.versions.all().delete()
|
||||||
upd = self.get(self.good_data)
|
instance = self.get_update_instance(self.data)
|
||||||
assert upd.get_rdf() == upd.get_no_updates_rdf()
|
assert instance.use_json is True
|
||||||
|
assert (
|
||||||
|
json.loads(instance.get_output()) ==
|
||||||
|
instance.get_no_updates_output())
|
||||||
|
|
||||||
|
# Seamonkey should have a rdf version of 'no updates'.
|
||||||
|
self.data['appID'] = '{92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a}'
|
||||||
|
instance = self.get_update_instance(self.data)
|
||||||
|
assert instance.use_json is False
|
||||||
|
result = instance.get_output()
|
||||||
|
assert result == instance.get_no_updates_output()
|
||||||
|
rdflib.Graph().parse(data=result)
|
||||||
|
|
||||||
def test_no_updates_my_fx(self):
|
def test_no_updates_my_fx(self):
|
||||||
data = self.good_data.copy()
|
data = self.data.copy()
|
||||||
data['appVersion'] = '5.0.1'
|
data['appVersion'] = '5.0.1'
|
||||||
upd = self.get(data)
|
instance = self.get_update_instance(data)
|
||||||
assert upd.get_rdf() == upd.get_no_updates_rdf()
|
assert (
|
||||||
|
json.loads(instance.get_output()) ==
|
||||||
|
instance.get_no_updates_output())
|
||||||
class TestFirefoxHotfix(VersionCheckMixin, TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
"""Create a "firefox hotfix" addon with a few versions.
|
|
||||||
|
|
||||||
Check bug 1031516 for more info.
|
|
||||||
|
|
||||||
"""
|
|
||||||
super(TestFirefoxHotfix, self).setUp()
|
|
||||||
self.addon = amo.tests.addon_factory(guid='firefox-hotfix@mozilla.org')
|
|
||||||
|
|
||||||
# First signature changing hotfix.
|
|
||||||
amo.tests.version_factory(addon=self.addon, version='20121019.01',
|
|
||||||
min_app_version='10.0',
|
|
||||||
max_app_version='16.*')
|
|
||||||
|
|
||||||
# Second signature changing hotfix.
|
|
||||||
amo.tests.version_factory(addon=self.addon, version='20130826.01',
|
|
||||||
min_app_version='10.0',
|
|
||||||
max_app_version='24.*')
|
|
||||||
|
|
||||||
# Newest version compatible with any Firefox.
|
|
||||||
amo.tests.version_factory(addon=self.addon, version='20202020.01',
|
|
||||||
min_app_version='10.0',
|
|
||||||
max_app_version='30.*')
|
|
||||||
|
|
||||||
self.data = {
|
|
||||||
'id': 'firefox-hotfix@mozilla.org',
|
|
||||||
'appID': '{ec8030f7-c20a-464f-9b0e-13a3a9e97384}',
|
|
||||||
'reqVersion': '2',
|
|
||||||
}
|
|
||||||
|
|
||||||
def test_10_16_first_hotfix(self):
|
|
||||||
"""The first hotfix changing the signature should be served."""
|
|
||||||
self.data['version'] = ''
|
|
||||||
self.data['appVersion'] = '16.0.1'
|
|
||||||
|
|
||||||
up = self.get(self.data)
|
|
||||||
rdf = up.get_rdf()
|
|
||||||
assert rdf.find('20121019.01') > -1
|
|
||||||
|
|
||||||
def test_10_16_second_hotfix(self):
|
|
||||||
"""The second hotfix changing the signature should be served."""
|
|
||||||
self.data['version'] = '20121019.01'
|
|
||||||
self.data['appVersion'] = '16.0.1'
|
|
||||||
|
|
||||||
up = self.get(self.data)
|
|
||||||
rdf = up.get_rdf()
|
|
||||||
assert rdf.find('20130826.01') > -1
|
|
||||||
|
|
||||||
def test_10_16_newest_hotfix(self):
|
|
||||||
"""The newest hotfix should be served."""
|
|
||||||
self.data['version'] = '20130826.01'
|
|
||||||
self.data['appVersion'] = '16.0.1'
|
|
||||||
|
|
||||||
up = self.get(self.data)
|
|
||||||
rdf = up.get_rdf()
|
|
||||||
assert rdf.find('20202020.01') > -1
|
|
||||||
|
|
||||||
def test_16_24_second_hotfix(self):
|
|
||||||
"""The second hotfix changing the signature should be served."""
|
|
||||||
self.data['version'] = ''
|
|
||||||
self.data['appVersion'] = '16.0.2'
|
|
||||||
|
|
||||||
up = self.get(self.data)
|
|
||||||
rdf = up.get_rdf()
|
|
||||||
assert rdf.find('20130826.01') > -1
|
|
||||||
|
|
||||||
def test_16_24_newest_hotfix(self):
|
|
||||||
"""The newest hotfix should be served."""
|
|
||||||
self.data['version'] = '20130826.01'
|
|
||||||
self.data['appVersion'] = '16.0.2'
|
|
||||||
|
|
||||||
up = self.get(self.data)
|
|
||||||
rdf = up.get_rdf()
|
|
||||||
assert rdf.find('20202020.01') > -1
|
|
||||||
|
|
||||||
def test_above_24_latest_version(self):
|
|
||||||
"""The newest hotfix should be served."""
|
|
||||||
self.data['version'] = ''
|
|
||||||
self.data['appVersion'] = '28.0'
|
|
||||||
|
|
||||||
up = self.get(self.data)
|
|
||||||
rdf = up.get_rdf()
|
|
||||||
assert rdf.find('20202020.01') > -1
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче