445 строки
18 KiB
Python
445 строки
18 KiB
Python
# -*- coding: utf8 -*-
|
|
import calendar
|
|
import json
|
|
import time
|
|
import uuid
|
|
|
|
from django.conf import settings
|
|
|
|
import mock
|
|
from nose.tools import eq_, ok_
|
|
from pyquery import PyQuery as pq
|
|
from test_utils import RequestFactory
|
|
|
|
from addons.models import AddonUser
|
|
import amo
|
|
import amo.tests
|
|
from amo.helpers import absolutify
|
|
from amo.urlresolvers import reverse
|
|
from devhub.models import AppLog
|
|
from mkt.constants import apps
|
|
from mkt.site.fixtures import fixture
|
|
from mkt.webapps.models import Webapp
|
|
from mkt.receipts.utils import create_test_receipt
|
|
from mkt.receipts.views import devhub_verify
|
|
from services.verify import decode_receipt, settings as verify_settings
|
|
from users.models import UserProfile
|
|
from zadmin.models import DownloadSource
|
|
|
|
from .test_models import TEST_LEEWAY
|
|
|
|
|
|
@mock.patch.object(settings, 'WEBAPPS_RECEIPT_KEY',
|
|
amo.tests.AMOPaths.sample_key())
|
|
class TestInstall(amo.tests.TestCase):
|
|
fixtures = fixture('user_999', 'user_editor', 'user_editor_group',
|
|
'group_editor')
|
|
|
|
def setUp(self):
|
|
self.addon = amo.tests.app_factory(manifest_url='http://cbc.ca/man')
|
|
self.url = self.addon.get_detail_url('record')
|
|
self.user = UserProfile.objects.get(email='regular@mozilla.com')
|
|
assert self.client.login(username=self.user.email, password='password')
|
|
|
|
def test_pending_free_for_reviewer(self):
|
|
self.addon.update(status=amo.STATUS_PENDING)
|
|
assert self.client.login(username='editor@mozilla.com',
|
|
password='password')
|
|
eq_(self.client.post(self.url).status_code, 200)
|
|
|
|
def test_pending_free_for_developer(self):
|
|
AddonUser.objects.create(addon=self.addon, user=self.user)
|
|
self.addon.update(status=amo.STATUS_PENDING)
|
|
eq_(self.client.post(self.url).status_code, 200)
|
|
|
|
def test_pending_free_for_anonymous(self):
|
|
self.addon.update(status=amo.STATUS_PENDING)
|
|
eq_(self.client.post(self.url).status_code, 404)
|
|
|
|
def test_pending_paid_for_reviewer(self):
|
|
self.addon.update(status=amo.STATUS_PENDING,
|
|
premium_type=amo.ADDON_PREMIUM)
|
|
assert self.client.login(username='editor@mozilla.com',
|
|
password='password')
|
|
eq_(self.client.post(self.url).status_code, 200)
|
|
# Because they aren't using reviewer tools, they'll get a normal
|
|
# install record and receipt.
|
|
eq_(self.addon.installed.all()[0].install_type,
|
|
apps.INSTALL_TYPE_USER)
|
|
|
|
def test_pending_paid_for_admin(self):
|
|
self.addon.update(status=amo.STATUS_PENDING,
|
|
premium_type=amo.ADDON_PREMIUM)
|
|
self.grant_permission(self.user, '*:*')
|
|
eq_(self.client.post(self.url).status_code, 200)
|
|
# Check ownership ignores admin users.
|
|
eq_(self.addon.installed.all()[0].install_type,
|
|
apps.INSTALL_TYPE_USER)
|
|
|
|
def test_pending_paid_for_developer(self):
|
|
AddonUser.objects.create(addon=self.addon, user=self.user)
|
|
self.addon.update(status=amo.STATUS_PENDING,
|
|
premium_type=amo.ADDON_PREMIUM)
|
|
eq_(self.client.post(self.url).status_code, 200)
|
|
eq_(self.user.installed_set.all()[0].install_type,
|
|
apps.INSTALL_TYPE_DEVELOPER)
|
|
|
|
def test_pending_paid_for_anonymous(self):
|
|
self.addon.update(status=amo.STATUS_PENDING,
|
|
premium_type=amo.ADDON_PREMIUM)
|
|
eq_(self.client.post(self.url).status_code, 404)
|
|
|
|
def test_not_record_addon(self):
|
|
self.addon.update(type=amo.ADDON_EXTENSION)
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 404)
|
|
eq_(self.user.installed_set.count(), 0)
|
|
|
|
@mock.patch('mkt.webapps.models.Webapp.has_purchased')
|
|
def test_paid(self, has_purchased):
|
|
has_purchased.return_value = True
|
|
self.addon.update(premium_type=amo.ADDON_PREMIUM)
|
|
eq_(self.client.post(self.url).status_code, 200)
|
|
|
|
def test_own_payments(self):
|
|
self.addon.update(premium_type=amo.ADDON_OTHER_INAPP)
|
|
eq_(self.client.post(self.url).status_code, 200)
|
|
|
|
@mock.patch('mkt.webapps.models.Webapp.has_purchased')
|
|
def test_not_paid(self, has_purchased):
|
|
has_purchased.return_value = False
|
|
self.addon.update(premium_type=amo.ADDON_PREMIUM)
|
|
eq_(self.client.post(self.url).status_code, 403)
|
|
|
|
def test_record_logged_out(self):
|
|
self.client.logout()
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 200)
|
|
|
|
@mock.patch('mkt.receipts.views.receipt_cef.log')
|
|
def test_log_metrics(self, cef):
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 200)
|
|
logs = AppLog.objects.filter(addon=self.addon)
|
|
eq_(logs.count(), 1)
|
|
eq_(logs[0].activity_log.action, amo.LOG.INSTALL_ADDON.id)
|
|
|
|
@mock.patch('mkt.receipts.views.record_action')
|
|
@mock.patch('mkt.receipts.views.receipt_cef.log')
|
|
def test_record_metrics(self, cef, record_action):
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 200)
|
|
eq_(record_action.call_args[0][0], 'install')
|
|
eq_(record_action.call_args[0][2], {'app-domain': u'http://cbc.ca',
|
|
'app-id': self.addon.pk,
|
|
'anonymous': False})
|
|
|
|
@mock.patch('mkt.receipts.views.record_action')
|
|
@mock.patch('mkt.receipts.views.receipt_cef.log')
|
|
@mock.patch.object(settings, 'SITE_URL', 'http://test.com')
|
|
def test_record_metrics_packaged_app(self, cef, record_action):
|
|
# Mimic packaged app.
|
|
self.addon.update(is_packaged=True, manifest_url=None)
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 200)
|
|
eq_(record_action.call_args[0][0], 'install')
|
|
eq_(record_action.call_args[0][2], {
|
|
'app-domain': u'http://test.com',
|
|
'app-id': self.addon.pk,
|
|
'anonymous': False})
|
|
|
|
@mock.patch('mkt.receipts.views.receipt_cef.log')
|
|
def test_cef_logs(self, cef):
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 200)
|
|
eq_(len(cef.call_args_list), 1)
|
|
eq_([x[0][2] for x in cef.call_args_list], ['sign'])
|
|
|
|
@mock.patch('mkt.receipts.views.receipt_cef.log')
|
|
def test_record_install(self, cef):
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 200)
|
|
installed = self.user.installed_set.all()
|
|
eq_(len(installed), 1)
|
|
eq_(installed[0].install_type, apps.INSTALL_TYPE_USER)
|
|
|
|
@mock.patch('mkt.receipts.views.receipt_cef.log')
|
|
def test_record_multiple_installs(self, cef):
|
|
self.client.post(self.url)
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 200)
|
|
eq_(self.user.installed_set.count(), 1)
|
|
|
|
@mock.patch.object(settings, 'WEBAPPS_RECEIPT_KEY',
|
|
amo.tests.AMOPaths.sample_key())
|
|
@mock.patch('mkt.receipts.views.receipt_cef.log')
|
|
def test_record_receipt(self, cef):
|
|
res = self.client.post(self.url)
|
|
content = json.loads(res.content)
|
|
assert content.get('receipt'), content
|
|
|
|
def test_installed_client_data(self):
|
|
download_source = DownloadSource.objects.create(name='mkt-home')
|
|
device_type = 'mobile'
|
|
user_agent = 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0)'
|
|
|
|
self.addon.update(type=amo.ADDON_WEBAPP)
|
|
res = self.client.post(self.url,
|
|
data={'device_type': device_type,
|
|
'is_chromeless': False,
|
|
'src': download_source.name},
|
|
HTTP_USER_AGENT=user_agent)
|
|
|
|
eq_(res.status_code, 200)
|
|
eq_(self.user.installed_set.count(), 1)
|
|
ins = self.user.installed_set.get()
|
|
eq_(ins.client_data.download_source, download_source)
|
|
eq_(ins.client_data.device_type, device_type)
|
|
eq_(ins.client_data.user_agent, user_agent)
|
|
eq_(ins.client_data.is_chromeless, False)
|
|
eq_(not ins.client_data.language, False)
|
|
eq_(not ins.client_data.region, False)
|
|
|
|
|
|
class TestReceiptVerify(amo.tests.TestCase):
|
|
fixtures = fixture('user_999', 'user_editor', 'user_editor_group',
|
|
'group_editor')
|
|
|
|
def setUp(self):
|
|
super(TestReceiptVerify, self).setUp()
|
|
self.app = Webapp.objects.create(app_slug='foo', guid=uuid.uuid4())
|
|
self.url = reverse('receipt.verify',
|
|
args=[self.app.guid])
|
|
self.log = AppLog.objects.filter(addon=self.app)
|
|
self.reviewer = UserProfile.objects.get(pk=5497308)
|
|
|
|
def get_mock(self, user=None, **kwargs):
|
|
self.verify = mock.Mock()
|
|
self.verify.return_value = json.dumps(kwargs)
|
|
self.verify.check_without_purchase.return_value = json.dumps(
|
|
{'status': 'ok'})
|
|
self.verify.invalid.return_value = json.dumps({'status': 'invalid'})
|
|
self.verify.user_id = user.pk if user else self.reviewer.pk
|
|
return self.verify
|
|
|
|
def test_post_required(self):
|
|
eq_(self.client.get(self.url).status_code, 405)
|
|
|
|
@mock.patch('mkt.receipts.views.Verify')
|
|
def test_empty(self, verify):
|
|
vfy = self.get_mock(user=self.reviewer, status='invalid')
|
|
# Because the receipt was empty, this never got set and so
|
|
# we didn't log it.
|
|
vfy.user_id = None
|
|
verify.return_value = vfy
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 200)
|
|
eq_(self.log.count(), 0)
|
|
eq_(json.loads(res.content)['status'], 'invalid')
|
|
|
|
@mock.patch('mkt.receipts.views.Verify')
|
|
def test_good(self, verify):
|
|
verify.return_value = self.get_mock(user=self.reviewer, status='ok')
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 200)
|
|
eq_(self.log.count(), 1)
|
|
eq_(json.loads(res.content)['status'], 'ok')
|
|
|
|
@mock.patch('mkt.receipts.views.Verify')
|
|
def test_not_reviewer(self, verify):
|
|
self.reviewer.groups.clear()
|
|
verify.return_value = self.get_mock(user=self.reviewer, status='ok')
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 200)
|
|
eq_(self.log.count(), 0)
|
|
eq_(json.loads(res.content)['status'], 'invalid')
|
|
|
|
@mock.patch('mkt.receipts.views.Verify')
|
|
def test_not_there(self, verify):
|
|
verify.return_value = self.get_mock(user=self.reviewer, status='ok')
|
|
self.reviewer.delete()
|
|
res = self.client.post(self.url)
|
|
eq_(res['Access-Control-Allow-Origin'], '*')
|
|
eq_(json.loads(res.content)['status'], 'invalid')
|
|
|
|
@mock.patch('mkt.receipts.views.Verify')
|
|
def test_logs(self, verify):
|
|
verify.return_value = self.get_mock(user=self.reviewer, status='ok')
|
|
eq_(self.log.count(), 0)
|
|
res = self.client.post(self.url)
|
|
eq_(self.log.count(), 1)
|
|
eq_(res.status_code, 200)
|
|
|
|
@mock.patch('mkt.receipts.views.Verify')
|
|
def test_logs_developer(self, verify):
|
|
developer = UserProfile.objects.get(pk=999)
|
|
AddonUser.objects.create(addon=self.app, user=developer)
|
|
verify.return_value = self.get_mock(user=developer, status='ok')
|
|
res = self.client.post(self.url)
|
|
eq_(res['Access-Control-Allow-Origin'], '*')
|
|
eq_(self.log.count(), 1)
|
|
eq_(res.status_code, 200)
|
|
|
|
|
|
class TestReceiptIssue(amo.tests.TestCase):
|
|
fixtures = fixture('user_999', 'user_editor', 'user_editor_group',
|
|
'group_editor', 'webapp_337141')
|
|
|
|
def setUp(self):
|
|
super(TestReceiptIssue, self).setUp()
|
|
self.app = Webapp.objects.get(pk=337141)
|
|
self.url = reverse('receipt.issue', args=[self.app.app_slug])
|
|
self.reviewer = UserProfile.objects.get(pk=5497308)
|
|
self.user = UserProfile.objects.get(pk=999)
|
|
|
|
@mock.patch('mkt.receipts.views.create_receipt')
|
|
def test_issued(self, create_receipt):
|
|
create_receipt.return_value = 'foo'
|
|
self.client.login(username=self.reviewer.email, password='password')
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 200)
|
|
eq_(create_receipt.call_args[1]['flavour'], 'reviewer')
|
|
eq_(self.reviewer.installed_set.all()[0].install_type,
|
|
apps.INSTALL_TYPE_REVIEWER)
|
|
|
|
def test_get(self):
|
|
self.client.login(username=self.reviewer.email, password='password')
|
|
res = self.client.get(self.url)
|
|
eq_(res.status_code, 405)
|
|
|
|
def test_issued_anon(self):
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 403)
|
|
|
|
def test_issued_not_reviewer(self):
|
|
self.client.login(username=self.user, password='password')
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 403)
|
|
|
|
@mock.patch('mkt.receipts.views.create_receipt')
|
|
def test_issued_developer(self, create_receipt):
|
|
create_receipt.return_value = 'foo'
|
|
AddonUser.objects.create(user=self.user, addon=self.app)
|
|
self.client.login(username=self.user.email, password='password')
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 200)
|
|
eq_(create_receipt.call_args[1]['flavour'], 'developer')
|
|
eq_(self.user.installed_set.all()[0].install_type,
|
|
apps.INSTALL_TYPE_DEVELOPER)
|
|
|
|
@mock.patch('mkt.receipts.views.create_receipt')
|
|
def test_unicode_name(self, create_receipt):
|
|
"""
|
|
Regression test to ensure that the CEF log works. Pass through the
|
|
app.pk instead of the full unicode name, until the CEF library is
|
|
fixed, or metlog is used.
|
|
"""
|
|
create_receipt.return_value = 'foo'
|
|
self.app.name = u'\u0627\u0644\u062a\u0637\u0628-news'
|
|
self.app.save()
|
|
|
|
self.client.login(username=self.reviewer.email, password='password')
|
|
res = self.client.post(self.url)
|
|
eq_(res.status_code, 200)
|
|
|
|
|
|
class TestReceiptCheck(amo.tests.TestCase):
|
|
fixtures = fixture('user_999', 'user_editor', 'user_editor_group',
|
|
'group_editor', 'webapp_337141')
|
|
|
|
def setUp(self):
|
|
super(TestReceiptCheck, self).setUp()
|
|
self.app = Webapp.objects.get(pk=337141)
|
|
self.app.update(status=amo.STATUS_PENDING)
|
|
self.url = reverse('receipt.check',
|
|
args=[self.app.guid])
|
|
self.reviewer = UserProfile.objects.get(pk=5497308)
|
|
self.user = UserProfile.objects.get(pk=999)
|
|
|
|
def test_anon(self):
|
|
eq_(self.client.get(self.url).status_code, 302)
|
|
|
|
def test_not_reviewer(self):
|
|
self.client.login(username=self.user.email, password='password')
|
|
eq_(self.client.get(self.url).status_code, 403)
|
|
|
|
def test_not_there(self):
|
|
self.client.login(username=self.reviewer.email, password='password')
|
|
res = self.client.get(self.url)
|
|
eq_(res.status_code, 200)
|
|
eq_(json.loads(res.content)['status'], False)
|
|
|
|
def test_there(self):
|
|
self.client.login(username=self.reviewer.email, password='password')
|
|
amo.log(amo.LOG.RECEIPT_CHECKED, self.app, user=self.reviewer)
|
|
res = self.client.get(self.url)
|
|
eq_(res.status_code, 200)
|
|
eq_(json.loads(res.content)['status'], True)
|
|
|
|
|
|
class RawRequestFactory(RequestFactory):
|
|
"""A request factory that does not encode the body."""
|
|
|
|
def _encode_data(self, data, content_type):
|
|
return data
|
|
|
|
|
|
# Ooof.
|
|
@mock.patch.object(verify_settings, 'WEBAPPS_RECEIPT_KEY',
|
|
amo.tests.AMOPaths.sample_key())
|
|
@mock.patch.object(settings, 'WEBAPPS_RECEIPT_KEY',
|
|
amo.tests.AMOPaths.sample_key())
|
|
@mock.patch.object(settings, 'SITE_URL', 'https://foo.com')
|
|
@mock.patch.object(verify_settings, 'DOMAIN', 'foo.com')
|
|
class TestDevhubReceipts(amo.tests.TestCase):
|
|
|
|
def setUp(self):
|
|
self.issue = reverse('receipt.test.issue')
|
|
|
|
def test_install_page(self):
|
|
eq_(self.client.get(reverse('receipt.test.install')).status_code, 200)
|
|
|
|
def test_details_page(self):
|
|
eq_(self.client.get(reverse('receipt.test.details')).status_code, 200)
|
|
|
|
def test_issue_get(self):
|
|
eq_(self.client.get(self.issue).status_code, 405)
|
|
|
|
def test_issue_none(self):
|
|
data = {'receipt_type': 'none', 'manifest_url': 'http://foo.com/'}
|
|
res = self.client.post(self.issue, data=data)
|
|
eq_(json.loads(res.content)['receipt'], '')
|
|
|
|
def test_bad_url(self):
|
|
data = {'receipt_type': 'none', 'manifest_url': ''}
|
|
res = self.client.post(self.issue, data=data)
|
|
ok_(json.loads(res.content)['error'], '')
|
|
|
|
def test_issue_expired(self):
|
|
data = {'receipt_type': 'expired', 'manifest_url': 'http://foo.com/'}
|
|
res = self.client.post(self.issue, data=data)
|
|
data = decode_receipt(json.loads(res.content)['receipt']
|
|
.encode('ascii'))
|
|
eq_(data['verify'], absolutify(reverse('receipt.test.verify',
|
|
kwargs={'status': 'expired'})))
|
|
ok_(data['exp'] > (calendar.timegm(time.gmtime()) +
|
|
(60 * 60 * 24) - TEST_LEEWAY))
|
|
|
|
def test_issue_other(self):
|
|
data = {'receipt_type': 'foo', 'manifest_url': ''}
|
|
res = self.client.post(self.issue, data=data)
|
|
ok_(json.loads(res.content)['error'])
|
|
|
|
def test_verify_fails(self):
|
|
req = RawRequestFactory().post('/', '')
|
|
res = devhub_verify(req, 'expired')
|
|
eq_(json.loads(res.content)['status'], 'invalid')
|
|
|
|
def test_verify(self):
|
|
url = absolutify(reverse('receipt.test.verify',
|
|
kwargs={'status': 'expired'}))
|
|
receipt = create_test_receipt('http://foo', 'expired')
|
|
req = RawRequestFactory().post(url, receipt)
|
|
res = devhub_verify(req, 'expired')
|
|
eq_(json.loads(res.content)['status'], 'expired')
|