fdroid-repomaker/repomaker/tests/models/test_remoteapp.py

269 lines
13 KiB
Python

import io
from datetime import datetime, timezone
from unittest.mock import patch
import os
from django.conf import settings
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError
from django.utils import translation
from repomaker import DEFAULT_USER_NAME
from repomaker.models import Repository, RemoteRepository, App, RemoteApp, Apk, ApkPointer, \
RemoteApkPointer, RemoteScreenshot
from repomaker.models.screenshot import PHONE
from repomaker.storage import get_icon_file_path_for_app
from .. import datetime_is_recent, RmTestCase
# noinspection PyProtectedMember
class RemoteAppTestCase(RmTestCase):
repo = None
app = None
local_repo = None
def setUp(self):
if not settings.SINGLE_USER_MODE:
self.user = User.objects.create(username=DEFAULT_USER_NAME)
self.client.force_login(user=self.user)
else:
self.user = User.objects.get()
date = datetime.fromtimestamp(1337, timezone.utc)
self.repo = RemoteRepository.objects.create(name='Test', url='http://repo_url',
last_change_date=date)
self.app = RemoteApp.objects.create(repo=self.repo, package_id="org.example",
last_updated_date=date)
self.local_repo = Repository.objects.create(name='Test Local',
url='http://repo_local',
fingerprint="local_fp",
user=self.user)
def test_update_from_json_only_when_update(self):
json = {'name': 'app', 'lastUpdated': 10000}
self.assertFalse(self.app.update_from_json(json)) # app did not change
def test_update_from_json(self):
json = {'name': 'app', 'summary': 'foo', 'description': 'bar<script>', 'webSite': 'site',
'added': 9999999900000, 'lastUpdated': 9999999900000, 'authorName': 'author'}
self.assertTrue(self.app.update_from_json(json)) # app changed
# assert that a default translation was created
self.assertEqual([settings.LANGUAGE_CODE], list(self.app.get_available_languages()))
# assert app was updated properly
self.assertEqual(json['name'], self.app.name)
self.assertEqual('foo', self.app.summary)
self.assertEqual('', self.app.summary_override) # override is not used
self.assertEqual('bar', self.app.description) # <script> tag was removed
self.assertEqual('', self.app.description_override) # override is not used
self.assertEqual(json['webSite'], self.app.website)
self.assertTrue(datetime_is_recent(self.app.added_date))
last_update = datetime.fromtimestamp(json['lastUpdated'] / 1000, timezone.utc)
self.assertEqual(last_update, self.app.last_updated_date)
self.assertEqual(json['authorName'], self.app.author_name)
def test_update_from_json_moves_overrides(self):
json = {'name': 'app', 'summary': 'foo', 'description': 'bar<script>',
'localized': {settings.LANGUAGE_CODE: {'foo': 'bar'}},
'lastUpdated': 9999999900000}
self.assertTrue(self.app.update_from_json(json)) # app changed
# assert app was updated properly
self.assertEqual('foo', self.app.summary)
self.assertEqual('', self.app.summary_override) # override is not used
self.assertEqual('bar', self.app.description) # <script> tag was removed
self.assertEqual('', self.app.description_override) # override is not used
def test_update_from_json_moves_override_no_data_loss(self):
json = {'name': 'app', 'summary': 'foo', 'description': 'bar<script>',
'localized': {
settings.LANGUAGE_CODE: {'summary': 'foo2', 'description': 'bar2<script>'}
},
'lastUpdated': 9999999900000}
self.assertTrue(self.app.update_from_json(json)) # app changed
# assert app was updated properly
self.assertEqual('foo2', self.app.summary)
self.assertEqual('foo', self.app.summary_override)
self.assertEqual('bar2', self.app.description) # <script> tag was removed
self.assertEqual('bar', self.app.description_override) # <script> tag was removed
@patch('fdroidserver.net.http_get')
@patch('repomaker.models.app.App.update_icon')
def test_update_icon(self, update_icon, http_get):
# set initial etag and icon for app
self.app.icon_etag = 'etag'
self.app.icon.save('test.png', io.BytesIO(b'foo'))
old_icon_path = self.app.icon.path
self.assertTrue(os.path.isfile(old_icon_path))
# create one local app tracking the remote one
App.objects.create(repo_id=self.local_repo.pk, package_id=self.app.package_id,
tracked_remote=self.app)
# update icon
http_get.return_value = b'icon-data', 'new_etag'
self.app.update_icon('icon.png') # pylint: disable=protected-access
http_get.assert_called_once_with(self.repo.url + '/icons-640/icon.png', 'etag')
# re-retrieve app from database to make sure it was saved
self.app = RemoteApp.objects.get(pk=self.app.pk)
# assert that old icon got deleted and new one was saved
self.assertFalse(os.path.isfile(old_icon_path))
new_icon_name = get_icon_file_path_for_app(self.app, 'icon.png')
self.assertEqual(new_icon_name, self.app.icon.name)
self.assertTrue(os.path.isfile(self.app.icon.path))
# assert that new etag was saved
self.assertEqual('new_etag', self.app.icon_etag)
# assert that local app icons have been updated
update_icon.called_once_with(self.app.icon)
def test_update_translations_new(self):
# update remote app translation with a new one
localized = {'en': {'summary': 'foo', 'description': 'bar', 'video': 'bla'}}
self.app._update_translations(localized) # pylint: disable=protected-access
# assert that translation has been saved
with translation.override('en'):
app = RemoteApp.objects.get(pk=self.app.pk)
self.assertEqual(localized['en']['summary'], app.summary)
self.assertEqual(localized['en']['description'], app.description)
def test_update_translations_existing(self):
# add a new translation
self.test_update_translations_new()
self.assertTrue('en' in self.app.get_available_languages())
# self.assertTrue(RemoteApp.objects.language('en').exists())
# update existing translation
localized = {'en': {'summary': 'newfoo', 'description': 'newbar', 'video': 'bla'}}
self.app._update_translations(localized) # pylint: disable=protected-access
# assert that translation has been updated
with translation.override('en'):
self.assertEqual(localized['en']['summary'], self.app.summary)
self.assertEqual(localized['en']['description'], self.app.description)
def test_update_translations_lowercase_language_code(self):
# update remote app translation with a new one
localized = {'en-US': {'summary': 'foo', 'description': 'bar', 'featureGraphic': 'test'}}
self.app._update_translations(localized) # pylint: disable=protected-access
with translation.override('en'):
# assert that translation has been saved with an all lower-case language code
self.assertEqual(localized['en-US']['summary'], self.app.summary)
self.assertEqual(localized['en-US']['description'], self.app.description)
# assert that language_code in URL was not changed
self.assertEqual('http://repo_url/org.example/en-US/test', self.app.feature_graphic_url)
def test_apply_translation(self):
# apply new translation
new_translation = {'summary': 'test1', 'description': 'test2',
'featureGraphic': 'feature.png', 'icon': 'icon.png',
'tvBanner': 'tv.png'}
self.app.translate('de')
with translation.override('de'):
self.app.apply_translation('de', new_translation)
# assert that translation has been saved
self.assertEqual(new_translation['summary'], self.app.summary)
self.assertEqual(new_translation['description'], self.app.description)
self.assertEqual('http://repo_url/org.example/de/feature.png',
self.app.feature_graphic_url)
self.assertEqual('http://repo_url/org.example/de/icon.png', self.app.high_res_icon_url)
self.assertEqual('http://repo_url/org.example/de/tv.png', self.app.tv_banner_url)
def test_apply_translation_sanitation(self):
# apply new translation
de_translation = {'summary': 'foo', 'description': 'test2<script>'}
self.app.translate('de')
with translation.override('de'):
self.app.apply_translation('de', de_translation)
# assert that translation has no <script> tag
self.assertEqual(de_translation['summary'], self.app.summary)
self.assertEqual('test2', self.app.description)
def test_update_screenshots(self):
self.assertEqual(0, RemoteScreenshot.objects.all().count())
# update remote app screenshots with two new ones
localized = {'en': {'summary': 'foo', PHONE: ['test1', 'test2']}}
self.app._update_screenshots(localized) # pylint: disable=protected-access
# assert that all screenshots have been saved properly
screenshots = RemoteScreenshot.objects.all()
self.assertEqual(2, screenshots.count())
self.assertEqual(PHONE, screenshots[0].type)
self.assertEqual(PHONE, screenshots[1].type)
self.assertEqual('en', screenshots[0].language_code)
self.assertEqual('en', screenshots[1].language_code)
self.assertEqual(self.app, screenshots[0].app)
self.assertEqual(self.app, screenshots[1].app)
self.assertEqual('http://repo_url/org.example/en/phoneScreenshots/test1',
screenshots[0].url)
self.assertEqual('http://repo_url/org.example/en/phoneScreenshots/test2',
screenshots[1].url)
def test_update_screenshots_lowercase_language_code(self):
# update remote app screenshots with two new ones
localized = {'en-US': {'summary': 'foo', PHONE: ['test1', 'test2']}}
self.app._update_screenshots(localized) # pylint: disable=protected-access
# assert that all screenshots have been saved properly
screenshots = RemoteScreenshot.objects.all()
self.assertEqual(2, screenshots.count())
# language code saved in lower-case
self.assertEqual('en-us', screenshots[0].language_code)
self.assertEqual('en-us', screenshots[1].language_code)
# language code in URL left unchanged
self.assertEqual('http://repo_url/org.example/en-US/phoneScreenshots/test1',
screenshots[0].url)
self.assertEqual('http://repo_url/org.example/en-US/phoneScreenshots/test2',
screenshots[1].url)
@patch('repomaker.tasks.download_remote_screenshot')
@patch('repomaker.tasks.download_remote_graphic_assets')
@patch('repomaker.models.apk.Apk.download_async')
def test_add_to_repo(self, download_async, download_remote_graphic_assets,
download_remote_screenshot):
# create pre-requisites
self.app.icon.save('test.png', io.BytesIO(b'foo'))
repo = Repository.objects.create(name='test', user=User.objects.create(username='user2'))
apk = Apk.objects.create(package_id='org.example')
remote_apk_pointer = RemoteApkPointer.objects.create(apk=apk, app=self.app, url='test_url')
remote_screenshot = RemoteScreenshot.objects.create(app=self.app)
# add remote app to local repo
app = self.app.add_to_repo(repo)
# assert that a local App has been created (details should be checked in other tests)
apps = App.objects.all()
self.assertEqual(1, apps.count())
self.assertEqual(app, apps[0])
self.assertTrue(app.icon.name.endswith('repo/icons-640/test.png'))
# assert that local ApkPointer got created properly
apk_pointers = ApkPointer.objects.all()
self.assertEqual(1, apk_pointers.count())
apk_pointer = apk_pointers[0]
self.assertEqual(apk, apk_pointer.apk)
# assert that all asynchronous tasks have been scheduled
download_async.assert_called_once_with(remote_apk_pointer.url)
download_remote_graphic_assets.assert_called_once_with(app.id, self.app.id)
download_remote_screenshot.assert_called_once_with(remote_screenshot.pk, self.app.pk)
def test_add_to_repo_without_apks(self):
# create pre-requisites
repo = Repository.objects.create(name='test', user=User.objects.create(username='user2'))
# try to add remote app without any APKs to local repo
with self.assertRaises(ValidationError):
self.app.add_to_repo(repo)