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:
Mathieu Pillard 2018-05-17 14:39:46 +02:00 коммит произвёл GitHub
Родитель 968c7636bf
Коммит 131c8f22db
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
3 изменённых файлов: 302 добавлений и 267 удалений

Просмотреть файл

@ -1,3 +1,4 @@
import json
import sys
from django.utils.encoding import force_bytes
@ -18,8 +19,7 @@ except ImportError:
from olympia.constants import applications, base
import olympia.core.logger
from utils import (
APP_GUIDS, get_cdn_url, log_configure, PLATFORMS)
from utils import get_cdn_url, log_configure, PLATFORM_NAMES_TO_CONSTANTS
# Go configure the log.
@ -81,6 +81,15 @@ class Update(object):
self.data['row'] = {}
self.version_int = 0
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):
# If you accessing this from unit tests, then before calling
@ -96,10 +105,12 @@ class Update(object):
if field not in data:
return False
data['app_id'] = APP_GUIDS.get(data['appID'])
if not data['app_id']:
app = applications.APP_GUIDS.get(data['appID'])
if not app:
return False
data['app_id'] = app.id
sql = """SELECT id, status, addontype_id, guid FROM addons
WHERE guid = %(guid)s AND
inactive = 0 AND
@ -118,7 +129,7 @@ class Update(object):
data['version_int'] = version_int(data['appVersion'])
if 'appOS' in data:
for k, v in PLATFORMS.items():
for k, v in PLATFORM_NAMES_TO_CONSTANTS.items():
if k in data['appOS']:
data['appOS'] = v
break
@ -243,27 +254,68 @@ class Update(object):
return False
def get_bad_rdf(self):
return bad_rdf
def get_rdf(self):
def get_output(self):
if self.is_valid():
if self.get_update():
rdf = self.get_good_rdf()
contents = self.get_success_output()
else:
rdf = self.get_no_updates_rdf()
contents = self.get_no_updates_output()
else:
rdf = self.get_bad_rdf()
contents = self.get_error_output()
self.cursor.close()
if self.conn:
self.conn.close()
return rdf
return json.dumps(contents) if self.use_json else contents
def get_no_updates_rdf(self):
name = base.ADDON_SLUGS_UPDATE[self.data['type']]
return no_updates_rdf % ({'guid': self.data['guid'], 'type': name})
def get_error_output(self):
return {} if self.use_json else bad_rdf
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['if_hash'] = ''
if data['hash']:
@ -283,7 +335,8 @@ class Update(object):
return '%s GMT' % formatdate(time() + secs)[:25]
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'),
('Last-Modified', self.format_date(0)),
('Expires', self.format_date(3600)),
@ -302,7 +355,7 @@ def application(environ, start_response):
compat_mode = data.pop('compatMode', 'strict')
try:
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)))
except Exception:
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.
# Perhaps we can import these without any problems and we can
# remove all this.
from olympia.constants.applications import APPS_ALL
from olympia.constants.platforms import PLATFORMS
@ -51,8 +50,9 @@ def user_media_url(what):
return getattr(settings, key, default)
APP_GUIDS = dict([(app.guid, app.id) for app in APPS_ALL.values()])
PLATFORMS = dict([(plat.api_name, plat.id) for plat in PLATFORMS.values()])
PLATFORM_NAMES_TO_CONSTANTS = {
platform.api_name: platform.id for platform in PLATFORMS.values()
}
ADDON_SLUGS_UPDATE = {
1: 'extension',

Просмотреть файл

@ -1,7 +1,12 @@
# -*- coding: utf-8 -*-
import json
import mock
from datetime import datetime, timedelta
from email import utils
import rdflib
from django.db import connection
from services import update
@ -17,10 +22,10 @@ from olympia.versions.models import ApplicationsVersions, Version
class VersionCheckMixin(object):
def get(self, data):
up = update.Update(data)
up.cursor = connection.cursor()
return up
def get_update_instance(self, data):
instance = update.Update(data)
instance.cursor = connection.cursor()
return instance
class TestDataValidate(VersionCheckMixin, TestCase):
@ -28,7 +33,7 @@ class TestDataValidate(VersionCheckMixin, TestCase):
def setUp(self):
super(TestDataValidate, self).setUp()
self.good_data = {
self.data = {
'id': '{2fa4ed95-0317-4c6a-a74c-5f3e3912c1f9}',
'version': '2.0.58',
'reqVersion': 1,
@ -37,74 +42,74 @@ class TestDataValidate(VersionCheckMixin, TestCase):
}
def test_app_os(self):
data = self.good_data.copy()
data = self.data.copy()
data['appOS'] = 'something %s penguin' % amo.PLATFORM_LINUX.api_name
form = self.get(data)
assert form.is_valid()
assert form.data['appOS'] == amo.PLATFORM_LINUX.id
instance = self.get_update_instance(data)
assert instance.is_valid()
assert instance.data['appOS'] == amo.PLATFORM_LINUX.id
def test_app_version_fails(self):
data = self.good_data.copy()
data = self.data.copy()
del data['appID']
form = self.get(data)
assert not form.is_valid()
instance = self.get_update_instance(data)
assert not instance.is_valid()
def test_app_version_wrong(self):
data = self.good_data.copy()
data = self.data.copy()
data['appVersion'] = '67.7'
form = self.get(data)
instance = self.get_update_instance(data)
# If you pass through the wrong version that's fine
# you will just end up with no updates because your
# version_int will be out.
assert form.is_valid()
assert instance.is_valid()
def test_app_version(self):
data = self.good_data.copy()
form = self.get(data)
assert form.is_valid()
assert form.data['version_int'] == 3070000001000
data = self.data.copy()
instance = self.get_update_instance(data)
assert instance.is_valid()
assert instance.data['version_int'] == 3070000001000
def test_sql_injection(self):
data = self.good_data.copy()
data = self.data.copy()
data['id'] = "'"
up = self.get(data)
assert not up.is_valid()
instance = self.get_update_instance(data)
assert not instance.is_valid()
def test_inactive(self):
addon = Addon.objects.get(pk=3615)
addon.update(disabled_by_user=True)
up = self.get(self.good_data)
assert not up.is_valid()
instance = self.get_update_instance(self.data)
assert not instance.is_valid()
def test_soft_deleted(self):
addon = Addon.objects.get(pk=3615)
addon.update(status=amo.STATUS_DELETED)
up = self.get(self.good_data)
assert not up.is_valid()
instance = self.get_update_instance(self.data)
assert not instance.is_valid()
def test_disabled(self):
addon = Addon.objects.get(pk=3615)
addon.update(status=amo.STATUS_DISABLED)
up = self.get(self.good_data)
assert not up.is_valid()
instance = self.get_update_instance(self.data)
assert not instance.is_valid()
def test_no_version(self):
data = self.good_data.copy()
data = self.data.copy()
del data['version']
up = self.get(data)
assert up.is_valid()
instance = self.get_update_instance(data)
assert instance.is_valid()
def test_unlisted_addon(self):
"""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)
self.make_addon_unlisted(addon)
up = self.get(self.good_data)
assert up.is_valid()
instance = self.get_update_instance(self.data)
assert instance.is_valid()
class TestLookup(VersionCheckMixin, TestCase):
@ -123,7 +128,7 @@ class TestLookup(VersionCheckMixin, TestCase):
self.version_1_2_1 = 112396
self.version_1_2_2 = 115509
def get(self, *args):
def get_update_instance(self, *args):
data = {
'id': self.addon.guid,
'appID': args[2].guid,
@ -134,12 +139,12 @@ class TestLookup(VersionCheckMixin, TestCase):
# Allow version to be optional.
if args[0]:
data['version'] = args[0]
up = super(TestLookup, self).get(data)
assert up.is_valid()
up.data['version_int'] = args[1]
up.get_update()
return (up.data['row'].get('version_id'),
up.data['row'].get('file_id'))
instance = super(TestLookup, self).get_update_instance(data)
assert instance.is_valid()
instance.data['version_int'] = args[1]
instance.get_update()
return (instance.data['row'].get('version_id'),
instance.data['row'].get('file_id'))
def change_status(self, version, status):
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
add-on is returned.
"""
version, file = self.get('', '3000000001100',
self.app, self.platform)
version, file = self.get_update_instance(
'', '3000000001100', self.app, self.platform)
assert version == self.version_1_0_2
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
add-on is returned.
"""
version, file = self.get('', self.version_int,
self.app, self.platform)
version, file = self.get_update_instance(
'', self.version_int, self.app, self.platform)
assert version == self.version_1_2_2
def test_min_client(self):
@ -180,8 +185,8 @@ class TestLookup(VersionCheckMixin, TestCase):
appversion.min = AppVersion.objects.get(pk=325) # 3.7a5
appversion.save()
version, file = self.get('', '3070000005000', # 3.7a5pre
self.app, self.platform)
version, file = self.get_update_instance(
'', '3070000005000', self.app, self.platform) # 3.7a5pre
assert version == self.version_1_1_3
def test_new_client_ordering(self):
@ -201,8 +206,8 @@ class TestLookup(VersionCheckMixin, TestCase):
application_version.max_id = 329
application_version.save()
version, file = self.get('', self.version_int,
self.app, self.platform)
version, file = self.get_update_instance(
'', self.version_int, self.app, self.platform)
assert version == self.version_1_2_2
def test_public(self):
@ -212,8 +217,8 @@ class TestLookup(VersionCheckMixin, TestCase):
self.change_status(self.version_1_2_2, amo.STATUS_PENDING)
self.addon.reload()
assert self.addon.status == amo.STATUS_PUBLIC
version, file = self.get('1.2', self.version_int,
self.app, self.platform)
version, file = self.get_update_instance(
'1.2', self.version_int, self.app, self.platform)
assert version == self.version_1_2_1
def test_no_unlisted(self):
@ -224,8 +229,8 @@ class TestLookup(VersionCheckMixin, TestCase):
channel=amo.RELEASE_CHANNEL_UNLISTED)
self.addon.reload()
assert self.addon.status == amo.STATUS_PUBLIC
version, file = self.get('1.2', self.version_int,
self.app, self.platform)
version, file = self.get_update_instance(
'1.2', self.version_int, self.app, self.platform)
assert version == self.version_1_2_1
def test_can_downgrade(self):
@ -236,8 +241,8 @@ class TestLookup(VersionCheckMixin, TestCase):
self.change_status(self.version_1_2_0, amo.STATUS_PENDING)
for v in Version.objects.filter(pk__gte=self.version_1_2_1):
v.delete()
version, file = self.get('1.2', self.version_int,
self.app, self.platform)
version, file = self.get_update_instance(
'1.2', self.version_int, self.app, self.platform)
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_version(self.version_1_2_0, '1.2beta')
version, file = self.get('1.2', self.version_int,
self.app, self.platform)
version, file = self.get_update_instance(
'1.2', self.version_int, self.app, self.platform)
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')
Version.objects.get(pk=self.version_1_2_0).files.all().delete()
version, file = self.get('1.2beta', self.version_int,
self.app, self.platform)
version, file = self.get_update_instance(
'1.2beta', self.version_int, self.app, self.platform)
dest = Version.objects.get(pk=self.version_1_2_2)
assert dest.addon.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.addon.update(status=amo.STATUS_NULL)
version, file = self.get('1.2.1', self.version_int,
self.app, self.platform)
version, file = self.get_update_instance(
'1.2.1', self.version_int, self.app, self.platform)
assert version == self.version_1_2_1
def test_platform_does_not_exist(self):
@ -292,8 +297,8 @@ class TestLookup(VersionCheckMixin, TestCase):
file.platform = amo.PLATFORM_LINUX.id
file.save()
version, file = self.get('1.2', self.version_int,
self.app, self.platform)
version, file = self.get_update_instance(
'1.2', self.version_int, self.app, self.platform)
assert version == self.version_1_2_1
def test_platform_exists(self):
@ -303,8 +308,8 @@ class TestLookup(VersionCheckMixin, TestCase):
file.platform = amo.PLATFORM_LINUX.id
file.save()
version, file = self.get('1.2', self.version_int,
self.app, amo.PLATFORM_LINUX)
version, file = self.get_update_instance(
'1.2', self.version_int, self.app, amo.PLATFORM_LINUX)
assert version == self.version_1_2_2
def test_file_for_platform(self):
@ -318,13 +323,13 @@ class TestLookup(VersionCheckMixin, TestCase):
platform=amo.PLATFORM_WIN.id,
status=amo.STATUS_PUBLIC)
file_two.save()
version, file = self.get('1.2', self.version_int,
self.app, amo.PLATFORM_LINUX)
version, file = self.get_update_instance(
'1.2', self.version_int, self.app, amo.PLATFORM_LINUX)
assert version == self.version_1_2_2
assert file == file_one.pk
version, file = self.get('1.2', self.version_int,
self.app, amo.PLATFORM_WIN)
version, file = self.get_update_instance(
'1.2', self.version_int, self.app, amo.PLATFORM_WIN)
assert version == self.version_1_2_2
assert file == file_two.pk
@ -385,18 +390,18 @@ class TestDefaultToCompat(VersionCheckMixin, TestCase):
for file in version.files.all():
file.update(**kw)
def get(self, **kw):
up = super(TestDefaultToCompat, self).get({
def get_update_instance(self, **kw):
instance = super(TestDefaultToCompat, self).get_update_instance({
'reqVersion': 1,
'id': self.addon.guid,
'version': kw.get('item_version', '1.0'),
'appID': self.app.guid,
'appVersion': kw.get('app_version', '3.0'),
})
assert up.is_valid()
up.compat_mode = kw.get('compat_mode', 'strict')
up.get_update()
return up.data['row'].get('version_id')
assert instance.is_valid()
instance.compat_mode = kw.get('compat_mode', 'strict')
instance.get_update()
return instance.data['row'].get('version_id')
def check(self, expected):
"""
@ -408,8 +413,42 @@ class TestDefaultToCompat(VersionCheckMixin, TestCase):
for version in versions:
for mode in modes:
assert self.get(app_version=version, compat_mode=mode) == (
expected['-'.join([version, mode])])
assert (
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):
# Tests simple add-on (non-binary-components, non-strict).
@ -523,7 +562,7 @@ class TestResponse(VersionCheckMixin, TestCase):
def setUp(self):
super(TestResponse, self).setUp()
self.addon_one = Addon.objects.get(pk=3615)
self.good_data = {
self.data = {
'id': '{2fa4ed95-0317-4c6a-a74c-5f3e3912c1f9}',
'version': '2.0.58',
'reqVersion': 1,
@ -535,25 +574,35 @@ class TestResponse(VersionCheckMixin, TestCase):
self.win = amo.PLATFORM_WIN
def test_bad_guid(self):
data = self.good_data.copy()
data["id"] = "garbage"
up = self.get(data)
assert up.get_rdf() == up.get_bad_rdf()
self.data['id'] = 'garbage'
instance = self.get_update_instance(self.data)
assert instance.use_json is True
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):
file = File.objects.get(pk=67442)
file.platform = self.win.id
file.save()
data = self.good_data.copy()
data["appOS"] = self.win.api_name
up = self.get(data)
assert up.get_rdf()
assert up.data['row']['file_id'] == file.pk
data = self.data.copy()
data['appOS'] = self.win.api_name
instance = self.get_update_instance(data)
assert instance.get_output()
assert instance.data['row']['file_id'] == file.pk
data["appOS"] = self.mac.api_name
up = self.get(data)
assert up.get_rdf() == up.get_no_updates_rdf()
data['appOS'] = self.mac.api_name
instance = self.get_update_instance(data)
assert (
json.loads(instance.get_output()) ==
instance.get_no_updates_output())
def test_different_platform(self):
file = File.objects.get(pk=67442)
@ -566,70 +615,67 @@ class TestResponse(VersionCheckMixin, TestCase):
file.save()
mac_file_pk = file.pk
data = self.good_data.copy()
data = self.data.copy()
data['appOS'] = self.win.api_name
up = self.get(data)
up.is_valid()
up.get_update()
assert up.data['row']['file_id'] == file_pk
instance = self.get_update_instance(data)
instance.is_valid()
instance.get_update()
assert instance.data['row']['file_id'] == file_pk
data['appOS'] = self.mac.api_name
up = self.get(data)
up.is_valid()
up.get_update()
assert up.data['row']['file_id'] == mac_file_pk
instance = self.get_update_instance(data)
instance.is_valid()
instance.get_update()
assert instance.data['row']['file_id'] == mac_file_pk
def test_good_version(self):
up = self.get(self.good_data)
up.is_valid()
up.get_update()
assert up.data['row']['hash'].startswith('sha256:3808b13e')
assert up.data['row']['min'] == '2.0'
assert up.data['row']['max'] == '4.0'
instance = self.get_update_instance(self.data)
assert instance.use_json is True
instance.is_valid()
instance.get_update()
assert instance.data['row']['hash'].startswith('sha256:3808b13e')
assert instance.data['row']['min'] == '2.0'
assert instance.data['row']['max'] == '4.0'
def test_no_app_version(self):
data = self.good_data.copy()
data = self.data.copy()
data['appVersion'] = '1.4'
up = self.get(data)
up.is_valid()
assert not up.get_update()
instance = self.get_update_instance(data)
instance.is_valid()
assert not instance.get_update()
def test_low_app_version(self):
data = self.good_data.copy()
data = self.data.copy()
data['appVersion'] = '2.0'
up = self.get(data)
up.is_valid()
up.get_update()
assert up.data['row']['hash'].startswith('sha256:3808b13e')
assert up.data['row']['min'] == '2.0'
assert up.data['row']['max'] == '4.0'
instance = self.get_update_instance(data)
instance.is_valid()
instance.get_update()
assert instance.data['row']['hash'].startswith('sha256:3808b13e')
assert instance.data['row']['min'] == '2.0'
assert instance.data['row']['max'] == '4.0'
def test_content_type(self):
up = self.get(self.good_data)
('Content-Type', 'text/xml') in up.get_headers(1)
instance = self.get_update_instance(self.data)
('Content-Type', 'text/xml') in instance.get_headers(1)
def test_cache_control(self):
up = self.get(self.good_data)
('Cache-Control', 'public, max-age=3600') in up.get_headers(1)
instance = self.get_update_instance(self.data)
('Cache-Control', 'public, max-age=3600') in instance.get_headers(1)
def test_length(self):
up = self.get(self.good_data)
('Cache-Length', '1') in up.get_headers(1)
instance = self.get_update_instance(self.data)
('Cache-Length', '1') in instance.get_headers(1)
def test_expires(self):
"""Check there are these headers and that expires is 3600 later."""
# We aren't bother going to test the actual time in expires, that
# way lies pain with broken tests later.
up = self.get(self.good_data)
hdrs = dict(up.get_headers(1))
lm = datetime(*utils.parsedate_tz(hdrs['Last-Modified'])[:7])
exp = datetime(*utils.parsedate_tz(hdrs['Expires'])[:7])
assert (exp - lm).seconds == 3600
def test_appguid(self):
up = self.get(self.good_data)
rdf = up.get_rdf()
assert rdf.find(self.good_data['appID']) > -1
instance = self.get_update_instance(self.data)
headers = dict(instance.get_headers(1))
last_modified = datetime(
*utils.parsedate_tz(headers['Last-Modified'])[:7])
expires = datetime(*utils.parsedate_tz(headers['Expires'])[:7])
assert (expires - last_modified).seconds == 3600
def get_file_url(self):
"""Return the file url with the hash as parameter."""
@ -638,39 +684,47 @@ class TestResponse(VersionCheckMixin, TestCase):
'e09fef55f2673458bc31f')
def test_url(self):
up = self.get(self.good_data)
up.get_rdf()
assert up.data['row']['url'] == self.get_file_url()
instance = self.get_update_instance(self.data)
instance.get_output()
assert instance.data['row']['url'] == self.get_file_url()
def test_url_local_recent(self):
a_bit_ago = datetime.now() - timedelta(seconds=60)
File.objects.get(pk=67442).update(datestatuschanged=a_bit_ago)
up = self.get(self.good_data)
up.get_rdf()
assert up.data['row']['url'] == self.get_file_url()
instance = self.get_update_instance(self.data)
instance.get_output()
assert instance.data['row']['url'] == self.get_file_url()
def test_hash(self):
rdf = self.get(self.good_data).get_rdf()
assert rdf.find('updateHash') > -1
content = self.get_update_instance(self.data).get_output()
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.hash = ''
file.save()
rdf = self.get(self.good_data).get_rdf()
assert rdf.find('updateHash') == -1
content = self.get_update_instance(self.data).get_output()
data = json.loads(content)
assert 'update_hash' not in data['addons'][guid]['updates'][0]
def test_releasenotes(self):
rdf = self.get(self.good_data).get_rdf()
assert rdf.find('updateInfoURL') > -1
content = self.get_update_instance(self.data).get_output()
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.update(releasenotes=None)
rdf = self.get(self.good_data).get_rdf()
assert rdf.find('updateInfoURL') == -1
content = self.get_update_instance(self.data).get_output()
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 = {
'id': 'bettergmail2@ginatrapani.org',
'version': '1',
@ -678,107 +732,35 @@ class TestResponse(VersionCheckMixin, TestCase):
'reqVersion': 1,
'appVersion': '1.0',
}
up = self.get(data)
rdf = up.get_rdf()
assert up.data['row']['hash'].startswith('sha256:9d9a389')
assert up.data['row']['min'] == '1.0'
assert up.data['row']['version'] == '0.5.2'
assert rdf.find(data['appID']) > -1
instance = self.get_update_instance(data)
result = instance.get_output()
assert instance.data['row']['hash'].startswith('sha256:9d9a389')
assert instance.data['row']['min'] == '1.0'
assert instance.data['row']['version'] == '0.5.2'
# Result should be a valid rdf.
rdflib.Graph().parse(data=result)
def test_no_updates_at_all(self):
self.addon_one.versions.all().delete()
upd = self.get(self.good_data)
assert upd.get_rdf() == upd.get_no_updates_rdf()
instance = self.get_update_instance(self.data)
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):
data = self.good_data.copy()
data = self.data.copy()
data['appVersion'] = '5.0.1'
upd = self.get(data)
assert upd.get_rdf() == upd.get_no_updates_rdf()
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
instance = self.get_update_instance(data)
assert (
json.loads(instance.get_output()) ==
instance.get_no_updates_output())