Example #1
0
class TestSearchFilters(BaseOAuth):
    fixtures = fixture('webapp_337141', 'user_2519')

    def setUp(self):
        super(TestSearchFilters, self).setUp()
        self.req = test_utils.RequestFactory().get('/')
        self.req.user = AnonymousUser()

        self.category = Category.objects.create(name='games',
                                                slug='games',
                                                type=amo.ADDON_WEBAPP)
        # Pick a region that has relatively few filters.
        set_region(regions.UK.slug)

        self.form_class = ApiSearchForm

    def _grant(self, rules):
        self.grant_permission(self.profile, rules)
        self.req.groups = self.profile.groups.all()

    def _filter(self, req, filters, sorting=None, **kwargs):
        form = self.form_class(filters)
        if form.is_valid():
            qs = Webapp.from_search(self.req, **kwargs)
            return _filter_search(self.req, qs, form.cleaned_data,
                                  sorting)._build_query()
        else:
            return form.errors.copy()

    def test_q(self):
        qs = self._filter(self.req, {'q': 'search terms'})
        qs_str = json.dumps(qs)
        ok_('"query": "search terms"' in qs_str)
        # TODO: Could do more checking here.

    def _addon_type_check(self, query, expected=amo.ADDON_WEBAPP):
        qs = self._filter(self.req, query)
        ok_({'term': {
            'type': expected
        }} in qs['filter']['and'], 'Unexpected type. Expected: %s.' % expected)

    def test_addon_type(self):
        # Test all that should end up being ADDON_WEBAPP.
        # Note: Addon type permission can't be checked here b/c the acl check
        # happens in the view, not the _filter_search call.
        self._addon_type_check({})
        self._addon_type_check({'type': 'app'})
        self._addon_type_check({'type': 'theme'})
        # Test a bad value.
        qs = self._filter(self.req, {'type': 'vindaloo'})
        ok_(u'Select a valid choice' in qs['type'][0])

    def _status_check(self, query, expected=amo.STATUS_PUBLIC):
        qs = self._filter(self.req, query)
        ok_({'term': {
            'status': expected
        }} in qs['filter']['and'],
            'Unexpected status. Expected: %s.' % expected)

    def test_status(self):
        self.form_class = ApiReviewersSearchForm
        # Test all that should end up being public.
        # Note: Status permission can't be checked here b/c the acl check
        # happens in the view, not the _filter_search call.
        self._status_check({})
        self._status_check({'status': 'public'})
        self._status_check({'status': 'rejected'})
        # Test a bad value.
        qs = self._filter(self.req, {'status': 'vindaloo'})
        ok_(u'Select a valid choice' in qs['status'][0])

    def test_category(self):
        qs = self._filter(self.req, {'cat': self.category.slug})
        ok_({'term': {'category': self.category.slug}} in qs['filter']['and'])

    def test_device(self):
        qs = self._filter(self.req, {'device': 'desktop'})
        ok_({'term': {
            'device': DEVICE_CHOICES_IDS['desktop']
        }} in qs['filter']['and'])

    def test_premium_types(self):
        ptype = lambda p: amo.ADDON_PREMIUM_API_LOOKUP.get(p)
        # Test a single premium type.
        qs = self._filter(self.req, {'premium_types': ['free']})
        ok_({'in': {'premium_type': [ptype('free')]}} in qs['filter']['and'])
        # Test many premium types.
        qs = self._filter(self.req, {'premium_types': ['free', 'free-inapp']})
        ok_({'in': {
            'premium_type': [ptype('free'), ptype('free-inapp')]
        }} in qs['filter']['and'])
        # Test a non-existent premium type.
        qs = self._filter(self.req, {'premium_types': ['free', 'platinum']})
        ok_(u'Select a valid choice' in qs['premium_types'][0])

    def test_app_type(self):
        qs = self._filter(self.req, {'app_type': ['hosted']})
        ok_({'in': {'app_type': [1]}} in qs['filter']['and'])

    def test_manifest_url(self):
        url = 'http://hy.fr/manifest.webapp'
        qs = self._filter(self.req, {'manifest_url': url})
        ok_({'term': {'manifest_url': url}} in qs['filter']['and'])

    def test_languages(self):
        qs = self._filter(self.req, {'languages': 'fr'})
        ok_({'in': {'supported_locales': ['fr']}} in qs['filter']['and'])

        qs = self._filter(self.req, {'languages': 'ar,en-US'})
        ok_({'in': {
            'supported_locales': ['ar', 'en-US']
        }} in qs['filter']['and'])

    def test_region_exclusions(self):
        qs = self._filter(self.req, {'q': 'search terms'}, region=regions.CO)
        ok_({
            'not': {
                'filter': {
                    'term': {
                        'region_exclusions': regions.CO.id
                    }
                }
            }
        } in qs['filter']['and'])

    def test_region_exclusions_override(self):
        self.create_flag('override-region-exclusion')
        qs = self._filter(self.req, {'q': 'search terms'}, region=regions.CO)
        ok_({
            'not': {
                'filter': {
                    'term': {
                        'region_exclusions': regions.CO.id
                    }
                }
            }
        } not in qs['filter']['and'])
Example #2
0
class TestPackagedManifest(mkt.site.tests.TestCase):
    fixtures = fixture('webapp_337141', 'group_editor', 'user_editor',
                       'user_editor_group')

    def setUp(self):
        self.app = Webapp.objects.get(pk=337141)
        self.app.update(is_packaged=True)
        # Create a fake package to go along with the app.
        self.latest_file = self.app.get_latest_file()
        with private_storage.open(self.latest_file.file_path,
                                  mode='w') as package:
            test_package = zipfile.ZipFile(package, 'w')
            test_package.writestr('manifest.webapp', 'foobar')
            test_package.close()
        self.latest_file.update(hash=self.latest_file.generate_hash())

        self.url = self.app.get_manifest_url()

    def tearDown(self):
        public_storage.delete(self.latest_file.file_path)

    def _mocked_json(self):
        data = {
            u'name': u'Packaged App √',
            u'version': u'1.0',
            u'size': 123456,
            u'release_notes': u'Bug fixes',
            u'packaged_path': u'/path/to/file.zip',
        }
        return json.dumps(data)

    def login_as_reviewer(self):
        self.client.logout()
        self.login('*****@*****.**')

    def login_as_author(self):
        self.client.logout()
        user = self.app.authors.all()[0]
        self.login(user.email)

    def test_non_packaged(self):
        self.app.update(is_packaged=False)
        res = self.client.get(self.url)
        eq_(res.status_code, 404)

    def test_disabled_by_user(self):
        self.app.update(disabled_by_user=True)
        res = self.client.get(self.url)
        eq_(res.status_code, 404)

    def test_app_pending(self):
        self.app.update(status=mkt.STATUS_PENDING)
        res = self.client.get(self.url)
        eq_(res.status_code, 404)

    def test_app_pending_reviewer(self):
        self.login_as_reviewer()
        self.app.update(status=mkt.STATUS_PENDING)
        res = self.client.get(self.url)
        eq_(res.status_code, 404)

    def test_app_pending_author(self):
        self.login_as_author()
        self.app.update(status=mkt.STATUS_PENDING)
        res = self.client.get(self.url)
        eq_(res.status_code, 404)

    @mock.patch('mkt.webapps.models.Webapp.get_cached_manifest')
    def test_app_unlisted(self, _mock):
        _mock.return_value = (self._mocked_json(), 'fake_etag')
        self.app.update(status=mkt.STATUS_UNLISTED)
        res = self.client.get(self.url)
        eq_(res.status_code, 200)

    @mock.patch('mkt.webapps.models.Webapp.get_cached_manifest')
    def test_app_unlisted_reviewer(self, _mock):
        _mock.return_value = (self._mocked_json(), 'fake_etag')
        self.login_as_reviewer()
        self.app.update(status=mkt.STATUS_UNLISTED)
        res = self.client.get(self.url)
        eq_(res.status_code, 200)

    @mock.patch('mkt.webapps.models.Webapp.get_cached_manifest')
    def test_app_unlisted_author(self, _mock):
        _mock.return_value = (self._mocked_json(), 'fake_etag')
        self.login_as_author()
        self.app.update(status=mkt.STATUS_UNLISTED)
        res = self.client.get(self.url)
        eq_(res.status_code, 200)

    def test_app_private(self):
        self.app.update(status=mkt.STATUS_APPROVED)
        res = self.client.get(self.url)
        eq_(res.status_code, 404)

    @mock.patch('mkt.webapps.models.Webapp.get_cached_manifest')
    def test_app_private_reviewer(self, _mock):
        _mock.return_value = (self._mocked_json(), 'fake_etag')
        self.login_as_reviewer()
        self.app.update(status=mkt.STATUS_APPROVED)
        res = self.client.get(self.url)
        eq_(res.status_code, 404)

    @mock.patch('mkt.webapps.models.Webapp.get_cached_manifest')
    def test_app_private_author(self, _mock):
        _mock.return_value = (self._mocked_json(), 'fake_etag')
        self.login_as_author()
        self.app.update(status=mkt.STATUS_APPROVED)
        res = self.client.get(self.url)
        eq_(res.status_code, 200)

    @mock.patch('mkt.webapps.models.Webapp.get_cached_manifest')
    def test_app_public(self, _mock):
        _mock.return_value = (self._mocked_json(), 'fake_etag')
        res = self.client.get(self.url)
        eq_(res.content, self._mocked_json())
        eq_(res['Content-Type'], MANIFEST_CONTENT_TYPE)
        eq_(res['ETag'], '"fake_etag"')

    @mock.patch('mkt.webapps.models.Webapp.get_cached_manifest')
    def test_conditional_get(self, _mock):
        _mock.return_value = (self._mocked_json(), 'fake_etag')
        res = self.client.get(self.url, HTTP_IF_NONE_MATCH='"fake_etag"')
        eq_(res.content, '')
        eq_(res.status_code, 304)

    @mock.patch('mkt.webapps.models.Webapp.get_cached_manifest')
    def test_logged_out(self, _mock):
        _mock.return_value = (self._mocked_json(), 'fake_etag')
        self.client.logout()
        res = self.client.get(self.url)
        eq_(res.status_code, 200)
        eq_(res['Content-Type'], MANIFEST_CONTENT_TYPE)

    @mock.patch('mkt.webapps.models.Webapp.get_cached_manifest')
    def test_has_cors(self, _mock):
        _mock.return_value = (self._mocked_json(), 'fake_etag')
        res = self.client.get(self.url)
        self.assertCORS(res, 'get')

    @mock.patch('mkt.webapps.utils.public_storage')
    @mock.patch('mkt.webapps.models.packaged')
    def test_calls_sign(self, _packaged, _storage):
        _packaged.sign.return_value = '/path/to/signed.zip'
        _storage.size.return_value = 1234
        self.client.get(self.url)
        assert _packaged.sign.called

    @mock.patch('mkt.webapps.models.Webapp.get_cached_manifest')
    def test_queries(self, _mock):
        """
        We explicitly want to avoid wanting to use all the query transforms
        since we don't need them here.

        The queries we are expecting are:
          * 2 savepoints
          * 2 addons - 1 for the addon, and 1 for the translations

        """
        _mock.return_value = (self._mocked_json(), 'fake_etag')
        with self.assertNumQueries(4):
            res = self.client.get(self.url)
            eq_(res.status_code, 200)
Example #3
0
class TestAppFeaturesForm(amo.tests.TestCase):
    fixtures = fixture('user_999', 'webapp_337141')

    def setUp(self):
        amo.set_user(UserProfile.objects.all()[0])
        self.form = forms.AppFeaturesForm()
        self.app = Webapp.objects.get(pk=337141)
        self.features = self.app.current_version.features

    def _check_log(self, action):
        assert AppLog.objects.filter(
            addon=self.app, activity_log__action=action.id).exists(), (
                "Didn't find `%s` action in logs." % action.short)

    def test_required(self):
        f_names = self.form.fields.keys()
        for value in (True, False):
            form = forms.AppFeaturesForm(dict((n, value) for n in f_names))
            eq_(form.is_valid(), True, form.errors)

    def test_correct_fields(self):
        fields = self.form.fields
        f_values = fields.values()
        assert 'version' not in fields
        assert all(isinstance(f, BooleanField) for f in f_values)
        self.assertSetEqual(fields, AppFeatures()._fields())

    def test_required_api_fields(self):
        fields = [f.help_text for f in self.form.required_api_fields()]
        eq_(fields, sorted(f['name'] for f in APP_FEATURES.values()))

    def test_required_api_fields_nonascii(self):
        forms.AppFeaturesForm.base_fields['has_apps'].help_text = _(
            u'H\xe9llo')
        fields = [f.help_text for f in self.form.required_api_fields()]
        eq_(fields, sorted(f['name'] for f in APP_FEATURES.values()))

    def test_changes_mark_for_rereview(self):
        self.features.update(has_sms=True)
        data = {'has_apps': True}
        self.form = forms.AppFeaturesForm(instance=self.features, data=data)
        self.form.save()
        ok_(self.features.has_apps)
        ok_(not self.features.has_sms)
        ok_(not self.features.has_contacts)
        action_id = amo.LOG.REREVIEW_FEATURES_CHANGED.id
        assert AppLog.objects.filter(addon=self.app,
                                     activity_log__action=action_id).exists()
        eq_(RereviewQueue.objects.count(), 1)

    def test_no_changes_not_marked_for_rereview(self):
        self.features.update(has_sms=True)
        data = {'has_sms': True}
        self.form = forms.AppFeaturesForm(instance=self.features, data=data)
        self.form.save()
        ok_(not self.features.has_apps)
        ok_(self.features.has_sms)
        eq_(RereviewQueue.objects.count(), 0)
        action_id = amo.LOG.REREVIEW_FEATURES_CHANGED.id
        assert not AppLog.objects.filter(
            addon=self.app, activity_log__action=action_id).exists()

    def test_changes_mark_for_rereview_bypass(self):
        self.features.update(has_sms=True)
        data = {'has_apps': True}
        self.form = forms.AppFeaturesForm(instance=self.features, data=data)
        self.form.save(mark_for_rereview=False)
        ok_(self.features.has_apps)
        ok_(not self.features.has_sms)
        eq_(RereviewQueue.objects.count(), 0)
        action_id = amo.LOG.REREVIEW_FEATURES_CHANGED.id
        assert not AppLog.objects.filter(
            addon=self.app, activity_log__action=action_id).exists()
Example #4
0
class TestUpdateDeveloperName(amo.tests.TestCase):
    fixtures = fixture('webapp_337141')

    def setUp(self):
        self.app = Webapp.objects.get(pk=337141)

    @mock.patch('mkt.webapps.tasks._update_developer_name')
    def test_ignore_not_webapp(self, mock_):
        self.app.update(type=amo.ADDON_EXTENSION)
        call_command('process_addons', task='update_developer_name')
        assert not mock_.called

    @mock.patch('mkt.webapps.tasks._update_developer_name')
    def test_pending(self, mock_):
        self.app.update(status=amo.STATUS_PENDING)
        call_command('process_addons', task='update_developer_name')
        assert mock_.called

    @mock.patch('mkt.webapps.tasks._update_developer_name')
    def test_public_waiting(self, mock_):
        self.app.update(status=amo.STATUS_PUBLIC_WAITING)
        call_command('process_addons', task='update_developer_name')
        assert mock_.called

    @mock.patch('mkt.webapps.tasks._update_developer_name')
    def test_ignore_disabled(self, mock_):
        self.app.update(status=amo.STATUS_DISABLED)
        call_command('process_addons', task='update_developer_name')
        assert not mock_.called

    @mock.patch('files.utils.WebAppParser.parse')
    def test_ignore_no_current_version(self, mock_parser):
        self.app.current_version.all_files[0].update(
            status=amo.STATUS_DISABLED)
        self.app.update_version()
        update_developer_name(ids=(self.app.pk, ))
        assert not mock_parser.called

    @mock.patch('files.utils.WebAppParser.parse')
    def test_ignore_if_existing_developer_name(self, mock_parser):
        version = self.app.current_version
        version.update(_developer_name=u"Mï")
        update_developer_name(ids=(self.app.pk, ))
        assert not mock_parser.called

    @mock.patch('files.utils.WebAppParser.parse')
    def test_update_developer_name(self, mock_parser):
        mock_parser.return_value = {'developer_name': u'New Dêv'}
        self.app.current_version.update(_developer_name='')
        update_developer_name(ids=(self.app.pk, ))
        version = self.app.current_version.reload()
        eq_(version._developer_name, u'New Dêv')
        eq_(version.developer_name, u'New Dêv')

    @mock.patch('files.utils.WebAppParser.parse')
    @mock.patch('mkt.webapps.tasks._log')
    def test_update_developer_name_validation_error(self, _log, mock_parser):
        mock_parser.side_effect = ValidationError('dummy validation error')
        self.app.current_version.update(_developer_name='')
        update_developer_name(ids=(self.app.pk, ))
        assert _log.called_with(337141, u'Webapp manifest can not be parsed')

        version = self.app.current_version.reload()
        eq_(version._developer_name, '')
