diff --git a/tests/base_test_mixins.py b/tests/base_test_mixins.py index 95594e336df798be8ba1c4323d0f390ffe0f97f7..b62ebb6327e75dfafeada824cef2e0e3917ead7a 100644 --- a/tests/base_test_mixins.py +++ b/tests/base_test_mixins.py @@ -23,20 +23,19 @@ THE SOFTWARE. """ import six +import tempfile import os import datetime -from django.conf import settings -from django.test import Client, override_settings +from copy import deepcopy +from django.test import Client, override_settings, mock from django.urls import reverse, resolve from django.contrib.auth import get_user_model from django.core.exceptions import ImproperlyConfigured -from relate.utils import force_remove_path from course.models import ( Course, Participation, ParticipationRole, FlowSession, FlowPageData, FlowPageVisit) from course.constants import participation_status, user_status -from .utils import mock CREATE_SUPERUSER_KWARGS = { "username": "test_admin", @@ -98,6 +97,10 @@ SINGLE_COURSE_SETUP_LIST = [ } ] +TWO_COURSE_SETUP_LIST = deepcopy(SINGLE_COURSE_SETUP_LIST) +TWO_COURSE_SETUP_LIST[0]["course"]["identifier"] = "test-course1" +TWO_COURSE_SETUP_LIST += deepcopy(SINGLE_COURSE_SETUP_LIST) +TWO_COURSE_SETUP_LIST[1]["course"]["identifier"] = "test-course2" NONE_PARTICIPATION_USER_CREATE_KWARG_LIST = [ { @@ -143,6 +146,10 @@ NONE_PARTICIPATION_USER_CREATE_KWARG_LIST = [ ] +class CourseCreateFailure(Exception): + pass + + class ResponseContextMixin(object): """ Response context refers to "the template Context instance that was used @@ -248,6 +255,9 @@ class SuperuserCreateMixin(ResponseContextMixin): # create user, course and participation. cls.superuser = cls.create_superuser() cls.c = Client() + cls.settings_git_root_override = ( + override_settings(GIT_ROOT=tempfile.mkdtemp())) + cls.settings_git_root_override.enable() super(SuperuserCreateMixin, cls).setUpTestData() @classmethod @@ -362,6 +372,7 @@ class CoursesTestMixinBase(SuperuserCreateMixin): courses_setup_list = [] none_participation_user_create_kwarg_list = [] courses_attributes_extra_list = None + override_settings_at_post_create_course = {} @classmethod def setUpTestData(cls): # noqa @@ -380,13 +391,16 @@ class CoursesTestMixinBase(SuperuserCreateMixin): cls.n_courses += 1 course_identifier = course_setup["course"]["identifier"] - cls.remove_exceptionally_undelete_course_repos(course_identifier) course_setup_kwargs = course_setup["course"] if cls.courses_attributes_extra_list: extra_attrs = cls.courses_attributes_extra_list[i] assert isinstance(extra_attrs, dict) course_setup_kwargs.update(extra_attrs) - cls.create_course(**course_setup_kwargs) + try: + cls.post_create_course(**course_setup_kwargs) + except Exception: + raise + course = Course.objects.get(identifier=course_identifier) if "participations" in course_setup: for participation in course_setup["participations"]: @@ -426,33 +440,9 @@ class CoursesTestMixinBase(SuperuserCreateMixin): cls.course_qset = Course.objects.all() - @classmethod - def remove_exceptionally_undelete_course_repos(cls, course_identifier): - """ - Remove existing course repo folders resulted in unexpected - exceptions in previous tests. - """ - repo_path = os.path.join(settings.GIT_ROOT, course_identifier) - try: - force_remove_path(repo_path) - except OSError: - if not os.path.isdir(repo_path): - # The repo path does not exist, that's good! - return - raise - - @classmethod - def remove_course_repo(cls, course): - from course.content import get_course_repo_path - repo_path = get_course_repo_path(course) - force_remove_path(repo_path) - @classmethod def tearDownClass(cls): cls.c.logout() - # Remove repo folder for all courses - for course in Course.objects.all(): - cls.remove_course_repo(course) super(CoursesTestMixinBase, cls).tearDownClass() @classmethod @@ -494,9 +484,36 @@ class CoursesTestMixinBase(SuperuserCreateMixin): return participation @classmethod - def create_course(cls, **create_course_kwargs): + def post_create_course(cls, raise_error=True, **create_course_kwargs): cls.c.force_login(cls.superuser) - cls.c.post(reverse("relate-set_up_new_course"), create_course_kwargs) + existing_course_count = Course.objects.count() + with override_settings(**cls.override_settings_at_post_create_course): + resp = cls.c.post( + reverse("relate-set_up_new_course"), create_course_kwargs) + if raise_error: + if not Course.objects.count() == existing_course_count + 1: + error_string = None + # most probably the reason course creation form error + form_context = resp.context.__getitem__("form") + assert form_context + error_list = [] + if form_context.errors: + error_list = [ + "%s: %s" + % (field, + "\n".join(["%s:%s" % (type(e).__name__, str(e)) + for e in errs])) + for field, errs + in six.iteritems(form_context.errors.as_data())] + non_field_errors = form_context.non_field_errors() + if non_field_errors: + error_list.append(repr(non_field_errors)) + if error_list: + error_string = "\n".join(error_list) + if not error_string: + error_string = ("course creation failed for unknown errors") + raise CourseCreateFailure(error_string) + return resp @classmethod def get_course_page_url(cls, course_identifier=None): @@ -549,11 +566,19 @@ class CoursesTestMixinBase(SuperuserCreateMixin): return ClientUserSwitcher(switch_to) + @classmethod + def get_default_course(cls): + if Course.objects.count() > 1: + raise AttributeError( + "'course' arg can not be omitted for " + "testcases with more than one courses") + raise NotImplementedError + @classmethod def get_default_course_identifier(cls): if Course.objects.count() > 1: raise AttributeError( - "course_identifier can not be omitted for " + "'course_identifier' arg can not be omitted for " "testcases with more than one courses") raise NotImplementedError @@ -579,6 +604,17 @@ class CoursesTestMixinBase(SuperuserCreateMixin): status=participation_status.active ).first().user + @classmethod + def update_course_attribute(cls, attrs, course=None): + # course instead of course_identifier because we need to do + # refresh_from_db + assert isinstance(attrs, dict) + course = course or cls.get_default_course() + if attrs: + course.__dict__.update(attrs) + course.save() + course.refresh_from_db() + @classmethod def start_flow(cls, flow_id, course_identifier=None): """ @@ -824,15 +860,15 @@ class CoursesTestMixinBase(SuperuserCreateMixin): course_identifier = self.get_default_course_identifier() return reverse("relate-update_course", args=[course_identifier]) - def update_course_content(self, commit_sha, - fetch_update=False, - prevent_discarding_revisions=True, - force_login_instructor=True, - course_identifier=None, - ): - - if course_identifier is None: - course_identifier = self.get_default_course_identifier() + def post_update_course_content(self, commit_sha, + fetch_update=False, + prevent_discarding_revisions=True, + force_login_instructor=True, + course=None, + ): + # course instead of course_identifier because we need to do + # refresh_from_db + course = course or self.get_default_course() try: commit_sha = commit_sha.decode() @@ -851,13 +887,12 @@ class CoursesTestMixinBase(SuperuserCreateMixin): force_login_user = None if force_login_instructor: - force_login_user = self.get_default_instructor_user(course_identifier) + force_login_user = self.get_default_instructor_user(course.identifier) with self.temporarily_switch_to_user(force_login_user): response = self.c.post( - self.get_update_course_url(course_identifier), data) - updated_course = Course.objects.get(identifier=course_identifier) - updated_course.refresh_from_db() + self.get_update_course_url(course.identifier), data) + course.refresh_from_db() return response @@ -937,9 +972,13 @@ class SingleCourseTestMixin(CoursesTestMixinBase): self.student_participation.refresh_from_db() self.ta_participation.refresh_from_db() + @classmethod + def get_default_course(cls): + return cls.course + @classmethod def get_default_course_identifier(cls): - return cls.course.identifier + return cls.get_default_course().identifier class TwoCourseTestMixin(CoursesTestMixinBase): diff --git a/tests/test_auth.py b/tests/test_auth.py index da4871988a00b6b5002d85846664bc8a93ce3080..8426636fec649e46abd291852c95d53b105fbe4a 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -26,11 +26,10 @@ from django.test import TestCase from django.contrib import messages from course.auth import get_impersonable_user_qset from course.models import FlowPageVisit, ParticipationPermission -from copy import deepcopy from .base_test_mixins import ( SingleCoursePageTestMixin, TwoCourseTestMixin, - FallBackStorageMessageTestMixin, SINGLE_COURSE_SETUP_LIST, + FallBackStorageMessageTestMixin, TWO_COURSE_SETUP_LIST, NONE_PARTICIPATION_USER_CREATE_KWARG_LIST) NOT_IMPERSONATING_MESSAGE = "Not currently impersonating anyone." @@ -41,11 +40,6 @@ IMPERSONATE_FORM_ERROR_NOT_VALID_USER_MSG = ( "Select a valid choice. That choice is " "not one of the available choices.") -TWO_COURSE_SETUP_LIST = deepcopy(SINGLE_COURSE_SETUP_LIST) -TWO_COURSE_SETUP_LIST[0]["course"]["identifier"] = "test-course1" -TWO_COURSE_SETUP_LIST += deepcopy(SINGLE_COURSE_SETUP_LIST) -TWO_COURSE_SETUP_LIST[1]["course"]["identifier"] = "test-course2" - class ImpersonateTest(SingleCoursePageTestMixin, FallBackStorageMessageTestMixin, TestCase): diff --git a/tests/test_content.py b/tests/test_content.py index 449562f2a73e8c279ebd287ecccd19028263a275..73095b381a0e5dff6b086d7a559637cdb0490767 100644 --- a/tests/test_content.py +++ b/tests/test_content.py @@ -22,11 +22,9 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -from django.test import TestCase +from django.test import TestCase, mock from tests.base_test_mixins import ( - improperly_configured_cache_patch, SingleCoursePageTestMixin, - mock -) + improperly_configured_cache_patch, SingleCoursePageTestMixin) from .test_pages import QUIZ_FLOW_ID diff --git a/tests/test_enrollment.py b/tests/test_enrollment.py index 348bba100b2221780d90fa98e590ef7283cdefc7..d4917d06fe8d5d1ad745096c1e052ee01432ac1c 100644 --- a/tests/test_enrollment.py +++ b/tests/test_enrollment.py @@ -22,7 +22,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -from django.test import TestCase +from django.test import TestCase, mock from django.conf import settings from django.test.utils import override_settings from django.core import mail @@ -44,7 +44,7 @@ from .base_test_mixins import ( NONE_PARTICIPATION_USER_CREATE_KWARG_LIST, FallBackStorageMessageTestMixin ) -from .utils import LocmemBackendTestsMixin, mock +from .utils import LocmemBackendTestsMixin TEST_EMAIL_SUFFIX1 = "@suffix.com" diff --git a/tests/utils.py b/tests/utils.py index e1a1924a85c1842aca77687e4479ac817bd51386..8dfde24a1fe81034fd8253c61aa219fd4d014062 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,11 +4,6 @@ from __future__ import division from django.test import override_settings from django.core import mail -try: - from unittest import mock # noqa -except Exception: - import mock # noqa - class BaseEmailBackendTestsMixin(object): email_backend = None