diff --git a/backend/core/tests/data_factories.py b/backend/core/tests/data_factories.py index 727e4b153fbb5bfc0ff8dc6bc2c8a2070ac8a369..1d5e9b54171614c798b7ad54fefc1e92d038468a 100644 --- a/backend/core/tests/data_factories.py +++ b/backend/core/tests/data_factories.py @@ -1,3 +1,6 @@ +""" A set of factory methods that make testing easier. Each method creates all +reuired subfields if not provided by via kwargs. """ + from core.models import (UserAccount, Student, Tutor, Reviewer, ExamType, SubmissionType, Submission, Feedback) diff --git a/backend/core/tests/test_student_page.py b/backend/core/tests/test_student_page.py index 1da0bd12977118a959c7f1243a7c85b718fd8947..ceabdb69caa40e5025ef3b428c89292ba51e58bd 100644 --- a/backend/core/tests/test_student_page.py +++ b/backend/core/tests/test_student_page.py @@ -1,44 +1,89 @@ from rest_framework.test import APITestCase, APIRequestFactory, force_authenticate from rest_framework import status -from core.models import Reviewer +from core.models import Reviewer, SubmissionType from core.tests import data_factories from django.urls import reverse from core.views import StudentApiView class StudentPageTests(APITestCase): + @classmethod def setUpTestData(cls): cls.factory = APIRequestFactory() def setUp(self): - self.submission, self.tutor, self.feedback = data_factories.make_minimal_exam() + self.submission, self.tutor, self.feedback = \ + data_factories.make_minimal_exam() self.student = self.submission.student - self.reviewer = Reviewer.objects.create(user=data_factories.make_user(username='reviewer')) + self.reviewer = Reviewer.objects.create( + user=data_factories.make_user(username='reviewer')) self.request = self.factory.get(reverse('student-page')) self.view = StudentApiView.as_view() - def test_unauthorized_access_denied(self): - response = self.view(self.request) - self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) + force_authenticate(self.request, user=self.student.user) + self.response = self.view(self.request) - def test_tutor_has_no_access(self): - force_authenticate(self.request, user=self.tutor.user) - response = self.view(self.request) - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + self.exam_obj = self.response.data['exam'] + self.submission_list = self.response.data['submissions'] + self.submission_list_first_entry = self.submission_list[0] - def test_reviewer_has_no_access(self): - force_authenticate(self.request, user=self.reviewer.user) - response = self.view(self.request) - self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) + def test_student_information_contains_name(self): + self.assertEqual( + self.response.data['name'], self.student.user.fullname) - def test_student_is_authorized(self): - force_authenticate(self.request, user=self.student.user) - response = self.view(self.request) - self.assertEqual(response.status_code, status.HTTP_200_OK) + def test_student_contains_associated_user(self): + self.assertEqual( + self.response.data['user'], self.student.user.username) - def test_contains_student_information(self): - force_authenticate(self.request, user=self.student.user) - response = self.view(self.request) - self.assertEqual(response.data['name'], self.student.user.fullname) - self.assertEqual(response.data['user'], self.student.user.username) + def test_all_student_submissions_are_loded(self): + self.assertEqual(len(self.submission_list), + SubmissionType.objects.count()) + + # Tests concerning exam data + def test_exam_data_contains_module_reference(self): + self.assertEqual( + self.exam_obj["module_reference"], + self.student.exam.module_reference) + + def test_exam_data_contains_total_score(self): + self.assertEqual( + self.exam_obj["total_score"], self.student.exam.total_score) + + def test_exam_data_contains_pass_score(self): + self.assertEqual( + self.exam_obj["pass_score"], self.student.exam.pass_score) + + def test_exam_data_contains_pass_only_field(self): + self.assertEqual( + self.exam_obj["pass_only"], self.student.exam.pass_only) + + # Tests concerning submission data + def test_a_student_submissions_contains_type(self): + self.assertEqual( + self.submission_list_first_entry['type'], + self.student.submissions.first().type.name) + + def test_submission_data_contains_text(self): + self.assertEqual( + self.submission_list_first_entry['text'], + self.student.submissions.first().text) + + def test_submission_data_contains_feedback(self): + self.assertEqual( + self.submission_list_first_entry['feedback'], + self.student.submissions.first().feedback.text) + + def test_submission_data_contains_score(self): + self.assertEqual( + self.submission_list_first_entry['score'], + self.student.submissions.first().feedback.score) + + def test_submission_data_contains_full_score(self): + self.assertEqual( + self.submission_list_first_entry['full_score'], + self.student.submissions.first().type.full_score) + + # We don't want a matriculation number here + def test_matriculation_number_is_not_senf(self): + self.assertNotIn('matrikel_no', self.submission_list_first_entry) diff --git a/backend/delbert.py b/backend/delbert.py old mode 100644 new mode 100755 diff --git a/backend/util/factories.py b/backend/util/factories.py index cce45139a7b137a8350730229de477ab6faa99ad..32c2d67fc03200556e3a44015bdf9525f7ff93b0 100644 --- a/backend/util/factories.py +++ b/backend/util/factories.py @@ -9,6 +9,7 @@ REVIEWERS = 'reviewers' PASSWORDS = '.importer_passwords' + def get_xkcd_password(k=2): with open('/usr/share/dict/words') as words: choose_from = list({word.strip().lower() @@ -30,14 +31,12 @@ def store_password(username, groupname, password): storage.write(passwd_file) - - class GradyUserFactory: def __init__(self, - password_generator_func=get_xkcd_password, - password_storge=store_password, - *args, **kwargs): + password_generator_func=get_xkcd_password, + password_storge=store_password, + *args, **kwargs): self.password_generator_func = password_generator_func self.password_storge = password_storge diff --git a/backend/util/importer.py b/backend/util/importer.py index 299f4ae547c1b8e1a8a9dcd2568bf3ca29d0b407..fcb6f4b825a174774e14c25405219f2f35c4dd69 100644 --- a/backend/util/importer.py +++ b/backend/util/importer.py @@ -14,9 +14,7 @@ from core.models import (ExamType, Feedback, Reviewer, Student, Submission, from util.messages import info, warn from util.processing import EmptyTest -STUDENTS = 'students' -TUTORS = 'tutors' -REVIEWERS = 'reviewers' +from util.factories import STUDENTS, REVIEWERS, TUTORS, GradyUserFactory HISTFILE = '.importer_history' RECORDS = '.importer' @@ -62,14 +60,6 @@ class chdir_context(object): os.chdir(self.old_dir) -def get_xkcd_password(k=2): - with open('/usr/share/dict/words') as words: - choose_from = list({word.strip().lower() - for word in words if 5 < len(word) < 8}) - - return ''.join(secrets.choice(choose_from) for _ in range(k)) - - def i(prompt: str, default: str='', is_path: bool=False, is_file: bool=False): if default is YES or default is NO: answer = valid[input(f'[Q] {prompt} ({default}): ').lower() or ('y' if YES == default else 'n')] @@ -84,96 +74,6 @@ def i(prompt: str, default: str='', is_path: bool=False, is_file: bool=False): return answer - -def store_password(username, groupname, password): - storage = configparser.ConfigParser() - storage.read(PASSWORDS) - - if not groupname in storage: - storage[groupname] = {} - - storage[groupname][username] = password - - with open(PASSWORDS, 'w') as passwd_file: - storage.write(passwd_file) - - -class GradyUserFactory: - - def __init__(self, password_generator_func=get_xkcd_password, *args, **kwargs): - self.password_generator_func = password_generator_func - - @staticmethod - def _get_random_name(prefix='', suffix='', k=1): - return ''.join((prefix, get_xkcd_password(k), suffix)) - - def _make_base_user(self, username, groupname, store_pw=False, **kwargs): - """ This is a specific wrapper for the django update_or_create method of - objects. - * A new user is created and password and group are set accordingly - * If the user was there before password is NOT change but group is. A - user must only have one group. - - Returns: - (User object, str): The user object that was added to the group and - the password of that user if it was created. - """ - username = username.strip() - - user, created = User.objects.update_or_create( - username=username, - defaults=kwargs) - - if created: - password = self.password_generator_func() - user.set_password(password) - user.save() - - if created and store_pw: - store_password(username, groupname, password) - - return user - - def _get_user_model_for_group(self, groupname): - """ Returns the model class for a usergroup """ - return { - STUDENTS: Student, - TUTORS: Tutor, - REVIEWERS: Reviewer, - }[groupname] - - def _make_user_generic(self, username, groupname, **kwargs): - """ Provides a model with a associated user but without any defaults - """ - - if not username: - username = self._get_random_name(prefix=groupname.lower() + '_') - - model = self._get_user_model_for_group(groupname) - user = self._make_base_user(username, groupname, **kwargs) - - generic_user, _ = model.objects.get_or_create(user=user) - - return generic_user - - def make_student(self, username=None, matrikel_no=None, exam=None, **kwargs): - """ Creates a student. Defaults can be passed via kwargs like in - relation managers objects.update method. """ - user = self._make_user_generic(username, STUDENTS, **kwargs) - if matrikel_no: - user.objects.update(matrikel_no=matrikel_no) - if exam: - user.objects.update(exam=exam) - return user - - def make_tutor(self, username=None, **kwargs): - """ Creates or updates a tutor if needed with defaults """ - return self._make_user_generic(username, TUTORS, **kwargs) - - def make_reviewer(self, username=None, **kwargs): - """ Creates or updates a reviewer if needed with defaults """ - return self._make_user_generic(username, REVIEWERS, **kwargs) - # TODO more factories def add_user(username, group, **kwargs):