Example #5
0
class TestTransactionSummary(TestCase):
    fixtures = fixture('user_support_staff', 'user_999')

    def setUp(self):
        self.uuid = 'some:uuid'
        self.transaction_id = 'some:tr'
        self.seller_uuid = 456
        self.related_tx_uuid = 789
        self.user = UserProfile.objects.get(pk=999)

        self.app = addon_factory(type=amo.ADDON_WEBAPP)
        self.contrib = Contribution.objects.create(
            addon=self.app,
            uuid=self.uuid,
            user=self.user,
            transaction_id=self.transaction_id)

        self.url = reverse('lookup.transaction_summary', args=[self.uuid])
        self.client.login(username='******',
                          password='******')

    def create_test_refund(self):
        refund_contrib = Contribution.objects.create(
            addon=self.app,
            related=self.contrib,
            type=amo.CONTRIB_REFUND,
            transaction_id='testtransactionid')
        refund_contrib.enqueue_refund(amo.REFUND_PENDING, self.user)

    def test_transaction_summary(self):
        data = _transaction_summary(self.uuid)

        eq_(data['is_refundable'], False)
        eq_(data['contrib'].pk, self.contrib.pk)

    @mock.patch('mkt.lookup.views.client')
    def test_refund_status(self, solitude):
        solitude.api.bango.refund.status.get.return_value = {'status': PENDING}

        self.create_test_refund()
        data = _transaction_summary(self.uuid)

        eq_(data['refund_status'], REFUND_STATUSES[PENDING])

    @mock.patch('mkt.lookup.views.client')
    def test_is_refundable(self, solitude):
        solitude.api.bango.refund.status.get.return_value = {'status': PENDING}

        self.contrib.update(type=amo.CONTRIB_PURCHASE)
        data = _transaction_summary(self.uuid)
        eq_(data['contrib'].pk, self.contrib.pk)
        eq_(data['is_refundable'], True)

        self.create_test_refund()
        data = _transaction_summary(self.uuid)
        eq_(data['is_refundable'], False)

    @mock.patch('mkt.lookup.views.client')
    def test_200(self, solitude):
        r = self.client.get(self.url)
        eq_(r.status_code, 200)

    def test_no_perm_403(self):
        self.client.login(username='******', password='******')
        r = self.client.get(self.url)
        eq_(r.status_code, 403)

    def test_no_transaction_404(self):
        r = self.client.get(reverse('lookup.transaction_summary', args=[999]))
        eq_(r.status_code, 404)
Example #6
0
class TestDiffViewer(FilesBase, mkt.site.tests.WebappTestCase):
    fixtures = fixture('group_editor', 'user_editor', 'user_editor_group',
                       'user_999', 'webapp_337141')

    def setUp(self):
        super(TestDiffViewer, self).setUp()
        self.file_viewer = DiffHelper(self.files[0], self.files[1])

    def poll_url(self):
        return reverse('mkt.files.compare.poll',
                       args=[self.files[0].pk, self.files[1].pk])

    def add_file(self, file_obj, name, contents):
        dest = os.path.join(file_obj.dest, name)
        with storage.open(dest, 'w') as f:
            f.write(contents)

    def file_url(self, file=None):
        args = [self.files[0].pk, self.files[1].pk]
        if file:
            args.extend(['file', file])
        return reverse('mkt.files.compare', args=args)

    def check_urls(self, status):
        for url in [self.poll_url(), self.file_url()]:
            status_code = self.client.get(url).status_code
            assert status_code == status, (
                'Request to %s returned status code %d (expected %d)' %
                (url, status_code, status))

    def test_tree_no_file(self):
        self.file_viewer.extract()
        res = self.client.get(self.file_url('doesnotexist.js'))
        eq_(res.status_code, 404)

    def test_content_file(self):
        self.file_viewer.extract()
        res = self.client.get(self.file_url(not_binary))
        doc = pq(res.content)
        eq_(len(doc('pre')), 3)

    def test_binary_serve_links(self):
        self.file_viewer.extract()
        res = self.client.get(self.file_url(binary))
        doc = pq(res.content)
        node = doc('#content-wrapper a')
        eq_(len(node), 2)
        assert node[0].text.startswith('Download 256.png')

    def test_view_both_present(self):
        self.file_viewer.extract()
        res = self.client.get(self.file_url(not_binary))
        doc = pq(res.content)
        eq_(len(doc('pre')), 3)
        eq_(len(doc('#content-wrapper p')), 4)

    def test_view_one_missing(self):
        self.file_viewer.extract()
        storage.delete(os.path.join(self.file_viewer.right.dest, 'script.js'))
        res = self.client.get(self.file_url(not_binary))
        doc = pq(res.content)
        eq_(len(doc('pre')), 3)
        eq_(len(doc('#content-wrapper p')), 2)

    def test_view_left_binary(self):
        self.file_viewer.extract()
        filename = os.path.join(self.file_viewer.left.dest, 'script.js')
        with storage.open(filename, 'w') as f:
            f.write('MZ')
        res = self.client.get(self.file_url(not_binary))
        assert 'This file is not viewable online' in res.content

    def test_view_right_binary(self):
        self.file_viewer.extract()
        filename = os.path.join(self.file_viewer.right.dest, 'script.js')
        with storage.open(filename, 'w') as f:
            f.write('MZ')
        assert not self.file_viewer.is_diffable()
        res = self.client.get(self.file_url(not_binary))
        assert 'This file is not viewable online' in res.content

    def test_different_tree(self):
        self.file_viewer.extract()
        storage.delete(os.path.join(self.file_viewer.left.dest, not_binary))
        res = self.client.get(self.file_url(not_binary))
        doc = pq(res.content)
        eq_(doc('h4:last').text(), 'Deleted files:')
        eq_(len(doc('ul.root')), 2)

    def test_file_chooser_selection(self):
        res = self.client.get(self.file_url())
        doc = pq(res.content)

        eq_(
            doc('#id_left option[selected]').attr('value'),
            str(self.files[0].id))
        eq_(
            doc('#id_right option[selected]').attr('value'),
            str(self.files[1].id))
Example #7
0
class TestAccount(BaseOAuth):
    fixtures = fixture('user_2519', 'user_10482', 'webapp_337141')

    def setUp(self):
        super(TestAccount, self).setUp(api_name='account')
        self.list_url = list_url('settings')
        self.get_url = get_url('settings', '2519')
        self.user = UserProfile.objects.get(pk=2519)

    def test_verbs(self):
        self._allowed_verbs(self.list_url, ())
        self._allowed_verbs(self.get_url, ('get', 'patch', 'put'))

    def test_not_allowed(self):
        eq_(self.anon.get(self.get_url).status_code, 401)

    def test_allowed(self):
        res = self.client.get(self.get_url)
        eq_(res.status_code, 200, res.content)
        data = json.loads(res.content)
        eq_(data['display_name'], self.user.display_name)

    def test_other(self):
        eq_(self.client.get(get_url('settings', '10482')).status_code, 403)

    def test_own(self):
        res = self.client.get(get_url('settings', 'mine'))
        eq_(res.status_code, 200)
        data = json.loads(res.content)
        eq_(data['display_name'], self.user.display_name)

    def test_patch(self):
        res = self.client.patch(self.get_url,
                                data=json.dumps({'display_name': 'foo'}))
        eq_(res.status_code, 202)
        user = UserProfile.objects.get(pk=self.user.pk)
        eq_(user.display_name, 'foo')

    def test_put(self):
        res = self.client.put(self.get_url,
                              data=json.dumps({'display_name': 'foo'}))
        eq_(res.status_code, 204)
        user = UserProfile.objects.get(pk=self.user.pk)
        eq_(user.display_name, 'foo')
        eq_(user.username, self.user.username)  # Did not change.

    def test_patch_extra_fields(self):
        res = self.client.patch(self.get_url,
                                data=json.dumps({
                                    'display_name': 'foo',
                                    'username': '******'
                                }))
        eq_(res.status_code, 202)
        user = UserProfile.objects.get(pk=self.user.pk)
        eq_(user.display_name, 'foo')  # Got changed successfully.
        eq_(user.username, self.user.username)  # Did not change.

    def test_patch_other(self):
        res = self.client.patch(get_url('settings', '10482'),
                                data=json.dumps({'display_name': 'foo'}))
        eq_(res.status_code, 403)
Example #8
0
class TestThreadList(RestOAuth, CommTestMixin):
    fixtures = fixture('webapp_337141', 'user_2519')

    def setUp(self):
        super(TestThreadList, self).setUp()
        self.create_switch('comm-dashboard')
        self.addon = Webapp.objects.get(pk=337141)
        self.list_url = reverse('comm-thread-list')

    def test_response(self):
        """Test the list response, we don't want public threads in the list."""
        self._thread_factory(note=True, perms=['public'])

        res = self.client.get(self.list_url)
        eq_(res.status_code, 200)
        eq_(len(res.json['objects']), 1)

    def test_addon_filter(self):
        self._thread_factory(note=True)

        self.grant_permission(self.user.get_profile(), 'Apps:Review')
        res = self.client.get(self.list_url, {'app': '337141'})
        eq_(res.status_code, 200)
        eq_(len(res.json['objects']), 1)

        # This add-on doesn't exist.
        res = self.client.get(self.list_url, {'app': '1000'})
        eq_(res.status_code, 404)

    def test_app_slug(self):
        thread = CommunicationThread.objects.create(addon=self.addon)
        CommunicationNote.objects.create(author=self.profile,
                                         thread=thread,
                                         note_type=0,
                                         body='something')

        self.grant_permission(self.user.get_profile(), 'Apps:Review')
        res = self.client.get(self.list_url, {'app': self.addon.app_slug})
        eq_(res.status_code, 200)
        eq_(res.json['objects'][0]['addon_meta']['app_slug'],
            self.addon.app_slug)

    def test_app_threads(self):
        version1 = version_factory(addon=self.addon, version='7.12')
        thread1 = CommunicationThread.objects.create(
            addon=self.addon, version=version1, read_permission_public=True)
        CommunicationThreadCC.objects.create(user=self.profile, thread=thread1)

        version2 = version_factory(addon=self.addon, version='1.16')
        thread2 = CommunicationThread.objects.create(
            addon=self.addon, version=version2, read_permission_public=True)
        CommunicationThreadCC.objects.create(user=self.profile, thread=thread2)

        self.grant_permission(self.user.get_profile(), 'Apps:Review')
        res = self.client.get(self.list_url, {'app': self.addon.app_slug})
        eq_(res.status_code, 200)
        eq_(res.json['app_threads'], [{
            'id': thread2.id,
            'version__version': version2.version
        }, {
            'id': thread1.id,
            'version__version': version1.version
        }])

    def test_create(self):
        self.create_switch('comm-dashboard')
        version_factory(addon=self.addon, version='1.1')
        data = {
            'app': self.addon.app_slug,
            'version': '1.1',
            'note_type': '0',
            'body': 'flylikebee'
        }
        self.addon.addonuser_set.create(user=self.user.get_profile())
        res = self.client.post(self.list_url, data=json.dumps(data))
        eq_(res.status_code, 201)
        assert self.addon.threads.count()

    @mock.patch('waffle.switch_is_active')
    def test_create_no_switch(self, waffle_mock):
        waffle_mock.return_value = False
        version_factory(addon=self.addon, version='1.1')
        data = {
            'app': self.addon.app_slug,
            'version': '1.1',
            'note_type': '0',
            'body': 'flylikebee'
        }
        self.addon.addonuser_set.create(user=self.user.get_profile())
        res = self.client.post(self.list_url, data=json.dumps(data))
        eq_(res.status_code, 403)
Example #9
0
class TestThreadDetail(RestOAuth, CommTestMixin):
    fixtures = fixture('webapp_337141', 'user_2519', 'user_support_staff')

    def setUp(self):
        super(TestThreadDetail, self).setUp()
        self.addon = Webapp.objects.get(pk=337141)

    def check_permissions(self, thread):
        req = req_factory_factory(reverse('comm-thread-detail',
                                          kwargs={'pk': thread.pk}),
                                  user=self.profile)

        return ThreadPermission().has_object_permission(
            req, 'comm-thread-detail', thread)

    def test_response(self):
        thread = self._thread_factory(note=True)

        res = self.client.get(
            reverse('comm-thread-detail', kwargs={'pk': thread.pk}))
        eq_(res.status_code, 200)
        eq_(len(res.json['recent_notes']), 1)
        eq_(res.json['addon'], self.addon.id)

    def test_recent_notes_perm(self):
        staff = UserProfile.objects.get(username='******')
        self.addon.addonuser_set.create(user=self.profile)
        thread = self._thread_factory(read_permission_developer=True)
        self._note_factory(thread,
                           perms=['developer'],
                           author=staff,
                           body='allowed')
        no_dev_note = self._note_factory(thread,
                                         no_perms=['developer'],
                                         author=staff)

        # Test that the developer can't access no-developer note.
        res = self.client.get(
            reverse('comm-thread-detail', kwargs={'pk': thread.pk}))
        eq_(res.status_code, 200)
        eq_(len(res.json['recent_notes']), 1)
        eq_(res.json['recent_notes'][0]['body'], 'allowed')
        eq_(res.json['addon'], self.addon.id)

        # Test that the author always has permissions.
        no_dev_note.update(author=self.profile)
        res = self.client.get(
            reverse('comm-thread-detail', kwargs={'pk': thread.pk}))
        eq_(len(res.json['recent_notes']), 2)

    def test_cc(self):
        # Test with no CC.
        thread = self._thread_factory()
        assert not self.check_permissions(thread)

        # Test with CC created.
        thread.thread_cc.create(user=self.profile)
        assert self.check_permissions(thread)

    def test_addon_dev_allowed(self):
        thread = self._thread_factory(perms=['developer'])
        self.addon.addonuser_set.create(user=self.profile)
        assert self.check_permissions(thread)

    def test_addon_dev_denied(self):
        """Test when the user is a developer of a different add-on."""
        thread = self._thread_factory(perms=['developer'])
        self.profile.addonuser_set.create(addon=addon_factory())
        assert not self.check_permissions(thread)

    def test_read_public(self):
        thread = self._thread_factory(perms=['public'])
        assert self.check_permissions(thread)

    def test_read_moz_contact(self):
        thread = self._thread_factory(perms=['mozilla_contact'])
        self.addon.update(mozilla_contact=self.profile.email)
        assert self.check_permissions(thread)

    def test_read_reviewer(self):
        thread = self._thread_factory(perms=['reviewer'])
        self.grant_permission(self.profile, 'Apps:Review')
        assert self.check_permissions(thread)

    def test_read_senior_reviewer(self):
        thread = self._thread_factory(perms=['senior_reviewer'])
        self.grant_permission(self.profile, 'Apps:ReviewEscalated')
        assert self.check_permissions(thread)

    def test_read_staff(self):
        thread = self._thread_factory(perms=['staff'])
        self.grant_permission(self.profile, 'Admin:%')
        assert self.check_permissions(thread)

    def test_cors_allowed(self):
        thread = self._thread_factory()

        res = self.client.get(
            reverse('comm-thread-detail', kwargs={'pk': thread.pk}))
        self.assertCORS(res, 'get', 'post', 'patch')

    def test_mark_read(self):
        thread = self._thread_factory()
        note1 = self._note_factory(thread)
        note2 = self._note_factory(thread)

        res = self.client.patch(reverse('comm-thread-detail',
                                        kwargs={'pk': thread.pk}),
                                data=json.dumps({'is_read': True}))
        eq_(res.status_code, 204)
        assert note1.read_by_users.filter(user=self.profile).exists()
        assert note2.read_by_users.filter(user=self.profile).exists()

    def test_review_url(self):
        thread = self._thread_factory(note=True)

        res = self.client.get(
            reverse('comm-thread-detail', kwargs={'pk': thread.pk}))
        eq_(res.status_code, 200)
        eq_(res.json['addon_meta']['review_url'],
            reverse('reviewers.apps.review', args=[self.addon.app_slug]))

    def test_version_number(self):
        version = version_factory(addon=self.addon, version='7.12')
        thread = CommunicationThread.objects.create(
            addon=self.addon, version=version, read_permission_public=True)

        res = self.client.get(reverse('comm-thread-detail', args=[thread.pk]))
        eq_(json.loads(res.content)['version_number'], '7.12')
        eq_(json.loads(res.content)['version_is_obsolete'], False)

        version.delete()
        res = self.client.get(reverse('comm-thread-detail', args=[thread.pk]))
        eq_(json.loads(res.content)['version_number'], '7.12')
        eq_(json.loads(res.content)['version_is_obsolete'], True)

    def test_app_threads(self):
        version1 = version_factory(addon=self.addon, version='7.12')
        thread1 = CommunicationThread.objects.create(
            addon=self.addon, version=version1, read_permission_public=True)

        version2 = version_factory(addon=self.addon, version='1.16')
        thread2 = CommunicationThread.objects.create(
            addon=self.addon, version=version2, read_permission_public=True)

        for thread in (thread1, thread2):
            res = self.client.get(
                reverse('comm-thread-detail', args=[thread.pk]))
            eq_(res.status_code, 200)
            eq_(
                json.loads(res.content)['app_threads'],
                [{
                    "id": thread2.id,
                    "version__version": version2.version
                }, {
                    "id": thread1.id,
                    "version__version": version1.version
                }])
class TestUpsell(RestOAuth, UpsellCase):
    fixtures = fixture('webapp_337141', 'user_2519')

    def setUp(self):
        super(TestUpsell, self).setUp()
        UpsellCase.setUp(self)

    def test_create(self):
        eq_(self.client.post(self.upsell_list, data={}).status_code, 400)

    def test_missing(self):
        res = self.client.post(self.upsell_list,
                               data=json.dumps({'free': self.free_url}))
        eq_(res.status_code, 400)
        eq_(res.json['premium'], [u'This field is required.'])

    def test_not_allowed(self):
        res = self.client.post(self.upsell_list,
                               data=json.dumps({
                                   'free': self.free_url,
                                   'premium': self.premium_url
                               }))
        eq_(res.status_code, 403)

    def test_allowed(self):
        self.create_allowed()
        res = self.client.post(self.upsell_list,
                               data=json.dumps({
                                   'free': self.free_url,
                                   'premium': self.premium_url
                               }))
        eq_(res.status_code, 201)

    def test_delete_not_allowed(self):
        self.create_upsell()
        eq_(self.client.delete(self.upsell_url).status_code, 403)

    def test_delete_allowed(self):
        self.create_upsell()
        self.create_allowed()
        eq_(self.client.delete(self.upsell_url).status_code, 204)

    def test_wrong_way_around(self):
        res = self.client.post(self.upsell_list,
                               data=json.dumps({
                                   'free': self.premium_url,
                                   'premium': self.free_url
                               }))
        eq_(res.status_code, 400)

    def test_patch_new_not_allowed(self):
        # Trying to patch to a new object you do not have access to.
        self.create_upsell()
        self.create_allowed()
        another = app_factory(premium_type=amo.ADDON_PREMIUM)
        res = self.client.patch(self.upsell_url,
                                data=json.dumps({
                                    'free': self.free_url,
                                    'premium': self.url(another)
                                }))
        eq_(res.status_code, 403)

    def test_patch_old_not_allowed(self):
        # Trying to patch an old object you do not have access to.
        self.create_upsell()
        AddonUser.objects.create(addon=self.free, user=self.profile)
        # We did not give you access to patch away from self.premium.
        another = app_factory(premium_type=amo.ADDON_PREMIUM)
        AddonUser.objects.create(addon=another, user=self.profile)
        res = self.client.patch(self.upsell_url,
                                data=json.dumps({
                                    'free': self.free_url,
                                    'premium': self.url(another)
                                }))
        eq_(res.status_code, 403)

    def test_patch(self):
        self.create_upsell()
        self.create_allowed()
        another = app_factory(premium_type=amo.ADDON_PREMIUM)
        AddonUser.objects.create(addon=another, user=self.profile)
        res = self.client.patch(self.upsell_url,
                                data=json.dumps({
                                    'free': self.free_url,
                                    'premium': self.url(another)
                                }))
        eq_(res.status_code, 200)
