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'])
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)
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()
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, '')
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)
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))
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)
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)
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)
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)
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)
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)
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 <script>alert("xss")</script>', '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)
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())
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)
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)
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)
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)
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)
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'])
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()
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])
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)
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()
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)
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()
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)
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