# coding: utf-8
import io
import six
import urllib.parse
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.contrib.contenttypes.models import ContentType
from django.core.files.base import ContentFile
from django.test import RequestFactory
from django.test.utils import override_settings
from django.urls import reverse
from oioioi.base.tests import TestCase, check_not_accessible
from oioioi.contests.current_contest import ContestMode
from oioioi.contests.models import Contest, ProblemInstance
from oioioi.filetracker.tests import TestStreamingMixin
from oioioi.problems.models import (
AlgorithmTag,
AlgorithmTagThrough,
DifficultyTag,
DifficultyTagThrough,
Problem,
ProblemAttachment,
ProblemPackage,
ProblemStatement,
)
from oioioi.problems.problem_site import problem_site_tab
from oioioi.problems.tests.utilities import AssertContainsOnlyMixin, get_test_filename
[docs]class TestProblemViews(TestCase, TestStreamingMixin):
[docs] fixtures = [
'test_users',
'test_contest',
'test_full_package',
'test_problem_instance',
'test_permissions',
]
[docs] def post_package_file(self, url, filename, visibility=Problem.VISIBILITY_PRIVATE):
return self.client.post(
url,
{
'package_file': open(filename, 'rb'),
'visibility': visibility,
},
follow=True,
)
[docs] def test_problem_statement_view(self):
# superuser
self.assertTrue(self.client.login(username='test_admin'))
statement = ProblemStatement.objects.get()
self.client.get('/c/c/') # 'c' becomes the current contest
url = reverse('show_statement', kwargs={'statement_id': statement.id})
response = self.client.get(url)
content = self.streamingContent(response)
self.assertTrue(content.startswith(b'%PDF'))
# contest admin
self.assertTrue(self.client.login(username='test_contest_admin'))
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
content = self.streamingContent(response)
self.assertTrue(content.startswith(b'%PDF'))
self.assertTrue(self.client.login(username='test_user'))
response = self.client.get(url)
self.assertIn(response.status_code, (403, 404))
[docs] def test_admin_changelist_view(self):
self.assertTrue(self.client.login(username='test_admin'))
self.client.get('/c/c/') # 'c' becomes the current contest
url = reverse('oioioiadmin:problems_problem_changelist')
response = self.client.get(url)
self.assertContains(response, 'Sum')
self.assertTrue(self.client.login(username='test_user'))
check_not_accessible(self, url)
user = User.objects.get(username='test_user')
content_type = ContentType.objects.get_for_model(Problem)
permission = Permission.objects.get(
content_type=content_type, codename='problems_db_admin'
)
user.user_permissions.add(permission)
response = self.client.get(url)
self.assertContains(response, 'Sum')
[docs] def test_admin_change_view(self):
self.assertTrue(self.client.login(username='test_admin'))
problem = Problem.objects.get()
self.client.get('/c/c/') # 'c' becomes the current contest
url = reverse('oioioiadmin:problems_problem_change', args=(problem.id,))
response = self.client.get(url)
elements_to_find = ['Sum', 'sum']
for element in elements_to_find:
self.assertContains(response, element)
[docs] def test_admin_delete_view_basic(self):
self.assertTrue(self.client.login(username='test_admin'))
problem = Problem.objects.get()
self.client.get('/c/c/') # 'c' becomes the current contest
url = reverse('oioioiadmin:problems_problem_delete', args=(problem.id,))
response = self.client.post(url, {'post': 'yes'}, follow=True)
self.assertEqual(Problem.objects.count(), 0)
self.assertEqual(response.status_code, 200)
[docs] def test_admin_add_in_contest_delete_in_problemset(self):
self.assertTrue(self.client.login(username='test_admin'))
ProblemInstance.objects.all().delete()
contest = Contest.objects.get()
filename = get_test_filename('test_full_package.tgz')
self.client.get('/c/c/') # 'c' becomes the current contest
url = reverse('admin:problems_problem_add')
response = self.client.get(url, {'contest_id': contest.id}, follow=True)
url = response.redirect_chain[-1][0]
self.assertEqual(response.status_code, 200)
self.assertIn(
'problems/add-or-update.html',
[getattr(t, 'name', None) for t in response.templates],
)
response = self.post_package_file(url, filename)
self.assertEqual(response.status_code, 200)
self.assertEqual(Problem.objects.count(), 1)
problem = Problem.objects.get()
url = reverse('oioioiadmin:problems_problem_delete', args=(problem.id,),)
response = self.client.post(url, {'post': 'yes'}, follow=True,)
self.assertEqual(response.status_code, 200)
self.assertEqual(Problem.objects.count(), 0)
[docs] def test_admin_add_to_contest_delete_in_problemset(self):
self.assertTrue(self.client.login(username='test_admin'))
url = reverse('problemset_add_to_contest', kwargs={'site_key': '123'})
url += '?problem_name=sum'
response = self.client.get(url, follow=True)
self.assertEqual(response.status_code, 200)
problem = Problem.objects.get()
self.assertEqual(Problem.objects.count(), 1)
url = reverse('oioioiadmin:problems_problem_delete', args=(problem.id,),)
response = self.client.post(url, {'post': 'yes'}, follow=True,)
self.assertEqual(Problem.objects.count(), 0)
self.assertEqual(response.status_code, 200)
[docs] def _test_problem_permissions(self):
problem = Problem.objects.get()
contest = Contest.objects.get()
statement = ProblemStatement.objects.get()
with io.open(__file__, 'rb') as f:
file = six.ensure_text(f.read())
check_not_accessible(
self,
'oioioiadmin:problems_problem_add',
data={'package_file': file, 'contest_id': contest.id},
)
check_not_accessible(
self,
'add_or_update_problem',
kwargs={'contest_id': contest.id},
qs={'problem': problem.id},
)
check_not_accessible(
self, 'oioioiadmin:problems_problem_download', args=(problem.id,)
)
check_not_accessible(
self, 'oioioiadmin:problems_problem_change', args=(problem.id,)
)
check_not_accessible(
self, 'oioioiadmin:problems_problem_delete', args=(problem.id,)
)
check_not_accessible(
self, 'show_statement', kwargs={'statement_id': statement.id}
)
[docs] def test_problem_permissions(self):
self._test_problem_permissions()
self.assertTrue(self.client.login(username='test_user'))
self._test_problem_permissions()
[docs]class TestProblemPackageAdminView(TestCase):
[docs] fixtures = [
'test_users',
'test_contest',
'test_problem_packages',
'test_problem_instance',
'test_two_empty_contests',
]
[docs] def test_links(self):
self.assertTrue(self.client.login(username='test_admin'))
self.client.get('/c/c/') # 'c' becomes the current contest
url = reverse('oioioiadmin:problems_problempackage_changelist')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Error details')
self.assertContains(response, 'Edit problem')
self.assertContains(response, 'Model solutions')
self.client.get('/c/c1/') # 'c1' becomes the current contest
url = reverse('oioioiadmin:problems_problempackage_changelist')
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'Error details')
# Not visible, because the problem's contest is 'c', not 'c1'
self.assertNotContains(response, 'Edit problem')
# Not visible, because the problem instances's contest is 'c', not 'c1'
self.assertNotContains(response, 'Model solutions')
[docs] def test_problem_info_brace(self):
self.assertTrue(self.client.login(username='test_admin'))
self.client.get('/c/c/') # 'c' becomes the current contest
url = reverse('oioioiadmin:problems_problempackage_changelist')
bad_package = ProblemPackage(status="ERR", info="foo } bar")
bad_package.save()
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
[docs]class TestProblemPackageViews(TestCase, TestStreamingMixin):
[docs] fixtures = [
'test_users',
'test_contest',
'test_problem_packages',
'test_problem_instance',
]
[docs] def _test_package_permissions(self, is_admin=False):
models = ['problempackage', 'contestproblempackage']
view_prefix = 'oioioiadmin:problems_'
package = ProblemPackage.objects.get(pk=2)
for m in models:
prefix = view_prefix + m + '_'
check_not_accessible(self, prefix + 'add')
check_not_accessible(self, prefix + 'change', args=(package.id,))
if not is_admin:
check_not_accessible(self, prefix + 'delete', args=(package.id,))
if not is_admin:
check_not_accessible(self, 'download_package', args=(package.id,))
check_not_accessible(
self,
'download_package_traceback',
kwargs={'package_id': str(package.id)},
)
[docs] def test_admin_changelist_view(self):
self.assertTrue(self.client.login(username='test_admin'))
self.client.get('/c/c/') # 'c' becomes the current contest
url = reverse('oioioiadmin:problems_problempackage_changelist')
response = self.client.get(url)
self.assertContains(response, 'XYZ')
[docs] def test_package_file_view(self):
package = ProblemPackage.objects.get(pk=1)
package.package_file = ContentFile(b'eloziom', name='foo')
package.save()
self.assertTrue(self.client.login(username='test_admin'))
self.client.get('/c/c/') # 'c' becomes the current contest
url = reverse('download_package', kwargs={'package_id': str(package.id)})
response = self.client.get(url)
content = self.streamingContent(response)
self.assertEqual(content, b'eloziom')
[docs] def test_package_traceback_view(self):
package = ProblemPackage.objects.get(pk=2)
package.traceback = ContentFile(b'eloziom', name='foo')
package.save()
self.assertTrue(self.client.login(username='test_admin'))
self.client.get('/c/c/') # 'c' becomes the current contest
download_package_traceback_url = reverse(
'download_package_traceback', kwargs={'package_id': str(package.id)}
)
response = self.client.get(download_package_traceback_url)
content = self.streamingContent(response)
self.assertEqual(content, b'eloziom')
package.traceback = None
package.save()
self.assertTrue(self.client.login(username='test_admin'))
response = self.client.get(download_package_traceback_url)
self.assertEqual(response.status_code, 404)
[docs] def test_package_permissions(self):
self._test_package_permissions()
self.assertTrue(self.client.login(username='test_user'))
self._test_package_permissions()
self.assertTrue(self.client.login(username='test_admin'))
self._test_package_permissions(is_admin=True)
@override_settings(CONTEST_MODE=ContestMode.neutral)
[docs]class TestProblemSite(TestCase, TestStreamingMixin):
[docs] fixtures = [
'test_users',
'test_contest',
'test_full_package',
'test_problem_instance_with_no_contest',
'test_submission',
'test_problem_site',
'test_algorithm_tags',
'test_difficulty_tags',
'test_proposals',
]
[docs] def setUp(self):
problem = Problem.objects.get(id=1)
AlgorithmTagThrough.objects.create(
problem=problem,
tag=AlgorithmTag.objects.get(name='dp'),
)
AlgorithmTagThrough.objects.create(
problem=problem,
tag=AlgorithmTag.objects.get(name='lcis'),
)
DifficultyTagThrough.objects.create(
problem=problem,
tag=DifficultyTag.objects.get(name='hard'),
)
[docs] def _get_site_urls(self):
url = reverse('problem_site', kwargs={'site_key': '123'})
url_statement = url + "?key=statement"
url_submissions = url + "?key=submissions"
return {'site': url, 'statement': url_statement, 'submissions': url_submissions}
[docs] def _create_PA(self):
problem = Problem.objects.get()
pa = ProblemAttachment(
problem=problem,
description='problem-attachment',
content=ContentFile(b'content-of-probatt', name='probatt.txt'),
)
pa.save()
[docs] def test_default_tabs(self):
urls = self._get_site_urls()
response = self.client.get(urls['site'])
self.assertRedirects(response, urls['statement'])
response = self.client.get(urls['statement'])
for url in urls.values():
self.assertContains(response, url)
[docs] def test_statement_tab(self):
url_external_stmt = reverse(
'problem_site_external_statement', kwargs={'site_key': '123'}
)
response = self.client.get(self._get_site_urls()['statement'])
self.assertEqual(response.status_code, 200)
self.assertContains(response, url_external_stmt)
[docs] def test_submissions_tab(self):
for problem in Problem.objects.all():
problem.main_problem_instance.contest = None
problem.main_problem_instance.round = None
problem.main_problem_instance.save()
url = self._get_site_urls()['submissions']
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertNotContains(response, '<tr')
self.assertTrue(self.client.login(username='test_user'))
self.assertEqual(response.status_code, 200)
response = self.client.get(url)
self.assertContains(response, '<tr', count=3)
[docs] def test_submit_tab(self):
url = reverse('problem_site', kwargs={'site_key': '123'}) + '?key=submit'
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTrue(self.client.login(username='test_user'))
self.assertEqual(response.status_code, 200)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
@override_settings(LANGUAGE_CODE='en')
[docs] def test_settings_tab(self):
problemsite_url = self._get_site_urls()['statement']
url = reverse('problem_site', kwargs={'site_key': '123'}) + '?key=settings'
response = self.client.get(problemsite_url)
self.assertNotContains(response, 'Settings')
self.assertTrue(self.client.login(username='test_admin'))
response = self.client.get(problemsite_url)
self.assertContains(response, 'Settings')
response = self.client.get(url)
self.assertContains(response, 'Add to contest')
self.assertContains(response, 'Current tags')
self.assertContains(response, 'Edit problem')
self.assertContains(response, 'Edit tests')
self.assertContains(response, 'Reupload problem')
self.assertContains(response, 'Model solutions')
self.assertContains(response, 'dp')
self.assertContains(response, 'lcis')
self.assertContains(response, 'Medium')
[docs] def test_statement_replacement(self):
url = (
reverse('problem_site', kwargs={'site_key': '123'})
+ '?key=replace_problem_statement'
)
self.assertTrue(self.client.login(username='test_user'))
response = self.client.get(url)
self.assertNotEqual(response.status_code, 200)
self.assertTrue(self.client.login(username='test_admin'))
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'sum_7.pdf')
new_statement_filename = get_test_filename('blank.pdf')
response = self.client.post(
url,
{
'file_name': 'sum_7.pdf',
'file_replacement': open(new_statement_filename, 'rb'),
},
follow=True,
)
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'blank.pdf')
self.assertNotContains(response, 'sum_7.pdf')
problem = Problem.objects.get(pk=1)
statement = ProblemStatement.objects.get(problem=problem)
url = reverse('show_statement', kwargs={'statement_id': statement.id})
response = self.client.get(url)
content = self.streamingContent(response)
self.assertEqual(content, open(new_statement_filename, 'rb').read())
[docs] def test_add_new_tab(self):
tab_title = 'Test tab'
tab_contents = 'Hello from test tab'
@problem_site_tab(tab_title, key='testtab')
def problem_site_test(request, problem):
return tab_contents
url = self._get_site_urls()['site'] + '?key=testtab'
response = self.client.get(url)
self.assertContains(response, tab_title)
self.assertContains(response, tab_contents)
[docs] def test_external_statement_view(self):
url_external_stmt = reverse(
'problem_site_external_statement', kwargs={'site_key': '123'}
)
response = self.client.get(url_external_stmt)
self.assertEqual(response.status_code, 200)
content = self.streamingContent(response)
self.assertTrue(content.startswith(b'%PDF'))
[docs] def test_external_attachment_view(self):
self._create_PA()
url_external_attmt = reverse(
'problem_site_external_attachment',
kwargs={'site_key': '123', 'attachment_id': 1},
)
response = self.client.get(url_external_attmt)
self.assertStreamingEqual(response, b'content-of-probatt')
@override_settings(LANGUAGE_CODE="en")
[docs]def get_submission_left(username, contest_id='c', pi_pk=1):
request = RequestFactory().request()
request.user = (
User.objects.get(username=username) if username is not None else AnonymousUser()
)
if contest_id is not None:
request.contest = Contest.objects.get(id=contest_id)
problem_instance = ProblemInstance.objects.get(pk=pi_pk)
return problem_instance.controller.get_submissions_left(request, problem_instance)
[docs]class TestSubmissionLeft(TestCase):
[docs] fixtures = [
'test_users',
'test_contest',
'test_full_package',
'test_problem_instance',
'test_submission',
]
[docs] def test_admin(self):
assert get_submission_left('test_admin') is None
[docs] def test_user_without_submissions(self):
assert get_submission_left('test_user2') == 10
[docs] def test_user_with_submissions(self):
assert get_submission_left('test_user') == 9
[docs] def test_not_authenticated_user(self):
assert get_submission_left(None) is None
[docs]class TestSubmissionLeftWhenNoLimit(TestCase):
[docs] fixtures = [
'test_users',
'test_contest',
'test_full_package',
'test_problem_instance_with_no_submissions_limit',
'test_submission',
]
[docs] def test_admin(self):
assert get_submission_left('test_admin') is None
[docs] def test_user_without_submissions(self):
assert get_submission_left('test_user2') is None
[docs] def test_user_with_submissions(self):
assert get_submission_left('test_user') is None
[docs] def test_not_authenticated_user(self):
assert get_submission_left(None) is None
[docs]class TestSubmissionLeftWhenNoContest(TestCase):
[docs] fixtures = [
'test_users',
'test_full_package',
'test_problem_instance_with_no_contest',
]
[docs] def test_admin(self):
assert get_submission_left('test_admin', None) is None
[docs] def test_user_without_submissions(self):
assert get_submission_left('test_user', None) is None
[docs] def test_not_authenticated_user(self):
assert get_submission_left(None, None) is None
[docs]class TestProblemSearchPermissions(TestCase, AssertContainsOnlyMixin):
[docs] fixtures = ['test_users', 'test_problem_search_permissions']
[docs] url = reverse('problemset_main')
task_names = all_values = [
'Task Public',
'Task User1Public',
'Task User1Private',
'Task Private',
]
[docs] def test_search_permissions_public(self):
self.client.get('/c/c/')
response = self.client.get(self.url, {'q': 'Task'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ['Task Public', 'Task User1Public'])
for user in ['test_user', 'test_user2', 'test_admin']:
self.assertTrue(self.client.login(username=user))
response = self.client.get(self.url, {'q': 'Task'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ['Task Public', 'Task User1Public'])
[docs] def test_search_permissions_my(self):
self.assertTrue(self.client.login(username='test_admin'))
response = self.client.get(self.url + 'myproblems', {'q': 'Task'}, follow=True)
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, [])
self.assertTrue(self.client.login(username='test_user'))
response = self.client.get(self.url + 'myproblems', {'q': 'Task'}, follow=True)
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ['Task User1Public', 'Task User1Private'])
self.assertTrue(self.client.login(username='test_user2'))
response = self.client.get(self.url + 'myproblems', {'q': 'Task'}, follow=True)
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, [])
[docs] def test_search_permissions_all(self):
self.client.get('/c/c/')
self.assertTrue(self.client.login(username='test_user'))
response = self.client.get(
self.url + 'all_problems', {'q': 'Task'}, follow=True
)
self.assertEqual(response.status_code, 403)
hints_url = reverse('get_search_hints', args=('all',))
response = self.client.get(hints_url, {'q': 'Task'})
self.assertEqual(response.status_code, 403)
self.assertTrue(self.client.login(username='test_admin'))
response = self.client.get(
self.url + 'all_problems', {'q': 'Task'}, follow=True
)
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, self.task_names)
response = self.client.get(hints_url, {'q': 'Task'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, self.task_names)
[docs]class TestProblemSearch(TestCase, AssertContainsOnlyMixin):
[docs] fixtures = ['test_problem_search']
[docs] url = reverse('problemset_main')
task_names = all_values = [
'Prywatne',
'Zadanko',
'Żółć',
'Znacznik',
'Algorytm',
'Trudność',
'Bajtocja',
'Byteland',
'Drzewo',
'Tree',
]
[docs] def test_search_name(self):
self.client.get('/c/c/')
response = self.client.get(self.url, {'q': 'Zadanko'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ('Zadanko',))
response = self.client.get(self.url, {'q': 'zADaNkO'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ('Zadanko',))
response = self.client.get(self.url, {'q': 'zadan'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ('Zadanko',))
[docs] def test_search_name_unicode(self):
self.client.get('/c/c/')
response = self.client.get(self.url, {'q': 'Żółć'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ('Żółć',))
response = self.client.get(self.url, {'q': 'żółć'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ('Żółć',))
response = self.client.get(self.url, {'q': 'Zolc'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ('Żółć',))
response = self.client.get(self.url, {'q': 'żoŁc'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ('Żółć',))
response = self.client.get(self.url, {'q': 'olc'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ('Żółć',))
[docs] def test_search_name_multiple(self):
self.client.get('/c/c/')
response = self.client.get(self.url, {'q': 'a'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(
response, ('Zadanko', 'Znacznik', 'Algorytm', 'Byteland')
)
[docs] def _test_search_name_localized(self, queries, exp_names):
self.client.get('/c/c/')
for query, exp_name in zip(queries, exp_names):
response = self.client.get(self.url, {'q': query})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, (exp_name,))
@override_settings(LANGUAGE_CODE="en")
[docs] def test_search_name_localized_en(self):
queries = 'Byte', 'land', 'bajtocj', 'TrEE', 'RzEw'
exp_names = 'Byteland', 'Byteland', 'Byteland', 'Tree', 'Tree'
self._test_search_name_localized(queries, exp_names)
@override_settings(LANGUAGE_CODE="pl")
[docs] def test_search_name_localized_pl(self):
queries = 'Bajto', 'baJtOcJ', 'byteland', 'Drzewo', 'tree'
exp_names = 'Bajtocja', 'Bajtocja', 'Bajtocja', 'Drzewo', 'Drzewo'
self._test_search_name_localized(queries, exp_names)
[docs] def test_search_short_name(self):
self.client.get('/c/c/')
response = self.client.get(self.url, {'q': 'zad'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ('Zadanko',))
response = self.client.get(self.url, {'q': 'zol'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ('Żółć',))
[docs] def test_search_short_name_multiple(self):
self.client.get('/c/c/')
response = self.client.get(self.url, {'q': '1'})
self.assertEqual(response.status_code, 200)
self.assert_contains_only(response, ('Zadanko', 'Żółć', 'Znacznik'))