Example #11
0
class TestVersion(BaseUploadTest, mkt.site.tests.TestCase):
    fixtures = fixture('webapp_337141')

    def setUp(self):
        self.version = Version.objects.latest('id')

    def test_developer_name(self):
        version = Version.objects.latest('id')
        version._developer_name = u'M€lâ'
        eq_(version.developer_name, u'M€lâ')
        eq_(Version(_developer_name=u'M€lâ').developer_name, u'M€lâ')

    @mock.patch('mkt.files.utils.parse_addon')
    def test_developer_name_from_upload(self, parse_addon):
        parse_addon.return_value = {
            'version': '42.0',
            'developer_name': u'Mýself'
        }
        addon = Webapp.objects.get(pk=337141)
        # Note: we need a valid FileUpload instance, but in the end we are not
        # using its contents since we are mocking parse_addon().
        path = os.path.join(settings.ROOT, 'mkt', 'developers', 'tests',
                            'addons', 'mozball.webapp')
        upload = self.get_upload(abspath=path)
        version = Version.from_upload(upload, addon)
        eq_(version.version, '42.0')
        eq_(version.developer_name, u'Mýself')

    @mock.patch('mkt.files.utils.parse_addon')
    def test_long_developer_name_from_upload(self, parse_addon):
        truncated_developer_name = u'ý' * 255
        long_developer_name = truncated_developer_name + u'àààà'
        parse_addon.return_value = {
            'version': '42.1',
            'developer_name': long_developer_name
        }
        addon = Webapp.objects.get(pk=337141)
        # Note: we need a valid FileUpload instance, but in the end we are not
        # using its contents since we are mocking parse_addon().
        path = os.path.join(settings.ROOT, 'mkt', 'developers', 'tests',
                            'addons', 'mozball.webapp')
        upload = self.get_upload(abspath=path)
        version = Version.from_upload(upload, addon)
        eq_(version.version, '42.1')
        eq_(version.developer_name, truncated_developer_name)

    def test_is_privileged_hosted_app(self):
        addon = Webapp.objects.get(pk=337141)
        eq_(addon.current_version.is_privileged, False)

    @mock.patch('mkt.webapps.models.Webapp.get_manifest_json')
    def test_is_privileged_app(self, get_manifest_json):
        get_manifest_json.return_value = {
            'type': 'privileged'
        }
        addon = Webapp.objects.get(pk=337141)
        addon.update(is_packaged=True)
        eq_(addon.current_version.is_privileged, True)

    @mock.patch('mkt.webapps.models.Webapp.get_manifest_json')
    def test_is_privileged_non_privileged_app(self, get_manifest_json):
        get_manifest_json.return_value = {
        }
        addon = Webapp.objects.get(pk=337141)
        addon.update(is_packaged=True)
        eq_(addon.current_version.is_privileged, False)

    def test_delete(self):
        version = Version.objects.all()[0]
        eq_(Version.objects.count(), 1)

        version.delete()

        eq_(Version.objects.count(), 0)
        eq_(Version.with_deleted.count(), 1)

        # Ensure deleted version's files get disabled.
        eq_(version.all_files[0].status, mkt.STATUS_DISABLED)

    def test_has_files(self):
        assert self.version.has_files, 'Version with files not recognized.'

        self.version.files.all().delete()
        self.version = Version.objects.latest('id')
        assert not self.version.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

    @mock.patch('mkt.versions.models.public_storage')
    def test_version_delete(self, storage_mock):
        self.version.delete()
        addon = Webapp.objects.get(pk=337141)
        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('mkt.versions.models.public_storage')
    def test_packaged_version_delete(self, storage_mock):
        addon = Webapp.objects.get(pk=337141)
        addon.update(is_packaged=True)
        version = addon.current_version
        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):
        eq_(self.version.files.all()[0].status, mkt.STATUS_PUBLIC)
        self.version.delete()
        eq_(self.version.files.all()[0].status, mkt.STATUS_DISABLED)

    @mock.patch('mkt.files.models.File.hide_disabled_file')
    def test_new_version_disable_old_unreviewed(self, hide_mock):
        addon = Webapp.objects.get(pk=337141)
        # The status doesn't change for public files.
        qs = File.objects.filter(version=addon.current_version)
        eq_(qs.all()[0].status, mkt.STATUS_PUBLIC)
        Version.objects.create(addon=addon)
        eq_(qs.all()[0].status, mkt.STATUS_PUBLIC)
        assert not hide_mock.called

        qs.update(status=mkt.STATUS_PENDING)
        version = Version.objects.create(addon=addon)
        version.disable_old_files()
        eq_(qs.all()[0].status, mkt.STATUS_DISABLED)
        assert hide_mock.called

    def _reset_version(self, version):
        version.all_files[0].status = mkt.STATUS_PUBLIC
        version.deleted = False

    def test_version_is_public(self):
        addon = Webapp.objects.get(id=337141)
        version = version_factory(addon=addon)

        # Base test. Everything is in order, the version should be public.
        eq_(version.is_public(), True)

        # Non-public file.
        self._reset_version(version)
        version.all_files[0].status = mkt.STATUS_DISABLED
        eq_(version.is_public(), False)

        # Deleted version.
        self._reset_version(version)
        version.deleted = True
        eq_(version.is_public(), False)

        # Non-public addon.
        self._reset_version(version)
        with mock.patch('mkt.webapps.models.Webapp.is_public') \
                as is_addon_public:
            is_addon_public.return_value = False
            eq_(version.is_public(), False)

    def test_app_feature_creation_app(self):
        app = Webapp.objects.create()
        ver = Version.objects.create(addon=app)
        assert ver.features, 'AppFeatures was not created with version.'
class TestPaymentAccount(AccountCase, RestOAuth):
    fixtures = fixture('webapp_337141', 'user_999', 'user_2519')

    def test_anonymous(self):
        r = self.anon.get(self.payment_url)
        eq_(r.status_code, 403)

        r = self.anon.get(self.payment_list)
        eq_(r.status_code, 403)

    def test_get_payments_account_list(self):
        self.other()
        res = self.client.get(self.payment_list)
        data = json.loads(res.content)
        eq_(data['meta']['total_count'], 1)
        eq_(data['objects'][0]['account_name'], 'mine')
        eq_(data['objects'][0]['resource_uri'], self.payment_url)

    def test_get_payments_account(self):
        res = self.client.get(self.payment_url)
        eq_(res.status_code, 200, res.content)
        data = json.loads(res.content)
        eq_(data['account_name'], 'mine')
        eq_(data['resource_uri'], self.payment_url)

    def test_get_other_payments_account(self):
        self.other()
        res = self.client.get(self.other_url)
        eq_(res.status_code, 404, res.content)

    def test_create(self):
        res = self.client.post(self.payment_list,
                               data=json.dumps(payment_data))
        data = json.loads(res.content)
        eq_(data['account_name'], 'new')
        new_account = PaymentAccount.objects.get(name='new')
        ok_(new_account.pk != self.account.pk)
        eq_(new_account.user, self.user)
        data = self.bango_patcher.package.post.call_args[1]['data']
        expected = package_data.copy()
        expected.pop('account_name')
        expected.pop('provider')
        for key in expected.keys():
            eq_(package_data[key], data[key])

    def test_update_payments_account(self):
        res = self.client.put(self.payment_url, data=json.dumps(payment_data))
        eq_(res.status_code, 204, res.content)
        self.account.reload()
        eq_(self.account.name, 'new')
        data = self.bango_patcher.api.by_url().patch.call_args[1]['data']
        expected = package_data.copy()
        expected.pop('account_name')
        expected.pop('provider')
        for key in expected.keys():
            eq_(package_data[key], data[key])

    def test_update_other_payments_account(self):
        self.other()
        res = self.client.put(self.other_url, data=json.dumps(payment_data))
        eq_(res.status_code, 404, res.content)
        self.other_account.reload()
        eq_(self.other_account.name, 'other')  # not "new".

    def test_delete_payments_account(self):
        self.create_user()
        self.create()
        eq_(self.account.inactive, False)
        res = self.client.delete(self.payment_url)
        eq_(res.status_code, 204, res.content)
        self.account.reload()
        eq_(self.account.inactive, True)

    def test_delete_shared(self):
        self.create_user()
        self.create()
        self.account.update(shared=True)
        eq_(self.account.inactive, False)
        res = self.client.delete(self.payment_url)
        eq_(res.status_code, 409)

    def test_delete_others_payments_account(self):
        self.create_user()
        self.create()
        self.other()
        eq_(self.other_account.inactive, False)
        res = self.client.delete(self.other_url)
        eq_(res.status_code, 404, res.content)
        self.other_account.reload()
        eq_(self.other_account.inactive, False)

    def test_create_fail(self):
        err = {'broken': True}
        self.bango_patcher.package.post.side_effect = HttpClientError(
            content=err)
        res = self.client.post(self.payment_list,
                               data=json.dumps(payment_data))
        eq_(res.status_code, 500)
        eq_(json.loads(res.content), err)

    def test_create_fail2(self):
        self.bango_patcher.package.post.side_effect = HttpServerError()
        res = self.client.post(self.payment_list,
                               data=json.dumps(payment_data))
        eq_(res.status_code, 500)
Example #13
0
class TestPreviewHandler(RestOAuth, MktPaths):
    fixtures = fixture('user_2519', 'webapp_337141')

    def setUp(self):
        super(TestPreviewHandler, self).setUp()
        self.app = Webapp.objects.get(pk=337141)
        self.user = UserProfile.objects.get(pk=2519)
        AddonUser.objects.create(user=self.user, addon=self.app)
        self.file = base64.b64encode(
            open(get_image_path('preview.jpg'), 'r').read())
        self.list_url = reverse('app-preview', kwargs={'pk': self.app.pk})
        self.good = {
            'file': {
                'data': self.file,
                'type': 'image/jpg'
            },
            'position': 1
        }

    def get_error(self, response):
        return json.loads(response.content)

    def test_has_cors(self):
        self.assertCORS(self.client.post(self.list_url), 'post', 'delete',
                        'get')

    def test_post_preview(self):
        res = self.client.post(self.list_url, data=json.dumps(self.good))
        eq_(res.status_code, 201)
        previews = self.app.previews
        eq_(previews.count(), 1)
        eq_(previews.all()[0].position, 1)

    def test_wrong_url(self):
        self.list_url = reverse('app-preview', kwargs={'pk': 'booyah'})
        res = self.client.post(self.list_url, data=json.dumps(self.good))
        eq_(res.status_code, 404)
        data = json.loads(res.content)
        eq_(data['detail'], 'Not found.')

    def test_not_mine(self):
        self.app.authors.clear()
        res = self.client.post(self.list_url, data=json.dumps(self.good))
        eq_(res.status_code, 403)

    def test_position_missing(self):
        data = {'file': {'data': self.file, 'type': 'image/jpg'}}
        res = self.client.post(self.list_url, data=json.dumps(data))
        eq_(res.status_code, 400)
        eq_(self.get_error(res)['position'], ['This field is required.'])

    def test_preview_missing(self):
        res = self.client.post(self.list_url, data=json.dumps({}))
        eq_(res.status_code, 400)
        eq_(self.get_error(res)['position'], ['This field is required.'])

    def create(self):
        self.client.post(self.list_url, data=json.dumps(self.good))
        self.preview = self.app.previews.all()[0]
        self.get_url = reverse('app-preview-detail',
                               kwargs={'pk': self.preview.pk})

    def test_delete(self):
        self.create()
        res = self.client.delete(self.get_url)
        eq_(res.status_code, 204)
        eq_(self.app.previews.count(), 0)

    def test_delete_not_mine(self):
        self.create()
        self.app.authors.clear()
        res = self.client.delete(self.get_url)
        eq_(res.status_code, 403)

    def test_delete_not_there(self):
        self.get_url = reverse('app-preview-detail', kwargs={'pk': 123123123})
        res = self.client.delete(self.get_url)
        eq_(res.status_code, 404)

    def test_get(self):
        self.create()
        res = self.client.get(self.get_url)
        eq_(res.status_code, 200)

    def test_get_not_mine(self):
        self.create()
        self.app.authors.clear()
        res = self.client.get(self.get_url)
        eq_(res.status_code, 403)

    def test_get_not_there(self):
        self.get_url = reverse('app-preview-detail', kwargs={'pk': 123123123})
        res = self.client.get(self.get_url)
        eq_(res.status_code, 404)
Example #14
0
class TestAppStatusHandler(RestOAuth, MktPaths):
    fixtures = fixture('user_2519', 'webapp_337141')

    def setUp(self):
        super(TestAppStatusHandler, self).setUp()
        self.app = Webapp.objects.get(pk=337141)
        AddonUser.objects.create(addon=self.app, user=self.user)
        self.get_url = reverse('app-status-detail', kwargs={'pk': self.app.pk})

    def get(self, expected_status=200):
        res = self.client.get(self.get_url)
        eq_(res.status_code, expected_status)
        data = json.loads(res.content)
        return res, data

    def test_verbs(self):
        self._allowed_verbs(self.get_url, ['get', 'patch'])

    def test_has_no_cors(self):
        res = self.client.get(self.get_url)
        assert 'access-control-allow-origin' not in res

    def test_status(self):
        res, data = self.get()
        eq_(self.app.status, mkt.STATUS_PUBLIC)
        eq_(data['status'], 'public')
        eq_(data['disabled_by_user'], False)

        self.app.update(status=mkt.STATUS_UNLISTED)
        res, data = self.get()
        eq_(data['status'], 'unlisted')
        eq_(data['disabled_by_user'], False)

        self.app.update(status=mkt.STATUS_NULL)
        res, data = self.get()
        eq_(data['status'], 'incomplete')
        eq_(data['disabled_by_user'], False)

        self.app.update(status=mkt.STATUS_PENDING)
        res, data = self.get()
        eq_(data['status'], 'pending')
        eq_(data['disabled_by_user'], False)

        self.app.update(disabled_by_user=True)
        res, data = self.get()
        eq_(data['status'], 'pending')
        eq_(data['disabled_by_user'], True)

    def test_status_not_mine(self):
        AddonUser.objects.get(user=self.user).delete()
        res = self.client.get(self.get_url)
        eq_(res.status_code, 403)

    def test_disable(self):
        eq_(self.app.disabled_by_user, False)
        res = self.client.patch(self.get_url,
                                data=json.dumps({'disabled_by_user': True}))
        eq_(res.status_code, 200)
        self.app.reload()
        data = json.loads(res.content)
        eq_(data['status'], 'public')
        eq_(self.app.disabled_by_user, True)
        eq_(self.app.status, mkt.STATUS_PUBLIC)  # Unchanged, doesn't matter.

    def test_disable_not_mine(self):
        AddonUser.objects.get(user=self.user).delete()
        res = self.client.patch(self.get_url,
                                data=json.dumps({'disabled_by_user': True}))
        eq_(res.status_code, 403)

    def test_change_status_to_pending_fails(self):
        res = self.client.patch(self.get_url,
                                data=json.dumps({'status': 'pending'}))
        eq_(res.status_code, 400)
        data = json.loads(res.content)
        ok_('status' in data)

    def test_change_status_to_public_fails(self):
        self.app.update(status=mkt.STATUS_PENDING)
        res = self.client.patch(self.get_url,
                                data=json.dumps({'status': 'public'}))
        eq_(res.status_code, 400)
        data = json.loads(res.content)
        ok_('status' in data)
        eq_(self.app.reload().status, mkt.STATUS_PENDING)

    @patch('mkt.webapps.models.Webapp.is_fully_complete')
    def test_incomplete_app(self, is_fully_complete):
        is_fully_complete.return_value = False
        self.app.update(status=mkt.STATUS_NULL)
        res = self.client.patch(self.get_url,
                                data=json.dumps({'status': 'pending'}))
        eq_(res.status_code, 400)
        data = json.loads(res.content)
        self.assertSetEqual(data['status'], self.app.completion_error_msgs())

    @patch('mkt.webapps.models.Webapp.is_fully_complete')
    def test_status_changes(self, is_fully_complete):
        is_fully_complete.return_value = True

        # Statuses we want to check in (from, [to, ...]) tuples.
        status_changes = (
            (mkt.STATUS_NULL, [mkt.STATUS_PENDING]),
            (mkt.STATUS_APPROVED, [mkt.STATUS_UNLISTED, mkt.STATUS_PUBLIC]),
            (mkt.STATUS_UNLISTED, [mkt.STATUS_APPROVED, mkt.STATUS_PUBLIC]),
            (mkt.STATUS_PUBLIC, [mkt.STATUS_APPROVED, mkt.STATUS_UNLISTED]),
        )

        for orig, new in status_changes:
            for to in new:
                self.app.update(status=orig)
                to_api_status = mkt.STATUS_CHOICES_API[to]
                res = self.client.patch(self.get_url,
                                        data=json.dumps(
                                            {'status': to_api_status}))
                eq_(res.status_code, 200)
                data = json.loads(res.content)
                eq_(data['status'], to_api_status)
                eq_(self.app.reload().status, to)

    @patch('mkt.webapps.models.Webapp.is_fully_complete')
    def test_senior_reviewer_incomplete_to_pending(self, is_fully_complete):
        # The app is incomplete, but the user has Admin:%s so he can override
        # that.
        is_fully_complete.return_value = False
        self.grant_permission(self.user, 'Admin:%s')
        self.app.update(status=mkt.STATUS_NULL)
        res = self.client.patch(self.get_url,
                                data=json.dumps({'status': 'pending'}))
        eq_(res.status_code, 200)
        data = json.loads(res.content)
        eq_(data['status'], 'pending')
        eq_(self.app.reload().status, mkt.STATUS_PENDING)

    @patch('mkt.webapps.models.Webapp.is_fully_complete')
    def test_senior_reviewer_incomplete_to_public(self, is_fully_complete):
        # The app is incomplete, but the user has Admin:%s so he can override
        # that.
        is_fully_complete.return_value = False
        self.grant_permission(self.user, 'Admin:%s')
        self.app.update(status=mkt.STATUS_NULL)
        res = self.client.patch(self.get_url,
                                data=json.dumps({'status': 'public'}))
        eq_(res.status_code, 200)
        data = json.loads(res.content)
        eq_(data['status'], 'public')
        eq_(self.app.reload().status, mkt.STATUS_PUBLIC)
Example #15
0
class TestDetails(TestSubmit):
    fixtures = fixture('webapp_337141', 'user_999', 'user_10482')

    def setUp(self):
        self.gia_mock = mock.patch(
            'mkt.developers.tasks.generate_image_assets').__enter__()
        self.fi_mock = mock.patch(
            'mkt.developers.tasks.fetch_icon').__enter__()
        super(TestDetails, self).setUp()
        self.webapp = self.get_webapp()
        self.webapp.update(status=amo.STATUS_NULL)
        self.url = reverse('submit.app.details', args=[self.webapp.app_slug])

    def tearDown(self):
        self.gia_mock.__exit__()
        self.fi_mock.__exit__()

    def get_webapp(self):
        return Webapp.objects.get(id=337141)

    def upload_preview(self, image_file=None):
        if not image_file:
            image_file = get_image_path('preview.jpg')
        return self._upload_image(self.webapp.get_dev_url('upload_preview'),
                                  image_file=image_file)

    def upload_icon(self, image_file=None):
        if not image_file:
            image_file = get_image_path('mozilla-sq.png')
        return self._upload_image(self.webapp.get_dev_url('upload_icon'),
                                  image_file=image_file)

    def _upload_image(self, url, image_file):
        with open(image_file, 'rb') as data:
            rp = self.client.post(url, {'upload_image': data})
        eq_(rp.status_code, 200)
        hash_ = json.loads(rp.content)['upload_hash']
        assert hash_, 'No hash: %s' % rp.content
        return hash_

    def _step(self):
        self.user.update(read_dev_agreement=datetime.datetime.now())
        self.cl = AppSubmissionChecklist.objects.create(addon=self.webapp,
                                                        terms=True,
                                                        manifest=True)

        # Associate app with user.
        AddonUser.objects.create(addon=self.webapp, user=self.user)

        # Associate device type with app.
        self.dtype = DEVICE_TYPES.values()[0]
        AddonDeviceType.objects.create(addon=self.webapp,
                                       device_type=self.dtype.id)
        self.device_types = [self.dtype]

        # Associate category with app.
        self.cat1 = Category.objects.create(type=amo.ADDON_WEBAPP, name='Fun')
        AddonCategory.objects.create(addon=self.webapp, category=self.cat1)

    def test_anonymous(self):
        self._test_anonymous()

    def test_resume_later(self):
        self._step()
        self.webapp.appsubmissionchecklist.update(details=True)
        r = self.client.get(
            reverse('submit.app.resume', args=[self.webapp.app_slug]))
        self.assert3xx(r, self.webapp.get_dev_url('edit'))

    def test_not_owner(self):
        self._step()
        assert self.client.login(username='******',
                                 password='******')
        eq_(self.client.get(self.url).status_code, 403)

    def test_page(self):
        self._step()
        r = self.client.get(self.url)
        eq_(r.status_code, 200)
        eq_(pq(r.content)('#submit-details').length, 1)

    def test_progress_display(self):
        self._step()
        self._test_progress_display(['terms', 'manifest'], 'details')

    def new_preview_formset(self, *args, **kw):
        ctx = self.client.get(self.url).context

        blank = initial(ctx['form_previews'].forms[-1])
        blank.update(**kw)
        return blank

    def preview_formset(self, *args, **kw):
        kw.setdefault('initial_count', 0)
        kw.setdefault('prefix', 'files')

        fs = formset(*[a for a in args] + [self.new_preview_formset()], **kw)
        return dict([(k, '' if v is None else v) for k, v in fs.items()])

    def get_dict(self, **kw):
        data = {
            'app_slug': 'testname',
            'summary': 'Hello!',
            'description': 'desc',
            'privacy_policy': 'XXX <script>alert("xss")</script>',
            'homepage': 'http://www.goodreads.com/user/show/7595895-krupa',
            'support_url': 'http://www.goodreads.com/user_challenges/351558',
            'support_email': '*****@*****.**',
            'categories': [self.cat1.id],
            'flash': '1',
            'publish': '1'
        }
        # Add the required screenshot.
        data.update(
            self.preview_formset({
                'upload_hash': '<hash>',
                'position': 0
            }))
        data.update(**kw)
        # Remove fields without values.
        data = dict((k, v) for k, v in data.iteritems() if v is not None)
        return data

    def check_dict(self, data=None, expected=None):
        if data is None:
            data = self.get_dict()
        addon = self.get_webapp()

        # Build a dictionary of expected results.
        expected_data = {
            'app_slug': 'testname',
            'summary': 'Hello!',
            'description': 'desc',
            'privacy_policy': 'XXX &lt;script&gt;alert("xss")&lt;/script&gt;',
            'uses_flash': True,
            'make_public': amo.PUBLIC_IMMEDIATELY
        }
        if expected:
            expected_data.update(expected)

        for field, expected in expected_data.iteritems():
            got = unicode(getattr(addon, field))
            expected = unicode(expected)
            eq_(got, expected,
                'Expected %r for %r. Got %r.' % (expected, field, got))

        self.assertSetEqual(addon.device_types, self.device_types)

    @mock.patch('mkt.submit.views.record_action')
    def test_success(self, record_action):
        self._step()
        data = self.get_dict()
        r = self.client.post(self.url, data)
        self.assertNoFormErrors(r)
        self.check_dict(data=data)
        self.webapp = self.get_webapp()
        self.assert3xx(r, self.get_url('done'))

        eq_(self.webapp.status, amo.STATUS_PENDING)
        assert record_action.called

    def test_success_paid(self):
        self._step()

        self.webapp = self.get_webapp()
        self.webapp.update(premium_type=amo.ADDON_PREMIUM)

        data = self.get_dict()
        r = self.client.post(self.url, data)
        self.assertNoFormErrors(r)
        self.check_dict(data=data)
        self.webapp = self.get_webapp()
        self.assert3xx(r, self.get_url('done'))

        eq_(self.webapp.status, amo.STATUS_NULL)
        eq_(self.webapp.highest_status, amo.STATUS_PENDING)
        self.assertSetEqual(self.webapp.get_region_ids(),
                            mkt.regions.ALL_PAID_REGION_IDS)

    def test_success_prefill_device_types_if_empty(self):
        """
        The new submission flow asks for device types at step one.
        This ensures that existing incomplete apps still have device
        compatibility.
        """
        self._step()

        AddonDeviceType.objects.all().delete()
        self.device_types = amo.DEVICE_TYPES.values()

        data = self.get_dict()
        r = self.client.post(self.url, data)
        self.assertNoFormErrors(r)
        self.check_dict(data=data)
        self.webapp = self.get_webapp()
        self.assert3xx(r, self.get_url('done'))

    def test_success_for_public_waiting(self):
        self._step()

        data = self.get_dict()
        del data['publish']

        r = self.client.post(self.url, data)
        self.assertNoFormErrors(r)

        self.check_dict(data=data, expected={'make_public': amo.PUBLIC_WAIT})
        self.webapp = self.get_webapp()
        self.assert3xx(r, self.get_url('done'))

    def test_media_types(self):
        self._step()
        res = self.client.get(self.url)
        doc = pq(res.content)
        eq_(
            doc('.screenshot_upload').attr('data-allowed-types'),
            'image/jpeg|image/png|video/webm')
        eq_(
            doc('#id_icon_upload').attr('data-allowed-types'),
            'image/jpeg|image/png')

    def test_screenshot(self):
        self._step()
        im_hash = self.upload_preview()
        data = self.get_dict()
        data.update(
            self.preview_formset({
                'upload_hash': im_hash,
                'position': 0
            }))
        rp = self.client.post(self.url, data)
        eq_(rp.status_code, 302)
        ad = Addon.objects.get(pk=self.webapp.pk)
        eq_(ad.previews.all().count(), 1)

    def test_icon(self):
        self._step()
        im_hash = self.upload_icon()
        data = self.get_dict()
        data['icon_upload_hash'] = im_hash
        data['icon_type'] = 'image/png'
        rp = self.client.post(self.url, data)
        eq_(rp.status_code, 302)
        ad = self.get_webapp()
        eq_(ad.icon_type, 'image/png')
        for size in amo.ADDON_ICON_SIZES:
            fn = '%s-%s.png' % (ad.id, size)
            assert os.path.exists(os.path.join(
                ad.get_icon_dir(), fn)), ('Expected %s in %s' %
                                          (fn, os.listdir(ad.get_icon_dir())))

    def test_screenshot_or_video_required(self):
        self._step()
        data = self.get_dict()
        for k in data:
            if k.startswith('files') and k.endswith('upload_hash'):
                data[k] = ''
        rp = self.client.post(self.url, data)
        eq_(rp.context['form_previews'].non_form_errors(),
            ['You must upload at least one screenshot or video.'])

    def test_unsaved_screenshot(self):
        self._step()
        # If there are form errors we should still pass the previews URIs.
        preview_type = 'video/webm'
        preview_uri = 'moz-filedata:p00p'
        data = self.preview_formset({
            'position': 1,
            'upload_hash': '<hash_one>',
            'unsaved_image_type': preview_type,
            'unsaved_image_data': preview_uri
        })
        r = self.client.post(self.url, data)
        eq_(r.status_code, 200)
        form = pq(r.content)('form')
        eq_(
            form.find('input[name=files-0-unsaved_image_type]').val(),
            preview_type)
        eq_(
            form.find('input[name=files-0-unsaved_image_data]').val(),
            preview_uri)

    def test_unique_allowed(self):
        self._step()
        r = self.client.post(self.url, self.get_dict(name=self.webapp.name))
        self.assertNoFormErrors(r)
        app = Webapp.objects.exclude(app_slug=self.webapp.app_slug)[0]
        self.assert3xx(r, reverse('submit.app.done', args=[app.app_slug]))
        eq_(self.get_webapp().status, amo.STATUS_PENDING)

    def test_slug_invalid(self):
        self._step()
        # Submit an invalid slug.
        d = self.get_dict(app_slug='slug!!! aksl23%%')
        r = self.client.post(self.url, d)
        eq_(r.status_code, 200)
        self.assertFormError(
            r, 'form_basic', 'app_slug',
            "Enter a valid 'slug' consisting of letters, numbers, underscores "
            "or hyphens.")

    def test_slug_required(self):
        self._step()
        r = self.client.post(self.url, self.get_dict(app_slug=''))
        eq_(r.status_code, 200)
        self.assertFormError(r, 'form_basic', 'app_slug',
                             'This field is required.')

    def test_summary_required(self):
        self._step()
        r = self.client.post(self.url, self.get_dict(summary=''))
        eq_(r.status_code, 200)
        self.assertFormError(r, 'form_basic', 'summary',
                             'This field is required.')

    def test_summary_length(self):
        self._step()
        r = self.client.post(self.url, self.get_dict(summary='a' * 1025))
        eq_(r.status_code, 200)
        self.assertFormError(
            r, 'form_basic', 'summary',
            'Ensure this value has at most 1024 characters (it has 1025).')

    def test_description_optional(self):
        self._step()
        r = self.client.post(self.url, self.get_dict(description=None))
        self.assertNoFormErrors(r)

    def test_privacy_policy_required(self):
        self._step()
        r = self.client.post(self.url, self.get_dict(privacy_policy=None))
        self.assertFormError(r, 'form_basic', 'privacy_policy',
                             'This field is required.')

    def test_clashing_locale(self):
        self.webapp.default_locale = 'de'
        self.webapp.save()
        self._step()
        self.client.cookies['current_locale'] = 'en-us'
        data = self.get_dict(name=None,
                             name_de='Test name',
                             privacy_policy=None,
                             **{'privacy_policy_en-us': 'XXX'})
        r = self.client.post(self.url, data)
        self.assertNoFormErrors(r)

    def test_homepage_url_optional(self):
        self._step()
        r = self.client.post(self.url, self.get_dict(homepage=None))
        self.assertNoFormErrors(r)

    def test_homepage_url_invalid(self):
        self._step()
        r = self.client.post(self.url, self.get_dict(homepage='xxx'))
        self.assertFormError(r, 'form_basic', 'homepage', 'Enter a valid URL.')

    def test_support_url_optional(self):
        self._step()
        r = self.client.post(self.url, self.get_dict(support_url=None))
        self.assertNoFormErrors(r)

    def test_support_url_invalid(self):
        self._step()
        r = self.client.post(self.url, self.get_dict(support_url='xxx'))
        self.assertFormError(r, 'form_basic', 'support_url',
                             'Enter a valid URL.')

    def test_support_email_required(self):
        self._step()
        r = self.client.post(self.url, self.get_dict(support_email=None))
        self.assertFormError(r, 'form_basic', 'support_email',
                             'This field is required.')

    def test_support_email_invalid(self):
        self._step()
        r = self.client.post(self.url, self.get_dict(support_email='xxx'))
        self.assertFormError(r, 'form_basic', 'support_email',
                             'Enter a valid e-mail address.')

    def test_categories_required(self):
        self._step()
        r = self.client.post(self.url, self.get_dict(categories=[]))
        eq_(r.context['form_cats'].errors['categories'],
            ['This field is required.'])

    def test_categories_max(self):
        self._step()
        eq_(amo.MAX_CATEGORIES, 2)
        cat2 = Category.objects.create(type=amo.ADDON_WEBAPP, name='bling')
        cat3 = Category.objects.create(type=amo.ADDON_WEBAPP, name='blang')
        cats = [self.cat1.id, cat2.id, cat3.id]
        r = self.client.post(self.url, self.get_dict(categories=cats))
        eq_(r.context['form_cats'].errors['categories'],
            ['You can have only 2 categories.'])

    def _post_cats(self, cats):
        self.client.post(self.url, self.get_dict(categories=cats))
        eq_(sorted(self.get_webapp().categories.values_list('id', flat=True)),
            sorted(cats))

    def test_categories_add(self):
        self._step()
        cat2 = Category.objects.create(type=amo.ADDON_WEBAPP, name='bling')
        self._post_cats([self.cat1.id, cat2.id])

    def test_categories_add_and_remove(self):
        self._step()
        cat2 = Category.objects.create(type=amo.ADDON_WEBAPP, name='bling')
        self._post_cats([cat2.id])

    def test_categories_remove(self):
        # Add another category here so it gets added to the initial formset.
        cat2 = Category.objects.create(type=amo.ADDON_WEBAPP, name='bling')
        AddonCategory.objects.create(addon=self.webapp, category=cat2)
        self._step()

        # `cat2` should get removed.
        self._post_cats([self.cat1.id])

    def test_games_default_excluded_in_brazil(self):
        games = Category.objects.create(type=amo.ADDON_WEBAPP, slug='games')
        self._step()

        r = self.client.post(self.url, self.get_dict(categories=[games.id]))
        self.assertNoFormErrors(r)
        eq_(list(AER.objects.values_list('region', flat=True)),
            [mkt.regions.BR.id])

    def test_other_categories_are_not_excluded(self):
        # Keep the category around for good measure.
        Category.objects.create(type=amo.ADDON_WEBAPP, slug='games')
        self._step()

        r = self.client.post(self.url, self.get_dict())
        self.assertNoFormErrors(r)
        eq_(AER.objects.count(), 0)
Example #16
0
class TestReceipt(amo.tests.TestCase):
    fixtures = fixture('user_2519', 'webapp_337141')

    def setUp(self):
        self.addon = Addon.objects.get(pk=337141)
        self.bundle = Bundle(data={'app': self.addon.pk})
        self.profile = UserProfile.objects.get(pk=2519)
        self.resource = ReceiptResource()

    def get_request(self, profile):
        request = RequestFactory().post('/')
        if not profile:
            request.user = AnonymousUser()
        else:
            request.user = profile.user
            request.amo_user = profile
        return request

    def handle(self, profile):
        return self.resource.handle(self.bundle, self.get_request(profile))

    def test_pending_free_for_developer(self):
        AddonUser.objects.create(addon=self.addon, user=self.profile)
        self.addon.update(status=amo.STATUS_PENDING)
        ok_(self.handle(self.profile))

    def test_pending_free_for_anonymous(self):
        self.addon.update(status=amo.STATUS_PENDING)
        with self.assertImmediate(http.HttpForbidden):
            ok_(self.handle(None))

    def test_pending_paid_for_developer(self):
        AddonUser.objects.create(addon=self.addon, user=self.profile)
        self.addon.update(status=amo.STATUS_PENDING,
                          premium_type=amo.ADDON_PREMIUM)
        ok_(self.handle(self.profile))
        eq_(self.profile.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)
        with self.assertImmediate(http.HttpForbidden):
            ok_(self.handle(None))

    def test_not_record_addon(self):
        self.addon.update(type=amo.ADDON_EXTENSION)
        with self.assertImmediate(http.HttpBadRequest):
            ok_(self.handle(self.profile))

    @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)
        ok_(self.handle(self.profile))

    def test_own_payments(self):
        self.addon.update(premium_type=amo.ADDON_OTHER_INAPP)
        ok_(self.handle(self.profile))

    @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)
        with self.assertImmediate(HttpPaymentRequired):
            ok_(self.handle(self.profile))

    @mock.patch('mkt.receipts.api.receipt_cef.log')
    def test_record_install(self, cef):
        ok_(self.handle(self.profile))
        installed = self.profile.installed_set.all()
        eq_(len(installed), 1)
        eq_(installed[0].install_type, apps.INSTALL_TYPE_USER)

    @mock.patch('mkt.receipts.api.receipt_cef.log')
    def test_record_multiple_installs(self, cef):
        ok_(self.handle(self.profile))
        ok_(self.handle(self.profile))
        eq_(self.profile.installed_set.count(), 1)

    @mock.patch('mkt.receipts.api.receipt_cef.log')
    def test_record_receipt(self, cef):
        res = self.handle(self.profile)
        ok_(Receipt(res).receipt_decoded())
Example #17
0
class TestFileViewer(FilesBase, mkt.site.tests.WebappTestCase):
    fixtures = fixture('group_editor', 'user_editor', 'user_editor_group',
                       'user_999', 'webapp_337141')

    def poll_url(self):
        return reverse('mkt.files.poll', args=[self.file.pk])

    def file_url(self, file=None):
        args = [self.file.pk]
        if file:
            args.extend(['file', file])
        return reverse('mkt.files.list', args=args)

    def check_urls(self, status):
        for url in [self.poll_url(), self.file_url()]:
            status_code = self.client.get(url).status_code
            assert status_code == status, (
                'Request to %s returned status code %d (expected %d)' %
                (url, status_code, status))

    def add_file(self, name, contents):
        dest = os.path.join(self.file_viewer.dest, name)
        with storage.open(dest, 'w') as f:
            f.write(contents)

    def test_files_xss(self):
        self.file_viewer.extract()
        self.add_file('<script>alert("foo")', '.')
        res = self.client.get(self.file_url())
        eq_(res.status_code, 200)
        doc = pq(res.content)
        # Note: this is text, not a DOM element, so escaped correctly.
        assert '<script>alert("' in doc('#files li a').text()

    def test_content_file(self):
        self.file_viewer.extract()
        res = self.client.get(self.file_url('manifest.webapp'))
        doc = pq(res.content)
        eq_(len(doc('#content')), 1)

    def test_content_no_file(self):
        self.file_viewer.extract()
        res = self.client.get(self.file_url())
        doc = pq(res.content)
        eq_(len(doc('#content')), 1)
        eq_(res.context['key'], 'manifest.webapp')

    def test_content_xss(self):
        self.file_viewer.extract()
        for name in ['file.txt', 'file.html', 'file.htm']:
            # If you are adding files, you need to clear out the memcache
            # file listing.
            cache.clear()
            self.add_file(name, '<script>alert("foo")</script>')
            res = self.client.get(self.file_url(name))
            doc = pq(res.content)
            # Note: this is text, not a DOM element, so escaped correctly.
            assert doc('#content').text().startswith('<script')

    def test_binary(self):
        self.file_viewer.extract()
        self.add_file('file.php', '<script>alert("foo")</script>')
        res = self.client.get(self.file_url('file.php'))
        eq_(res.status_code, 200)
        assert self.file_viewer.get_files()['file.php']['md5'] in res.content

    def test_tree_no_file(self):
        self.file_viewer.extract()
        res = self.client.get(self.file_url('doesnotexist.js'))
        eq_(res.status_code, 404)

    def test_directory(self):
        self.file_viewer.extract()
        res = self.client.get(self.file_url('doesnotexist.js'))
        eq_(res.status_code, 404)

    def test_serve_no_token(self):
        self.file_viewer.extract()
        res = self.client.get(self.files_serve(binary))
        eq_(res.status_code, 403)

    def test_serve_fake_token(self):
        self.file_viewer.extract()
        res = self.client.get(self.files_serve(binary) + '?token=aasd')
        eq_(res.status_code, 403)

    def test_serve_bad_token(self):
        self.file_viewer.extract()
        res = self.client.get(self.files_serve(binary) + '?token=a asd')
        eq_(res.status_code, 403)

    def test_serve_get_token(self):
        self.file_viewer.extract()
        res = self.client.get(self.files_redirect(binary))
        eq_(res.status_code, 302)
        url = res['Location']
        assert url.startswith(settings.STATIC_URL)
        assert urlparse.urlparse(url).query.startswith('token=')

    def test_memcache_goes_bye_bye(self):
        self.file_viewer.extract()
        res = self.client.get(self.files_redirect(binary))
        url = res['Location'][len(settings.STATIC_URL) - 1:]
        cache.clear()
        res = self.client.get(url)
        eq_(res.status_code, 403)

    def test_bounce(self):
        # Don't run this test if the server has x-sendfile turned off.
        if not settings.XSENDFILE:
            raise SkipTest()

        self.file_viewer.extract()
        res = self.client.get(self.files_redirect(binary), follow=True)
        eq_(res.status_code, 200)
        eq_(res[settings.XSENDFILE_HEADER],
            self.file_viewer.get_files().get(binary)['full'])

    @patch.object(settings, 'FILE_VIEWER_SIZE_LIMIT', 5)
    def test_file_size(self):
        self.file_viewer.extract()
        res = self.client.get(self.file_url(not_binary))
        doc = pq(res.content)
        assert doc('.error').text().startswith('File size is')

    def test_poll_failed(self):
        msg = Message('file-viewer:%s' % self.file_viewer)
        msg.save('I like cheese.')
        res = self.client.get(self.poll_url())
        eq_(res.status_code, 200)
        data = json.loads(res.content)
        eq_(data['status'], False)
        eq_(data['msg'], ['I like cheese.'])

    def test_file_chooser_selection(self):
        res = self.client.get(self.file_url())
        doc = pq(res.content)

        eq_(
            doc('#id_left option[selected]').attr('value'),
            str(self.files[0].id))
        eq_(len(doc('#id_right option[value][selected]')), 0)
Example #18
0
class TestAPI(BaseOAuth):
    fixtures = fixture('user_2519', 'webapp_337141')

    def setUp(self):
        super(TestAPI, self).setUp(api_name='receipts')
        self.addon = Addon.objects.get(pk=337141)
        self.url = list_url('install')
        self.data = json.dumps({'app': self.addon.pk})
        self.profile = self.user.get_profile()

    def test_has_cors(self):
        self.assertCORS(self.client.get(self.url), 'post')

    def post(self, anon=False):
        client = self.client if not anon else self.anon
        return client.post(self.url, data=self.data)

    def test_no_app(self):
        self.data = json.dumps({'app': 0})
        eq_(self.post().status_code, 400)

    def test_app_slug(self):
        self.data = json.dumps({'app': self.addon.app_slug})
        eq_(self.post().status_code, 201)

    def test_record_logged_out(self):
        res = self.post(anon=True)
        eq_(res.status_code, 201)
        eq_(res.json['receipt'], None)

    @mock.patch('mkt.receipts.api.receipt_cef.log')
    def test_cef_logs(self, cef):
        eq_(self.post().status_code, 201)
        eq_(len(cef.call_args_list), 1)
        eq_([x[0][2] for x in cef.call_args_list], ['sign'])

    @mock.patch('mkt.receipts.api.record_action')
    @mock.patch('mkt.receipts.api.receipt_cef.log')
    def test_record_metrics(self, cef, record_action):
        res = self.post()
        eq_(res.status_code, 201)
        record_action.assert_called_with('install', mock.ANY, {
            'app-domain': u'http://micropipes.com',
            'app-id': self.addon.pk, 'anonymous': False})

    @mock.patch('mkt.receipts.api.record_action')
    @mock.patch('mkt.receipts.api.receipt_cef.log')
    def test_record_metrics_packaged_app(self, cef, record_action):
        # Mimic packaged app.
        self.addon.update(is_packaged=True, manifest_url=None)
        res = self.post()
        eq_(res.status_code, 201)
        record_action.assert_called_with('install', mock.ANY, {
            'app-domain': settings.SITE_URL,
            'app-id': self.addon.pk, 'anonymous': False})

    @mock.patch('mkt.receipts.views.receipt_cef.log')
    def test_log_metrics(self, cef):
        eq_(self.post().status_code, 201)
        logs = AppLog.objects.filter(addon=self.addon)
        eq_(logs.count(), 1)
        eq_(logs[0].activity_log.action, amo.LOG.INSTALL_ADDON.id)
Example #19
0
class TestRegionForm(amo.tests.WebappTestCase):
    fixtures = fixture('webapp_337141')

    def setUp(self):
        super(TestRegionForm, self).setUp()
        self.request = RequestFactory()
        self.kwargs = {'product': self.app}

    def test_initial_empty(self):
        form = forms.RegionForm(data=None, **self.kwargs)
        self.assertSetEqual(
            form.initial['regions'],
            set(mkt.regions.ALL_REGION_IDS) -
            set(mkt.regions.SPECIAL_REGION_IDS))
        eq_(form.initial['enable_new_regions'], False)

    def test_initial_excluded_in_region(self):
        self.app.addonexcludedregion.create(region=mkt.regions.BR.id)

        # Everything except Brazil.
        regions = set(mkt.regions.ALL_REGION_IDS)
        regions.remove(mkt.regions.BR.id)
        self.assertSetEqual(self.get_app().get_region_ids(restofworld=True),
                            regions)

        form = forms.RegionForm(data=None, **self.kwargs)

        # Everything except Brazil and China.
        self.assertSetEqual(form.initial['regions'],
                            regions - set(mkt.regions.SPECIAL_REGION_IDS))
        eq_(form.initial['enable_new_regions'], False)

    def test_initial_excluded_in_regions_and_future_regions(self):
        regions = [mkt.regions.BR, mkt.regions.UK, mkt.regions.RESTOFWORLD]
        for region in regions:
            self.app.addonexcludedregion.create(region=region.id)

        regions = set(mkt.regions.ALL_REGION_IDS)
        regions.remove(mkt.regions.BR.id)
        regions.remove(mkt.regions.UK.id)
        regions.remove(mkt.regions.RESTOFWORLD.id)

        self.assertSetEqual(self.get_app().get_region_ids(), regions)

        form = forms.RegionForm(data=None, **self.kwargs)
        self.assertSetEqual(form.initial['regions'],
                            regions - set(mkt.regions.SPECIAL_REGION_IDS))
        eq_(form.initial['enable_new_regions'], False)

    def test_restofworld_only(self):
        form = forms.RegionForm({'regions': [mkt.regions.RESTOFWORLD.id]},
                                **self.kwargs)
        assert form.is_valid(), form.errors

    def test_no_regions(self):
        form = forms.RegionForm({'enable_new_regions': True}, **self.kwargs)
        assert not form.is_valid(), 'Form should be invalid'
        eq_(form.errors, {'regions': ['You must select at least one region.']})

    def test_exclude_each_region(self):
        """Test that it's possible to exclude each region."""

        for region_id in mkt.regions.ALL_REGION_IDS:
            to_exclude = list(mkt.regions.ALL_REGION_IDS)
            to_exclude.remove(region_id)

            form = forms.RegionForm(
                {
                    'regions': to_exclude,
                    'restricted': '1',
                    'enable_new_regions': True
                }, **self.kwargs)
            assert form.is_valid(), form.errors
            form.save()

            r_id = mkt.regions.REGIONS_CHOICES_ID_DICT[region_id]
            eq_(self.app.reload().get_region_ids(True), to_exclude,
                'Failed for %s' % r_id)

    def test_exclude_restofworld(self):
        form = forms.RegionForm(
            {
                'regions': mkt.regions.REGION_IDS,
                'restricted': '1',
                'enable_new_regions': False
            }, **self.kwargs)
        assert form.is_valid(), form.errors
        form.save()
        eq_(self.app.get_region_ids(True), mkt.regions.REGION_IDS)

    def test_reinclude_region(self):
        self.app.addonexcludedregion.create(region=mkt.regions.BR.id)

        form = forms.RegionForm(
            {
                'regions': mkt.regions.ALL_REGION_IDS,
                'enable_new_regions': True
            }, **self.kwargs)
        assert form.is_valid(), form.errors
        form.save()
        eq_(self.app.get_region_ids(True), mkt.regions.ALL_REGION_IDS)

    def test_reinclude_restofworld(self):
        self.app.addonexcludedregion.create(region=mkt.regions.RESTOFWORLD.id)

        form = forms.RegionForm({'regions': mkt.regions.ALL_REGION_IDS},
                                **self.kwargs)
        assert form.is_valid(), form.errors
        form.save()
        eq_(self.app.get_region_ids(True), mkt.regions.ALL_REGION_IDS)

    def test_restofworld_valid_choice_paid(self):
        self.app.update(premium_type=amo.ADDON_PREMIUM)
        form = forms.RegionForm({'regions': [mkt.regions.RESTOFWORLD.id]},
                                **self.kwargs)
        assert form.is_valid(), form.errors

    def test_restofworld_valid_choice_free(self):
        form = forms.RegionForm({'regions': [mkt.regions.RESTOFWORLD.id]},
                                **self.kwargs)
        assert form.is_valid(), form.errors

    def test_china_initially_excluded_if_null(self):
        self.create_flag('special-regions')
        form = forms.RegionForm(None, **self.kwargs)
        cn = mkt.regions.CN.id
        assert cn not in form.initial['regions']
        assert cn in dict(form.fields['regions'].choices).keys()

    def _test_china_excluded_if_pending_or_rejected(self):
        self.create_flag('special-regions')

        # Mark app as pending/rejected in China.
        for status in (amo.STATUS_PENDING, amo.STATUS_REJECTED):
            self.app.geodata.set_status(mkt.regions.CN, status, save=True)
            eq_(self.app.geodata.get_status(mkt.regions.CN), status)

            # Post the form.
            form = forms.RegionForm(
                {
                    'regions': mkt.regions.ALL_REGION_IDS,
                    'special_regions': [mkt.regions.CN.id]
                }, **self.kwargs)

            # China should be checked if it's pending and
            # unchecked if rejected.
            cn = mkt.regions.CN.id
            if status == amo.STATUS_PENDING:
                assert cn in form.initial['regions'], (status,
                                                       form.initial['regions'])
            else:
                assert cn not in form.initial['regions'], (
                    status, form.initial['regions'])
            choices = dict(form.fields['regions'].choices).keys()
            assert cn in choices, (status, choices)

            assert form.is_valid(), form.errors
            form.save()

            # App should be unlisted in China and always pending after
            # requesting China.
            self.app = self.app.reload()
            eq_(self.app.listed_in(mkt.regions.CN), False)
            eq_(self.app.geodata.get_status(mkt.regions.CN),
                amo.STATUS_PENDING)

    def test_china_excluded_if_pending_or_rejected(self):
        self._test_china_excluded_if_pending_or_rejected()

    def test_china_already_excluded_and_pending_or_rejected(self):
        cn = mkt.regions.CN.id
        self.app.addonexcludedregion.create(region=cn)

        # If the app was already excluded in China, the checkbox should still
        # be checked if the app's been requested for approval in China now.
        self._test_china_excluded_if_pending_or_rejected()

    def test_china_excluded_if_pending_cancelled(self):
        """
        If the developer already requested to be in China,
        and a reviewer hasn't reviewed it for China yet,
        keep the region exclusion and the status as pending.

        """

        self.create_flag('special-regions')

        # Mark app as pending in China.
        status = amo.STATUS_PENDING
        self.app.geodata.set_status(mkt.regions.CN, status, save=True)
        eq_(self.app.geodata.get_status(mkt.regions.CN), status)

        # Post the form.
        form = forms.RegionForm({'regions': mkt.regions.ALL_REGION_IDS},
                                **self.kwargs)

        # China should be checked if it's pending.
        cn = mkt.regions.CN.id
        assert cn in form.initial['regions']
        assert cn in dict(form.fields['regions'].choices).keys()

        assert form.is_valid(), form.errors
        form.save()

        # App should be unlisted in China and now null.
        self.app = self.app.reload()
        eq_(self.app.listed_in(mkt.regions.CN), False)
        eq_(self.app.geodata.get_status(mkt.regions.CN), amo.STATUS_NULL)

    def test_china_included_if_approved_but_unchecked(self):
        self.create_flag('special-regions')

        # Mark app as public in China.
        status = amo.STATUS_PUBLIC
        self.app.geodata.set_status(mkt.regions.CN, status, save=True)
        eq_(self.app.geodata.get_status(mkt.regions.CN), status)

        # Post the form.
        form = forms.RegionForm({'regions': mkt.regions.ALL_REGION_IDS},
                                **self.kwargs)

        # China should be checked if it's public.
        cn = mkt.regions.CN.id
        assert cn in form.initial['regions']
        assert cn in dict(form.fields['regions'].choices).keys()

        assert form.is_valid(), form.errors
        form.save()

        # App should be unlisted in China and now null.
        self.app = self.app.reload()
        eq_(self.app.listed_in(mkt.regions.CN), False)
        eq_(self.app.geodata.get_status(mkt.regions.CN), amo.STATUS_NULL)

    def test_china_included_if_approved_and_checked(self):
        self.create_flag('special-regions')

        # Mark app as public in China.
        status = amo.STATUS_PUBLIC
        self.app.geodata.set_status(mkt.regions.CN, status, save=True)
        eq_(self.app.geodata.get_status(mkt.regions.CN), status)

        # Post the form.
        form = forms.RegionForm(
            {
                'regions': mkt.regions.ALL_REGION_IDS,
                'special_regions': [mkt.regions.CN.id]
            }, **self.kwargs)
        assert form.is_valid(), form.errors
        form.save()

        # App should still be listed in China and still public.
        self.app = self.app.reload()
        eq_(self.app.listed_in(mkt.regions.CN), True)
        eq_(self.app.geodata.get_status(mkt.regions.CN), status)
Example #20
0
class TestLangPackNonAPIViews(TestCase):
    fixtures = fixture('user_2519')

    def setUp(self):
        super(TestLangPackNonAPIViews, self).setUp()
        self.fake_manifest = {
            'name': u'Fake LangPäck',
            'developer': {
                'name': 'Mozilla'
            }
        }
        self.langpack = LangPack.objects.create(version='0.1',
                                                active=True,
                                                manifest=json.dumps(
                                                    self.fake_manifest))
        self.user = UserProfile.objects.get(pk=2519)
        with public_storage.open(self.langpack.file_path, 'w') as f:
            f.write('sample data\n')

    def _expected_etag(self):
        expected_etag = hashlib.sha256()
        expected_etag.update(unicode(self.langpack.pk))
        expected_etag.update(unicode(self.langpack.file_version))
        return '"%s"' % expected_etag.hexdigest()

    @override_settings(
        XSENDFILE=True,
        DEFAULT_FILE_STORAGE='mkt.site.storage_utils.LocalFileStorage')
    def test_download(self):
        ok_(self.langpack.download_url)
        response = self.client.get(self.langpack.download_url)

        eq_(response.status_code, 200)
        eq_(response[settings.XSENDFILE_HEADER], self.langpack.file_path)
        eq_(response['Content-Type'], 'application/zip')
        eq_(response['etag'], self._expected_etag())

        self.login(self.user)
        response = self.client.get(self.langpack.download_url)
        eq_(response.status_code, 200)

    @override_settings(
        DEFAULT_FILE_STORAGE='mkt.site.storage_utils.S3BotoPrivateStorage')
    def test_download_storage(self):
        ok_(self.langpack.download_url)
        response = self.client.get(self.langpack.download_url)
        path = public_storage.url(self.langpack.file_path)
        self.assert3xx(response, path)

    def test_download_inactive(self):
        self.langpack.update(active=False)
        ok_(self.langpack.download_url)
        response = self.client.get(self.langpack.download_url)
        eq_(response.status_code, 404)

        self.login(self.user)
        response = self.client.get(self.langpack.download_url)
        eq_(response.status_code, 404)

    @override_settings(
        XSENDFILE=True,
        DEFAULT_FILE_STORAGE='mkt.site.storage_utils.LocalFileStorage')
    def test_download_inactive_has_perm(self):
        self.langpack.update(active=False)
        self.grant_permission(self.user, 'LangPacks:Admin')
        self.login(self.user)
        ok_(self.langpack.download_url)
        response = self.client.get(self.langpack.download_url)
        eq_(response.status_code, 200)
        eq_(response[settings.XSENDFILE_HEADER], self.langpack.file_path)
        eq_(response['Content-Type'], 'application/zip')
        eq_(response['etag'], self._expected_etag())

    def test_manifest(self):
        ok_(self.langpack.manifest_url)
        response = self.client.get(self.langpack.manifest_url)
        eq_(response.status_code, 200)
        eq_(response['Content-Type'], MANIFEST_CONTENT_TYPE)
        manifest_contents = json.loads(
            self.langpack.get_minifest_contents()[0])
        data = json.loads(response.content)
        eq_(data, manifest_contents)

    def test_manifest_etag(self):
        response = self.client.get(self.langpack.manifest_url)
        eq_(response.status_code, 200)
        original_etag = response['ETag']
        ok_(original_etag)
        self.assertCloseToNow(
            response['Last-Modified'],
            now=self.langpack.modified.replace(tzinfo=tzutc()))

        # Test that the etag is different if the langpack file_version changes.
        self.langpack.update(file_version=42)
        self.langpack.get_minifest_contents(force=True)  # Re-generate cache.
        response = self.client.get(self.langpack.manifest_url)
        eq_(response.status_code, 200)
        new_etag = response['ETag']
        ok_(new_etag)
        ok_(original_etag != new_etag)

        # Test that the etag is different if just the minifest contents change,
        # but not the langpack instance itself.
        minifest_contents = json.loads(
            self.langpack.get_minifest_contents()[0])
        minifest_contents['name'] = 'Different Name'
        minifest_contents = json.dumps(minifest_contents)
        patch_method = 'mkt.langpacks.models.LangPack.get_minifest_contents'
        with patch(patch_method) as get_minifest_contents_mock:
            get_minifest_contents_mock.return_value = (minifest_contents,
                                                       'yet_another_etag')
            response = self.client.get(self.langpack.manifest_url)
            eq_(response.status_code, 200)
            yet_another_etag = response['ETag']
            ok_(yet_another_etag)
            ok_(original_etag != new_etag != yet_another_etag)

    def test_manifest_inactive(self):
        manifest_url = self.langpack.manifest_url
        ok_(manifest_url)
        self.langpack.update(active=False)
        # We don't return a manifest url when the langpack is inactive.
        eq_(self.langpack.manifest_url, '')
        response = self.client.get(manifest_url)
        eq_(response.status_code, 404)

    def test_manifest_inactive_has_perm(self):
        manifest_url = self.langpack.manifest_url
        ok_(manifest_url)
        self.langpack.update(active=False)
        self.grant_permission(self.user, 'LangPacks:Admin')
        self.login(self.user)
        # We don't return a manifest url when the langpack is inactive, but
        # it should still work if you have the right permission.
        eq_(self.langpack.manifest_url, '')
        response = self.client.get(manifest_url)
        eq_(response.status_code, 200)
        manifest_contents = json.loads(
            self.langpack.get_minifest_contents()[0])
        data = json.loads(response.content)
        eq_(data, manifest_contents)
Example #21
0
class TestUpdateFeatures(amo.tests.TestCase):
    fixtures = fixture('webapp_337141')

    def setUp(self):
        self.create_switch('buchets')

        self.app = Webapp.objects.get(pk=337141)
        self.app.update(is_packaged=True)
        # Note: the app files are wrong, since we now have a packaged app, but
        # it doesn't matter since we are mocking everything, we'll never touch
        # the files.

        p = mock.patch('mkt.webapps.tasks.run_validator')
        self.mock_validator = p.start()
        self.mock_validator.return_value = json.dumps({'feature_profile': {}})
        self.patches = [p]

    def tearDown(self):
        super(TestUpdateFeatures, self).tearDown()
        for p in self.patches:
            p.stop()

    @mock.patch('mkt.webapps.tasks._update_features')
    def test_ignore_not_webapp(self, mock_):
        self.app.update(type=amo.ADDON_EXTENSION)
        call_command('process_addons', task='update_features')
        assert not mock_.called

    @mock.patch('mkt.webapps.tasks._update_features')
    def test_pending(self, mock_):
        self.app.update(status=amo.STATUS_PENDING)
        call_command('process_addons', task='update_features')
        assert mock_.called

    @mock.patch('mkt.webapps.tasks._update_features')
    def test_public_waiting(self, mock_):
        self.app.update(status=amo.STATUS_PUBLIC_WAITING)
        call_command('process_addons', task='update_features')
        assert mock_.called

    @mock.patch('mkt.webapps.tasks._update_features')
    def test_ignore_disabled(self, mock_):
        self.app.update(status=amo.STATUS_DISABLED)
        call_command('process_addons', task='update_features')
        assert not mock_.called

    @mock.patch('mkt.webapps.tasks._update_features')
    def test_ignore_non_packaged(self, mock_):
        self.app.update(is_packaged=False)
        call_command('process_addons', task='update_features')
        assert not mock_.called

    def test_ignore_no_current_version(self):
        self.app.current_version.all_files[0].update(
            status=amo.STATUS_DISABLED)
        self.app.update_version()
        update_features(ids=(self.app.pk, ))
        assert not self.mock_validator.called

    def test_ignore_non_empty_features_profile(self):
        version = self.app.current_version
        version.features.update(has_sms=True)
        update_features(ids=(self.app.pk, ))
        assert not self.mock_validator.called

    def test_validator(self):
        update_features(ids=(self.app.pk, ))
        assert self.mock_validator.called
        features = self.app.current_version.features.to_dict()
        eq_(AppFeatures().to_dict(), features)

    def test_validator_with_results(self):
        feature_profile = ['APPS', 'ACTIVITY']
        self.mock_validator.return_value = json.dumps(
            {'feature_profile': feature_profile})
        update_features(ids=(self.app.pk, ))
        assert self.mock_validator.called
        features = self.app.current_version.features.to_dict()
        eq_(features['has_apps'], True)
        eq_(features['has_activity'], True)
        eq_(features['has_sms'], False)

    def test_validator_with_results_existing_empty_profile(self):
        feature_profile = ['APPS', 'ACTIVITY']
        self.mock_validator.return_value = json.dumps(
            {'feature_profile': feature_profile})
        version = self.app.current_version
        eq_(AppFeatures.objects.count(), 1)
        eq_(version.features.to_list(), [])
        update_features(ids=(self.app.pk, ))
        assert self.mock_validator.called
        eq_(AppFeatures.objects.count(), 1)

        # Features are cached on the version, therefore reload the app since we
        # were using the same instance as before.
        self.app.reload()
        features = self.app.current_version.features.to_dict()
        eq_(features['has_apps'], True)
        eq_(features['has_activity'], True)
        eq_(features['has_sms'], False)
Example #22
0
class TestAppCreateHandler(CreateHandler, AMOPaths):
    fixtures = fixture('user_admin', 'user_2519', 'user_999')

    def count(self):
        return Webapp.objects.count()

    def test_verbs(self):
        self.create()
        self._allowed_verbs(self.list_url, ['get', 'post'])
        self.create_app()
        self._allowed_verbs(self.get_url, ['get', 'put', 'delete'])

    def test_not_accepted_tos(self):
        self.user.update(read_dev_agreement=None)
        obj = self.create()
        res = self.client.post(self.list_url,
                               data=json.dumps({'manifest': obj.uuid}))
        eq_(res.status_code, 403)
        eq_(res.json, {'detail': 'Terms of Service not accepted.'})

    def test_not_valid(self):
        obj = self.create()
        obj.update(valid=False)
        res = self.client.post(self.list_url,
                               data=json.dumps({'manifest': obj.uuid}))
        eq_(res.status_code, 400)
        eq_(res.json['detail'], 'Upload not valid.')
        eq_(self.count(), 0)

    def test_not_there(self):
        res = self.client.post(
            self.list_url,
            data=json.dumps({'manifest': 'some-random-32-character-stringy'}))
        eq_(res.status_code, 400)
        eq_(res.json['detail'], 'No upload found.')
        eq_(self.count(), 0)

    def test_anon(self):
        obj = self.create()
        obj.update(user=None)
        res = self.client.post(self.list_url,
                               data=json.dumps({'manifest': obj.uuid}))
        eq_(res.status_code, 403)
        eq_(self.count(), 0)

    def test_not_yours(self):
        obj = self.create()
        obj.update(user=UserProfile.objects.get(email='*****@*****.**'))
        res = self.client.post(self.list_url,
                               data=json.dumps({'manifest': obj.uuid}))
        eq_(res.status_code, 403)
        eq_(self.count(), 0)

    @patch('mkt.webapps.views.record_action')
    def test_create(self, record_action):
        obj = self.create()
        res = self.client.post(self.list_url,
                               data=json.dumps({'manifest': obj.uuid}))
        eq_(res.status_code, 201)
        content = json.loads(res.content)
        eq_(content['status'], 0)
        eq_(content['slug'], u'mozillaball')
        eq_(content['support_email'], None)
        eq_(self.count(), 1)

        app = Webapp.objects.get(app_slug=content['slug'])
        eq_(set(app.authors.all()), set([self.user]))
        assert record_action.called

    def create_app(self, fil=None):
        obj = self.create(fil)
        res = self.client.post(self.list_url,
                               data=json.dumps({'manifest': obj.uuid}))
        pk = json.loads(res.content)['id']
        self.get_url = reverse('app-detail', kwargs={'pk': pk})
        return Webapp.objects.get(pk=pk)

    def test_upsell(self):
        app = self.create_app()
        upsell = app_factory()
        self.make_premium(upsell)
        AddonUpsell.objects.create(free=app, premium=upsell)
        res = self.client.get(self.get_url)
        eq_(res.status_code, 200)
        obj = json.loads(res.content)['upsell']
        eq_(obj['id'], upsell.id)
        eq_(obj['app_slug'], upsell.app_slug)
        eq_(obj['name'], upsell.name)
        eq_(obj['icon_url'], upsell.get_icon_url(128))
        eq_(obj['resource_uri'], reverse('app-detail',
                                         kwargs={'pk': upsell.id}))

    def test_get(self):
        self.create_app()
        res = self.client.get(self.get_url)
        eq_(res.status_code, 200)
        content = json.loads(res.content)
        eq_(content['status'], 0)

    def test_get_slug(self):
        app = self.create_app()
        url = reverse('app-detail', kwargs={'pk': app.app_slug})
        res = self.client.get(url)
        content = json.loads(res.content)
        eq_(content['id'], app.pk)

    def test_list(self):
        app = self.create_app()
        res = self.client.get(self.list_url)
        eq_(res.status_code, 200)
        content = json.loads(res.content)
        eq_(content['meta']['total_count'], 1)
        eq_(content['objects'][0]['id'], app.pk)

    def test_list_anon(self):
        eq_(self.anon.get(self.list_url).status_code, 403)

    def test_get_device(self):
        app = self.create_app()
        AddonDeviceType.objects.create(addon=app,
                                       device_type=amo.DEVICE_DESKTOP.id)
        res = self.client.get(self.get_url)
        eq_(res.status_code, 200)
        content = json.loads(res.content)
        eq_(content['device_types'], [u'desktop'])

    def test_not_public(self):
        self.create_app()
        res = self.anon.get(self.get_url)
        eq_(res.status_code, 403)

    def test_get_public(self):
        app = self.create_app()
        app.update(status=amo.STATUS_PUBLIC)
        res = self.anon.get(self.get_url)
        eq_(res.status_code, 200)

    def test_get_previews(self):
        app = self.create_app()
        res = self.client.get(self.get_url)
        eq_(len(json.loads(res.content)['previews']), 0)
        Preview.objects.create(addon=app)
        res = self.client.get(self.get_url)
        eq_(len(json.loads(res.content)['previews']), 1)

    def test_get_not_mine(self):
        obj = self.create_app()
        obj.authors.clear()
        res = self.client.get(self.get_url)
        eq_(res.status_code, 403)

    def test_get_privacy_policy(self):
        app = self.create_app()
        data = self.base_data()
        self.client.put(self.get_url, data=json.dumps(data))
        app.reload()
        app.authors.clear()
        res = self.client.get(
            reverse('app-privacy-policy-detail', args=[app.pk]))
        eq_(res.status_code, 403)

    def test_get_privacy_policy_anon(self):
        app = self.create_app()
        data = self.base_data()
        self.client.put(self.get_url, data=json.dumps(data))
        res = self.anon.get(reverse('app-privacy-policy-detail',
                                    args=[app.pk]))
        eq_(res.status_code, 403)

    def test_get_privacy_policy_mine(self):
        app = self.create_app()
        data = self.base_data()
        self.client.put(self.get_url, data=json.dumps(data))
        res = self.client.get(
            reverse('app-privacy-policy-detail', args=[app.pk]))
        eq_(res.json['privacy_policy'], data['privacy_policy'])

    def test_get_privacy_policy_slug(self):
        app = self.create_app()
        data = self.base_data()
        self.client.put(self.get_url, data=json.dumps(data))
        url = reverse('app-privacy-policy-detail', kwargs={'pk': app.app_slug})
        res = self.client.get(url)
        eq_(res.json['privacy_policy'], data['privacy_policy'])

    def base_data(self):
        return {
            'support_email': '*****@*****.**',
            'privacy_policy': 'wat',
            'homepage': 'http://www.whatever.com',
            'name': 'mozball',
            'categories': self.categories,
            'description': 'wat...',
            'premium_type': 'free',
            'regions': ['us'],
            'device_types': amo.DEVICE_LOOKUP.keys()
        }

    def test_put(self):
        app = self.create_app()
        res = self.client.put(self.get_url, data=json.dumps(self.base_data()))
        eq_(res.status_code, 202)
        app = Webapp.objects.get(pk=app.pk)
        eq_(app.privacy_policy, 'wat')

    def test_put_as_post(self):
        # This is really a test of the HTTP_X_HTTP_METHOD_OVERRIDE header
        # and that signing works correctly. Do a POST, but ask DRF to do
        # a PUT.
        app = self.create_app()
        res = self.client.post(self.get_url,
                               data=json.dumps(self.base_data()),
                               HTTP_X_HTTP_METHOD_OVERRIDE='PUT')
        eq_(res.status_code, 202)
        app = Webapp.objects.get(pk=app.pk)
        eq_(app.privacy_policy, 'wat')

    def test_put_anon(self):
        app = self.create_app()
        app.update(status=amo.STATUS_PUBLIC)
        res = self.anon.put(self.get_url, data=json.dumps(self.base_data()))
        eq_(res.status_code, 403)

    def test_put_categories_worked(self):
        app = self.create_app()
        res = self.client.put(self.get_url, data=json.dumps(self.base_data()))
        eq_(res.status_code, 202)
        app = Webapp.objects.get(pk=app.pk)
        eq_(set(app.categories), set(self.categories))

    def test_post_content_ratings(self):
        """Test the @action on AppViewSet to attach the content ratings."""
        app = self.create_app()
        url = reverse('app-content-ratings', kwargs={'pk': app.pk})
        res = self.client.post(url,
                               data=json.dumps({
                                   'submission_id': 50,
                                   'security_code': 'AB12CD3'
                               }))
        eq_(res.status_code, 201)
        eq_(res.content, '')

    def test_post_content_ratings_bad(self):
        """Test the @action on AppViewSet to attach the content ratings."""
        app = self.create_app()
        url = reverse('app-content-ratings', kwargs={'pk': app.pk})
        # Missing `security_code`.
        res = self.client.post(url, data=json.dumps({'submission_id': 50}))
        eq_(res.status_code, 400)
        eq_(json.loads(res.content),
            {'security_code': ['This field is required.']})

    def test_dehydrate(self):
        app = self.create_app()
        res = self.client.put(self.get_url, data=json.dumps(self.base_data()))
        app = app.reload()
        version = app.current_version

        eq_(res.status_code, 202)
        res = self.client.get(self.get_url + '?lang=en')
        eq_(res.status_code, 200)
        data = json.loads(res.content)

        eq_(set(app.categories), set(data['categories']))
        eq_(data['current_version'], version and version.version)
        self.assertSetEqual(data['device_types'],
                            [n.api_name for n in amo.DEVICE_TYPES.values()])
        eq_(data['homepage'], u'http://www.whatever.com')
        eq_(data['is_packaged'], False)
        eq_(data['author'], 'Mozilla Labs')
        eq_(data['manifest_url'], app.manifest_url)
        eq_(data['premium_type'], 'free')
        eq_(data['price'], None)
        eq_(data['price_locale'], None)
        eq_(data['public_stats'], False)
        eq_(data['support_email'], u'*****@*****.**')
        eq_(data['ratings'], {'count': 0, 'average': 0.0})
        eq_(data['user'], {
            'developed': True,
            'installed': False,
            'purchased': False
        })

    def test_ratings(self):
        app = self.create_app()
        rater = UserProfile.objects.get(pk=999)
        Review.objects.create(addon=app, user=self.user, body='yes', rating=3)
        Review.objects.create(addon=app, user=rater, body='no', rating=2)
        res = self.client.get(self.get_url)
        eq_(res.status_code, 200)
        data = json.loads(res.content)
        eq_(data['ratings'], {'count': 2, 'average': 2.5})

    def test_put_wrong_category(self):
        self.create_app()
        data = self.base_data()
        data['categories'] = ['nonexistent']
        res = self.client.put(self.get_url, data=json.dumps(data))
        eq_(res.status_code, 400)

    def test_put_no_categories(self):
        self.create_app()
        data = self.base_data()
        del data['categories']
        res = self.client.put(self.get_url, data=json.dumps(data))
        eq_(res.status_code, 400)
        eq_(res.json['categories'], ['This field is required.'])

    def test_put_no_desktop(self):
        self.create_app()
        data = self.base_data()
        del data['device_types']
        res = self.client.put(self.get_url, data=json.dumps(data))
        eq_(res.status_code, 400)
        eq_(res.json['device_types'], ['This field is required.'])

    def test_put_devices_worked(self):
        app = self.create_app()
        data = self.base_data()
        data['device_types'] = [a.api_name for a in amo.DEVICE_TYPES.values()]
        res = self.client.put(self.get_url, data=json.dumps(data))
        eq_(res.status_code, 202)
        app = Webapp.objects.get(pk=app.pk)
        eq_(set(d for d in app.device_types),
            set(amo.DEVICE_TYPES[d] for d in amo.DEVICE_TYPES.keys()))

    def test_put_desktop_error_nice(self):
        self.create_app()
        data = self.base_data()
        data['device_types'] = [12345]
        res = self.client.put(self.get_url, data=json.dumps(data))
        eq_(res.status_code, 400)
        assert '12345' in res.json['device_types'][0], res.data

    def create_price(self, price):
        tier = Price.objects.create(price=price)
        # This is needed for the serialisation of the app.
        PriceCurrency.objects.create(tier=tier,
                                     price=price,
                                     provider=1,
                                     region=regions.US.id)

    def test_put_price(self):
        app = self.create_app()
        data = self.base_data()
        self.create_price('1.07')
        data['premium_type'] = 'premium'
        data['price'] = '1.07'
        res = self.client.put(self.get_url, data=json.dumps(data))
        eq_(res.status_code, 202)
        app = Webapp.objects.get(pk=app.pk)
        eq_(str(app.get_price(region=regions.US.id)), '1.07')

    def test_put_premium_inapp(self):
        app = self.create_app()
        data = self.base_data()
        self.create_price('1.07')
        data['premium_type'] = 'premium-inapp'
        data['price'] = '1.07'
        res = self.client.put(self.get_url, data=json.dumps(data))
        eq_(res.status_code, 202)
        app = Webapp.objects.get(pk=app.pk)
        eq_(str(app.get_price(region=regions.US.id)), '1.07')
        eq_(app.premium_type, amo.ADDON_PREMIUM_INAPP)

    def test_put_bad_price(self):
        self.create_app()
        data = self.base_data()
        self.create_price('1.07')
        self.create_price('3.14')
        data['premium_type'] = 'premium'
        data['price'] = "2.03"
        res = self.client.put(self.get_url, data=json.dumps(data))
        eq_(res.status_code, 400)
        eq_(
            res.json['price'][0],
            'Premium app specified without a valid price. Price can be one of '
            '"1.07", "3.14".')

    def test_put_no_price(self):
        self.create_app()
        data = self.base_data()
        Price.objects.create(price='1.07')
        Price.objects.create(price='3.14')
        data['premium_type'] = 'premium'
        res = self.client.put(self.get_url, data=json.dumps(data))
        eq_(res.status_code, 400)
        eq_(
            res.json['price'][0],
            'Premium app specified without a valid price. Price can be one of '
            '"1.07", "3.14".')

    def test_put_free_inapp(self):
        app = self.create_app()
        data = self.base_data()
        data['premium_type'] = 'free-inapp'
        res = self.client.put(self.get_url, data=json.dumps(data))
        eq_(res.status_code, 202)
        eq_(app.reload().get_price(region=regions.US.id), None)


# TODO: renable when regions are sorted out.
#    def test_put_region_bad(self):
#        self.create_app()
#        data = self.base_data()
#        data['regions'] = []
#        res = self.client.put(self.get_url, data=json.dumps(data))
#        eq_(res.status_code, 400)
#
#    def test_put_region_good(self):
#        app = self.create_app()
#        data = self.base_data()
#        data['regions'] = ['br', 'us', 'uk']
#        res = self.client.put(self.get_url, data=json.dumps(data))
#        eq_(res.status_code, 202)
#        eq_(app.get_regions(), [regions.BR, regions.UK, regions.US])

    def test_put_not_mine(self):
        obj = self.create_app()
        obj.authors.clear()
        res = self.client.put(self.get_url, data='{}')
        eq_(res.status_code, 403)

    def test_put_not_there(self):
        url = reverse('app-detail', kwargs={'pk': 123})
        res = self.client.put(url, data='{}')
        eq_(res.status_code, 404)

    def test_delete(self):
        obj = self.create_app()
        res = self.client.delete(self.get_url)
        eq_(res.status_code, 204)
        assert not Webapp.objects.filter(pk=obj.pk).exists()

    def test_delete_not_mine(self):
        obj = self.create_app()
        obj.authors.clear()
        res = self.client.delete(self.get_url)
        eq_(res.status_code, 403)
        assert Webapp.objects.filter(pk=obj.pk).exists()

    def test_reviewer_get(self):
        app = self.create_app()
        data = self.base_data()
        self.client.put(self.get_url, data=json.dumps(data))

        editor = UserProfile.objects.get(email='*****@*****.**')
        g = Group.objects.create(rules='Apps:Review,Reviews:Edit')
        GroupUser.objects.create(group=g, user=editor)
        ac = Access.objects.create(key='adminOauthKey',
                                   secret=generate(),
                                   user=editor)
        client = RestOAuthClient(ac)
        r = client.get(self.get_url)
        eq_(r.status_code, 200)

        res = client.get(reverse('app-privacy-policy-detail', args=[app.pk]))
        eq_(r.status_code, 200)
        eq_(res.json['privacy_policy'], data['privacy_policy'])

    def test_admin_get(self):
        app = self.create_app()
        data = self.base_data()
        self.client.put(self.get_url, data=json.dumps(data))

        admin = UserProfile.objects.get(email='*****@*****.**')
        g = Group.objects.create(rules='*:*')
        GroupUser.objects.create(group=g, user=admin)
        ac = Access.objects.create(key='adminOauthKey',
                                   secret=generate(),
                                   user=admin)
        client = RestOAuthClient(ac)
        r = client.get(self.get_url)
        eq_(r.status_code, 200)

        res = client.get(reverse('app-privacy-policy-detail', args=[app.pk]))
        eq_(r.status_code, 200)
        eq_(res.json['privacy_policy'], data['privacy_policy'])
Example #23
0
class TestAPI(RestOAuth):
    fixtures = fixture('user_2519', 'webapp_337141')

    def setUp(self):
        super(TestAPI, self).setUp()
        self.addon = Webapp.objects.get(pk=337141)
        self.url = reverse('app-install-list')
        self.data = json.dumps({'app': self.addon.pk})
        self.profile = self.user

    def test_has_cors(self):
        self.assertCORS(self.client.post(self.url), 'post')

    def post(self, anon=False):
        client = self.anon if anon else self.client
        return client.post(self.url, data=self.data)

    def test_no_app(self):
        self.data = json.dumps({'app': 0})
        eq_(self.post().status_code, 400)

    def test_not_public(self):
        self.addon.update(status=mkt.STATUS_DISABLED)
        self.data = json.dumps({'app': self.addon.app_slug})
        eq_(self.post().status_code, 403)

    def test_not_paid(self):
        self.addon.update(premium_type=mkt.ADDON_PREMIUM)
        self.data = json.dumps({'app': self.addon.app_slug})
        eq_(self.post().status_code, 400)

    def test_app_slug(self):
        self.data = json.dumps({'app': self.addon.app_slug})
        eq_(self.post().status_code, 201)
        eq_(self.profile.reload().installed_set.all()[0].addon, self.addon)

    def test_app_pk(self):
        self.data = json.dumps({'app': self.addon.pk})
        eq_(self.post().status_code, 201)
        eq_(self.profile.reload().installed_set.all()[0].addon, self.addon)

    @patch('mkt.installs.utils.record_action')
    def test_logged(self, record_action):
        self.data = json.dumps({'app': self.addon.pk})
        eq_(self.post().status_code, 201)
        record_action.assert_called_with(
            'install', ANY, {
                'app-domain': u'http://micropipes.com',
                'app-id': 337141L,
                'region': 'restofworld',
                'anonymous': False
            })

    @patch('mkt.installs.utils.record_action')
    def test_logged_anon(self, record_action):
        self.data = json.dumps({'app': self.addon.pk})
        eq_(self.post(anon=True).status_code, 201)
        record_action.assert_called_with(
            'install', ANY, {
                'app-domain': u'http://micropipes.com',
                'app-id': 337141L,
                'region': 'restofworld',
                'anonymous': True
            })

    @patch('mkt.installs.utils.record_action')
    def test_app_install_twice(self, record_action):
        Installed.objects.create(user=self.profile,
                                 addon=self.addon,
                                 install_type=INSTALL_TYPE_USER)
        eq_(self.post().status_code, 202)

    def test_app_install_developer(self):
        AddonUser.objects.create(addon=self.addon, user=self.profile)
        self.data = json.dumps({'app': self.addon.app_slug})
        eq_(self.post().status_code, 201)
        eq_(self.profile.reload().installed_set.all()[0].install_type,
            INSTALL_TYPE_DEVELOPER)

    def test_app_install_developer_not_public(self):
        self.addon.update(status=mkt.STATUS_DISABLED)
        self.test_app_install_developer()
Example #24
0
class TestAppDetail(RestOAuth):
    fixtures = fixture('user_2519', 'webapp_337141')

    def setUp(self, api_name='apps'):
        super(TestAppDetail, self).setUp()
        self.app = Webapp.objects.get(pk=337141)
        self.get_url = reverse('app-detail', kwargs={'pk': self.app.app_slug})

    def test_price(self):
        res = self.client.get(self.get_url)
        data = json.loads(res.content)
        eq_(data['price'], None)

    def test_price_other_region(self):
        res = self.client.get(self.get_url, {'lang': 'fr'})
        data = json.loads(res.content)
        eq_(data['price'], None)

    def test_nonexistent_app(self):
        """
        In combination with test_nonregion, this ensures that a distinction is
        appropriately drawn between attempts to access nonexistent apps and
        attempts to access apps that are unavailable due to legal restrictions.
        """
        url = reverse('app-detail', kwargs={'pk': 1})
        res = self.client.get(url)
        eq_(res.status_code, 404)

    def test_nonregion(self):
        self.app.addonexcludedregion.create(region=regions.BR.id)
        self.app.support_url = u'http://www.example.com/fake_support_url'
        self.app.save()
        res = self.client.get(self.get_url, data={'region': 'br'})
        eq_(res.status_code, 451)
        data = json.loads(res.content)['detail']
        eq_(data['reason'], 'Not available in your region.')
        eq_(data['support_email'], '*****@*****.**')
        eq_(data['support_url'], 'http://www.example.com/fake_support_url')

    def test_owner_nonregion(self):
        AddonUser.objects.create(addon_id=337141, user_id=self.user.pk)
        AddonExcludedRegion.objects.create(addon_id=337141,
                                           region=regions.BR.id)
        res = self.client.get(self.get_url, data={'region': 'br'})
        eq_(res.status_code, 200)

    def test_packaged_manifest_url(self):
        self.app.update(is_packaged=True)
        res = self.client.get(self.get_url, pk=self.app.app_slug)
        data = json.loads(res.content)
        eq_(self.app.get_manifest_url(), data['manifest_url'])

    def test_get_upsold(self):
        free = Webapp.objects.create(status=amo.STATUS_PUBLIC)
        AddonUpsell.objects.create(premium_id=337141, free=free)
        res = self.client.get(self.get_url)
        eq_(res.json['upsold'], reverse('app-detail', kwargs={'pk': free.pk}))

    def test_tags(self):
        tag1 = Tag.objects.create(tag_text='example1')
        tag2 = Tag.objects.create(tag_text='example2')
        AddonTag.objects.create(tag=tag1, addon=self.app)
        AddonTag.objects.create(tag=tag2, addon=self.app)
        res = self.client.get(self.get_url, pk=self.app.app_slug)
        data = json.loads(res.content)
        eq_(data['tags'], ['example1', 'example2'])

    def test_banner_message(self):
        geodata = self.app.geodata
        geodata.banner_regions = [mkt.regions.BR.id, mkt.regions.AR.id]
        geodata.banner_message = u'Hello!'
        geodata.save()
        res = self.client.get(self.get_url + '?lang=en')
        eq_(res.status_code, 200)
        data = json.loads(res.content)
        eq_(data['banner_message'], unicode(geodata.banner_message))
        eq_(data['banner_regions'], [mkt.regions.AR.slug, mkt.regions.BR.slug])
Example #25
0
class TestTransactionRefund(TestCase):
    fixtures = fixture('user_support_staff', 'user_999')

    def setUp(self):
        self.uuid = 'paymentuuid'
        self.refund_uuid = 'refunduuid'
        self.summary_url = reverse('lookup.transaction_summary',
                                   args=[self.uuid])
        self.url = reverse('lookup.transaction_refund', args=[self.uuid])
        self.app = app_factory()
        self.user = UserProfile.objects.get(username='******')
        AddonUser.objects.create(addon=self.app, user=self.user)

        self.contrib = Contribution.objects.create(addon=self.app,
                                                   user=self.user,
                                                   uuid=self.uuid,
                                                   type=amo.CONTRIB_PURCHASE,
                                                   amount=1)

        self.req = RequestFactory().post(self.url, {'refund_reason': 'text'})
        self.req.user = User.objects.get(username='******')
        self.req.amo_user = UserProfile.objects.get(username='******')
        self.req.groups = self.req.amo_user.groups.all()
        # Fix Django 1.4 RequestFactory bug with MessageMiddleware.
        setattr(self.req, 'session', 'session')
        messages = FallbackStorage(self.req)
        setattr(self.req, '_messages', messages)
        self.login(self.req.user)

    def bango_ret(self, status):
        return {'status': status, 'transaction': 'transaction_uri'}

    def refund_tx_ret(self):
        return {'uuid': self.refund_uuid}

    @mock.patch('mkt.lookup.views.client')
    def test_refund_success(self, solitude):
        solitude.api.bango.refund.post.return_value = self.bango_ret(PENDING)
        solitude.get.return_value = self.refund_tx_ret()

        # Do refund.
        res = transaction_refund(self.req, self.uuid)
        refund = Refund.objects.filter(contribution__addon=self.app)
        refund_contribs = self.contrib.get_refund_contribs()

        # Check Refund created.
        assert refund.exists()
        eq_(refund[0].status, amo.REFUND_PENDING)
        assert self.req.POST['refund_reason'] in refund[0].refund_reason

        # Check refund Contribution created.
        eq_(refund_contribs.exists(), True)
        eq_(refund_contribs[0].refund, refund[0])
        eq_(refund_contribs[0].related, self.contrib)
        eq_(refund_contribs[0].amount, -self.contrib.amount)

        self.assert3xx(res, self.summary_url)

    @mock.patch('mkt.lookup.views.client')
    def test_refund_failed(self, solitude):
        solitude.api.bango.refund.post.return_value = self.bango_ret(FAILED)

        res = transaction_refund(self.req, self.uuid)

        # Check no refund Contributions created.
        assert not self.contrib.get_refund_contribs().exists()
        self.assert3xx(res, self.summary_url)

    def test_cant_refund(self):
        self.contrib.update(type=amo.CONTRIB_PENDING)
        resp = self.client.post(self.url, {'refund_reason': 'text'})
        eq_(resp.status_code, 404)

    @mock.patch('mkt.lookup.views.client')
    def test_already_refunded(self, solitude):
        solitude.api.bango.refund.post.return_value = self.bango_ret(PENDING)
        solitude.get.return_value = self.refund_tx_ret()
        res = transaction_refund(self.req, self.uuid)
        refund_count = Contribution.objects.all().count()

        # Check no refund Contributions created.
        res = self.client.post(self.url, {'refund_reason': 'text'})
        assert refund_count == Contribution.objects.all().count()
        self.assert3xx(res,
                       reverse('lookup.transaction_summary', args=[self.uuid]))

    @mock.patch('mkt.lookup.views.client')
    def test_refund_slumber_error(self, solitude):
        for exception in (exceptions.HttpClientError,
                          exceptions.HttpServerError):
            solitude.api.bango.refund.post.side_effect = exception
            res = transaction_refund(self.req, self.uuid)

            # Check no refund Contributions created.
            assert not self.contrib.get_refund_contribs().exists()
            self.assert3xx(res, self.summary_url)

    @mock.patch('mkt.lookup.views.client')
    def test_redirect(self, solitude):
        solitude.api.bango.refund.post.return_value = self.bango_ret(PENDING)
        solitude.get.return_value = self.refund_tx_ret()

        res = self.client.post(self.url, {'refund_reason': 'text'})
        self.assert3xx(res,
                       reverse('lookup.transaction_summary', args=[self.uuid]))

    @mock.patch('mkt.lookup.views.client')
    @mock.patch.object(settings, 'SEND_REAL_EMAIL', True)
    def test_refund_pending_email(self, solitude):
        solitude.api.bango.refund.post.return_value = self.bango_ret(PENDING)
        solitude.get.return_value = self.refund_tx_ret()

        transaction_refund(self.req, self.uuid)
        eq_(len(mail.outbox), 1)
        assert self.app.name.localized_string in smart_str(mail.outbox[0].body)

    @mock.patch('mkt.lookup.views.client')
    @mock.patch.object(settings, 'SEND_REAL_EMAIL', True)
    def test_refund_completed_email(self, solitude):
        solitude.api.bango.refund.post.return_value = self.bango_ret(COMPLETED)
        solitude.get.return_value = self.refund_tx_ret()

        transaction_refund(self.req, self.uuid)
        eq_(len(mail.outbox), 1)
        assert self.app.name.localized_string in smart_str(mail.outbox[0].body)

    @mock.patch('mkt.lookup.views.client')
    def test_403_reg_user(self, solitude):
        solitude.api.bango.refund.post.return_value = self.bango_ret(PENDING)
        solitude.get.return_value = self.refund_tx_ret()

        self.login(self.user)
        res = self.client.post(self.url, {'refund_reason': 'text'})
        eq_(res.status_code, 403)
Example #26
0
class TestPriceTier(RestOAuth):
    fixtures = fixture('user_2519', 'prices2')

    def setUp(self):
        self.permission = 'Prices:Edit'
        RestOAuth.setUp(self)
        self.list_url = reverse('price-tier-list')
        self.detail_url = reverse('price-tier-detail', kwargs={'pk': 1})

    def test_list(self):
        self.grant_permission(self.profile, self.permission)
        res = self.client.get(self.list_url)
        j = json.loads(res.content)
        eq_(len(j['objects']), 2)
        eq_(
            j['objects'][0], {
                'active': True,
                'name': '1',
                'price': '0.99',
                'method': 'operator+card',
                'resource_uri': self.detail_url
            })

    def test_detail(self):
        self.grant_permission(self.profile, self.permission)
        res = self.client.get(self.detail_url)
        j = json.loads(res.content)
        eq_(
            j, {
                'active': True,
                'name': '1',
                'price': '0.99',
                'method': 'operator+card',
                'resource_uri': self.detail_url
            })

    def test_post_unauthorized(self):
        res = self.client.post(self.list_url, '{}')
        eq_(res.status_code, 403)

    def test_post_admin(self):
        self.grant_permission(self.profile, self.permission)
        res = self.client.post(
            self.list_url,
            json.dumps({
                'name': '3',
                'price': '3.14',
                'method': 'operator+card',
                'active': True
            }))
        eq_(res.status_code, 201)
        p = Price.objects.get(pk=3)
        eq_(p.name, '3')
        eq_(p.price, Decimal('3.14'))
        eq_(p.method, amo.PAYMENT_METHOD_ALL)
        assert p.active

    def test_put_unauthorized(self):
        res = self.client.put(self.detail_url, '{}')
        eq_(res.status_code, 403)

    def test_put(self):
        self.grant_permission(self.profile, self.permission)
        res = self.client.put(
            self.detail_url,
            json.dumps({
                'name': '1',
                'price': '0.10',
                'method': 'operator',
                'active': True
            }))
        eq_(res.status_code, 200)
        p = Price.objects.get(pk=1)
        eq_(p.name, '1')
        eq_(p.price, Decimal('0.10'))
        eq_(p.method, amo.PAYMENT_METHOD_OPERATOR)
        assert p.active

    def test_delete_unauthorized(self):
        res = self.client.delete(self.detail_url)
        eq_(res.status_code, 403)

    def test_delete(self):
        self.grant_permission(self.profile, self.permission)
        res = self.client.delete(self.detail_url)
        eq_(res.status_code, 204)
        assert not Price.objects.filter(pk=1).exists()
Example #27
0
class TestFile(amo.tests.TestCase, amo.tests.AMOPaths):
    """
    Tests the methods of the File model.
    """
    fixtures = fixture('webapp_337141')

    def test_get_absolute_url(self):
        f = File.objects.get()
        url = f.get_absolute_url(src='src')
        expected = '/downloads/file/81555/steamcube.webapp?src=src'
        assert url.endswith(expected), url

    def check_delete(self, file_, filename):
        """Test that when the File object is deleted, it is removed from the
        filesystem."""
        try:
            with storage.open(filename, 'w') as f:
                f.write('sample data\n')
            assert storage.exists(filename)
            file_.delete()
            assert not storage.exists(filename)
        finally:
            if storage.exists(filename):
                storage.delete(filename)

    def test_delete_by_version(self):
        f = File.objects.get()
        version = f.version
        self.check_delete(version, f.file_path)

    def test_delete_file_path(self):
        f = File.objects.get()
        self.check_delete(f, f.file_path)

    def test_delete_no_file(self):
        """Test that the file object can be deleted without the file being
        present."""
        f = File.objects.get()
        filename = f.file_path
        assert not os.path.exists(filename), 'File exists at: %s' % filename
        f.delete()

    def test_delete_signal(self):
        """Test that if there's no filename, the signal is ok."""
        f = File.objects.get()
        f.update(filename='')
        f.delete()

    @mock.patch('files.models.File.hide_disabled_file')
    def test_disable_signal(self, hide_mock):
        f = File.objects.get()
        f.status = amo.STATUS_PUBLIC
        f.save()
        assert not hide_mock.called

        f.status = amo.STATUS_DISABLED
        f.save()
        assert hide_mock.called

    @mock.patch('files.models.File.unhide_disabled_file')
    def test_unhide_on_enable(self, unhide_mock):
        f = File.objects.get()
        f.status = amo.STATUS_PUBLIC
        f.save()
        assert not unhide_mock.called

        f = File.objects.get()
        f.status = amo.STATUS_DISABLED
        f.save()
        assert not unhide_mock.called

        f = File.objects.get()
        f.status = amo.STATUS_PUBLIC
        f.save()
        assert unhide_mock.called

    def test_unhide_disabled_files(self):
        f = File.objects.get()
        f.status = amo.STATUS_PUBLIC
        with storage.open(f.guarded_file_path, 'wb') as fp:
            fp.write('some data\n')
        f.unhide_disabled_file()
        assert storage.exists(f.file_path)
        assert storage.open(f.file_path).size

    def test_generate_filename(self):
        f = File.objects.get()
        eq_(f.generate_filename(), 'something-something-1.0.webapp')

    def test_generate_filename_packaged_app(self):
        f = File.objects.get()
        f.version.addon.app_slug = 'testing-123'
        f.version.addon.type = amo.ADDON_WEBAPP
        f.version.addon.is_packaged = True
        eq_(f.generate_filename(), 'testing-123-1.0.zip')

    def test_generate_webapp_fn_non_ascii(self):
        f = File()
        f.version = Version(version='0.1.7')
        f.version.addon = Addon(app_slug=u' フォクすけ  といっしょ',
                                type=amo.ADDON_WEBAPP)
        eq_(f.generate_filename(), 'app-0.1.7.webapp')

    def test_generate_webapp_fn_partial_non_ascii(self):
        f = File()
        f.version = Version(version='0.1.7')
        f.version.addon = Addon(app_slug=u'myapp フォクすけ  といっしょ',
                                type=amo.ADDON_WEBAPP)
        eq_(f.generate_filename(), 'myapp-0.1.7.webapp')

    def test_generate_filename_ja(self):
        f = File()
        f.version = Version(version='0.1.7')
        f.version.addon = Addon(name=u' フォクすけ  といっしょ')
        eq_(f.generate_filename(), 'none-0.1.7.webapp')

    def clean_files(self, f):
        if not storage.exists(f.file_path):
            with storage.open(f.file_path, 'w') as fp:
                fp.write('sample data\n')

    def test_generate_hash(self):
        f = File()
        f.version = Version.objects.get()
        fn = self.packaged_app_path('mozball.zip')
        assert f.generate_hash(fn).startswith('sha256:ad85d6316166d4')

    def test_addon(self):
        f = File.objects.get()
        addon_id = f.version.addon_id
        addon = Addon.objects.no_cache().get(pk=addon_id)
        addon.update(status=amo.STATUS_DELETED)
        eq_(f.addon.id, addon_id)
Example #28
0
class TestPriceCurrency(RestOAuth):
    fixtures = fixture('user_2519', 'prices2')

    def setUp(self):
        self.permission = 'Prices:Edit'
        RestOAuth.setUp(self)
        self.list_url = reverse('price-currency-list')
        self.detail_url = reverse('price-currency-detail', kwargs={'pk': 1})
        self.tier_url = reverse('price-tier-detail', kwargs={'pk': 1})

    def test_list(self):
        self.grant_permission(self.profile, self.permission)
        res = self.client.get(self.list_url)
        j = json.loads(res.content)
        eq_(len(j['objects']), 8)
        eq_(
            j['objects'][0], {
                'carrier': None,
                'currency': 'PLN',
                'dev': True,
                'method': 'operator+card',
                'paid': True,
                'price': '5.01',
                'provider': 'bango',
                'region': 'pl',
                'resource_uri': self.detail_url,
                'tier': self.tier_url
            })

    def test_detail(self):
        self.grant_permission(self.profile, self.permission)
        res = self.client.get(self.detail_url)
        j = json.loads(res.content)
        eq_(
            j, {
                'carrier': None,
                'currency': 'PLN',
                'dev': True,
                'method': 'operator+card',
                'paid': True,
                'price': '5.01',
                'provider': 'bango',
                'region': 'pl',
                'resource_uri': self.detail_url,
                'tier': self.tier_url
            })

    def test_post_unauthorized(self):
        res = self.client.post(self.list_url, '{}')
        eq_(res.status_code, 403)

    def test_post_admin(self):
        self.grant_permission(self.profile, self.permission)
        res = self.client.post(
            self.list_url,
            json.dumps({
                'tier': self.tier_url,
                'carrier': None,
                'currency': 'PHP',
                'method': 'operator',
                'price': '10.05',
                'provider': 'bango',
                'region': 'pl',
                'paid': True,
                'dev': True
            }))
        eq_(res.status_code, 201)
        # Get the pk from the response.
        pk = res.json['resource_uri'].split('/')[-2]
        p = PriceCurrency.objects.get(pk=pk)
        eq_(p.tier_id, 1)
        eq_(p.price, Decimal('10.05'))
        eq_(p.method, amo.PAYMENT_METHOD_OPERATOR)
        eq_(p.currency, 'PHP')

    def test_put_unauthorized(self):
        res = self.client.put(self.detail_url, '{}')
        eq_(res.status_code, 403)

    def test_put(self):
        self.grant_permission(self.profile, self.permission)
        res = self.client.put(
            self.detail_url,
            json.dumps({
                'tier': self.tier_url,
                'carrier': None,
                'currency': 'USD',
                'method': 'operator',
                'price': '10.05',
                'provider': 'bango',
                'region': 'pl',
                'paid': True,
                'dev': False
            }))
        eq_(res.status_code, 200, res.content)
        p = PriceCurrency.objects.get(pk=1)
        eq_(p.tier_id, 1)
        eq_(p.price, Decimal('10.05'))
        eq_(p.method, amo.PAYMENT_METHOD_OPERATOR)
        eq_(p.currency, 'USD')
        eq_(p.region, 11)
        eq_(p.paid, True)
        eq_(p.dev, False)

    def test_delete_unauthorized(self):
        res = self.client.delete(self.detail_url)
        eq_(res.status_code, 403)

    def test_delete(self):
        self.grant_permission(self.profile, self.permission)
        res = self.client.delete(self.detail_url)
        eq_(res.status_code, 204)
        assert not PriceCurrency.objects.filter(pk=1).exists()
Example #29
0
class TestApproveRegion(RestOAuth):
    fixtures = fixture('user_2519', 'webapp_337141')

    def url(self, **kwargs):
        kw = {'pk': '337141', 'region': 'cn'}
        kw.update(kwargs)
        return reverse('approve-region', kwargs=kw)

    def test_verbs(self):
        self.grant_permission(self.profile, 'Apps:ReviewRegionCN')
        self._allowed_verbs(self.url(), ['post'])

    def test_anon(self):
        res = self.anon.post(self.url())
        eq_(res.status_code, 403)

    def test_bad_webapp(self):
        self.grant_permission(self.profile, 'Apps:ReviewRegionCN')
        res = self.client.post(self.url(pk='999'))
        eq_(res.status_code, 404)

    def test_webapp_not_pending_in_region(self):
        self.grant_permission(self.profile, 'Apps:ReviewRegionCN')
        res = self.client.post(self.url())
        eq_(res.status_code, 404)

    def test_good_but_no_permission(self):
        res = self.client.post(self.url())
        eq_(res.status_code, 403)

    def test_good_webapp_but_wrong_region_permission(self):
        self.grant_permission(self.profile, 'Apps:ReviewRegionBR')

        app = Webapp.objects.get(id=337141)
        app.geodata.set_status('cn', mkt.STATUS_PENDING, save=True)

        res = self.client.post(self.url())
        eq_(res.status_code, 403)

    def test_good_webapp_but_wrong_region_queue(self):
        self.grant_permission(self.profile, 'Apps:ReviewRegionCN')

        app = Webapp.objects.get(id=337141)
        app.geodata.set_status('cn', mkt.STATUS_PENDING, save=True)

        res = self.client.post(self.url(region='br'))
        eq_(res.status_code, 403)

    def test_good_rejected(self):
        self.grant_permission(self.profile, 'Apps:ReviewRegionCN')

        app = Webapp.objects.get(id=337141)
        app.geodata.set_status('cn', mkt.STATUS_PENDING, save=True)
        app.geodata.set_nominated_date('cn', save=True)

        res = self.client.post(self.url())
        eq_(res.status_code, 200)
        obj = json.loads(res.content)
        eq_(obj['approved'], False)
        eq_(app.geodata.reload().get_status('cn'), mkt.STATUS_REJECTED)

    def test_good_approved(self):
        self.grant_permission(self.profile, 'Apps:ReviewRegionCN')

        app = Webapp.objects.get(id=337141)
        app.geodata.set_status('cn', mkt.STATUS_PENDING, save=True)
        app.geodata.set_nominated_date('cn', save=True)

        res = self.client.post(self.url(), data=json.dumps({'approve': '1'}))
        eq_(res.status_code, 200)
        obj = json.loads(res.content)
        eq_(obj['approved'], True)
        eq_(app.geodata.reload().get_status('cn'), mkt.STATUS_PUBLIC)
Example #30
0
class TestHideDisabledFiles(amo.tests.TestCase):
    fixtures = fixture('webapp_337141')

    msg = 'Moving disabled file: %s => %s'

    def setUp(self):
        self.addon = Webapp.objects.get(pk=337141)
        self.version = self.addon.latest_version
        self.f1 = self.version.all_files[0]

    @mock.patch('mkt.files.models.os')
    def test_leave_nondisabled_files(self, os_mock):
        stati = [(amo.STATUS_PUBLIC, amo.STATUS_PUBLIC)]
        for addon_status, file_status in stati:
            self.addon.update(status=addon_status)
            File.objects.update(status=file_status)
            cron.hide_disabled_files()
            assert not os_mock.path.exists.called, (addon_status, file_status)

    @mock.patch('mkt.files.models.File.mv')
    @mock.patch('mkt.files.models.storage')
    def test_move_user_disabled_addon(self, m_storage, mv_mock):
        # Use Webapp.objects.update so the signal handler isn't called.
        Webapp.objects.filter(id=self.addon.id).update(
            status=amo.STATUS_PUBLIC, disabled_by_user=True)
        File.objects.update(status=amo.STATUS_PUBLIC)
        cron.hide_disabled_files()

        # Check that f1 was moved.
        mv_mock.assert_called_with(self.f1.file_path,
                                   self.f1.guarded_file_path, self.msg)
        # There's only 1 file.
        eq_(mv_mock.call_count, 1)

    @mock.patch('mkt.files.models.File.mv')
    @mock.patch('mkt.files.models.storage')
    def test_move_admin_disabled_addon(self, m_storage, mv_mock):
        Webapp.objects.filter(id=self.addon.id).update(
            status=amo.STATUS_DISABLED)
        File.objects.update(status=amo.STATUS_PUBLIC)
        cron.hide_disabled_files()
        # Check that f1 was moved.
        mv_mock.assert_called_with(self.f1.file_path,
                                   self.f1.guarded_file_path, self.msg)
        # There's only 1 file.
        eq_(mv_mock.call_count, 1)

    @mock.patch('mkt.files.models.File.mv')
    @mock.patch('mkt.files.models.storage')
    def test_move_disabled_file(self, m_storage, mv_mock):
        Webapp.objects.filter(id=self.addon.id).update(
            status=amo.STATUS_REJECTED)
        File.objects.filter(id=self.f1.id).update(status=amo.STATUS_DISABLED)
        cron.hide_disabled_files()
        # f1 should have been moved.
        mv_mock.assert_called_with(self.f1.file_path,
                                   self.f1.guarded_file_path, self.msg)
        eq_(mv_mock.call_count, 1)

    @mock.patch('mkt.files.models.File.mv')
    @mock.patch('mkt.files.models.storage')
    def test_ignore_deleted_versions(self, m_storage, mv_mock):
        # Apps only have 1 file and version delete only deletes one.
        self.version.delete()
        mv_mock.reset_mock()
        # Create a new version/file just like the one we deleted.
        version = Version.objects.create(addon=self.addon)
        File.objects.create(version=version, filename='f2')
        cron.hide_disabled_files()
        # Mock shouldn't have been called.
        assert not mv_mock.called, mv_mock.call_args