""" test utils """ from nose.plugins.attrib import attr from ccx.models import ( # pylint: disable=import-error CcxMembership, CcxFutureMembership, ) from ccx.tests.factories import ( # pylint: disable=import-error CcxFactory, CcxMembershipFactory, CcxFutureMembershipFactory, ) from student.roles import CourseCcxCoachRole # pylint: disable=import-error from student.tests.factories import ( # pylint: disable=import-error AdminFactory, UserFactory, CourseEnrollmentFactory, AnonymousUserFactory, ) from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @attr('shard_1') class TestEmailEnrollmentState(ModuleStoreTestCase): """unit tests for the EmailEnrollmentState class """ def setUp(self): """ Set up tests """ super(TestEmailEnrollmentState, self).setUp() # remove user provided by the parent test case so we can make our own # when needed. self.user = None course = CourseFactory.create() coach = AdminFactory.create() role = CourseCcxCoachRole(course.id) role.add_users(coach) self.ccx = CcxFactory(course_id=course.id, coach=coach) def create_user(self): """provide a legitimate django user for testing """ if getattr(self, 'user', None) is None: self.user = UserFactory() def register_user_in_ccx(self): """create registration of self.user in self.ccx registration will be inactive """ self.create_user() CcxMembershipFactory(ccx=self.ccx, student=self.user) def create_one(self, email=None): """Create a single EmailEnrollmentState object and return it """ from ccx.utils import EmailEnrollmentState # pylint: disable=import-error if email is None: email = self.user.email return EmailEnrollmentState(self.ccx, email) def test_enrollment_state_for_non_user(self): """verify behavior for non-user email address """ ee_state = self.create_one(email='nobody@nowhere.com') for attr in ['user', 'member', 'full_name', 'in_ccx']: value = getattr(ee_state, attr, 'missing attribute') self.assertFalse(value, "{}: {}".format(value, attr)) def test_enrollment_state_for_non_member_user(self): """verify behavior for email address of user who is not a ccx memeber """ self.create_user() ee_state = self.create_one() self.assertTrue(ee_state.user) self.assertFalse(ee_state.in_ccx) self.assertEqual(ee_state.member, self.user) self.assertEqual(ee_state.full_name, self.user.profile.name) def test_enrollment_state_for_member_user(self): """verify behavior for email address of user who is a ccx member """ self.create_user() self.register_user_in_ccx() ee_state = self.create_one() for attr in ['user', 'in_ccx']: self.assertTrue( getattr(ee_state, attr, False), "attribute {} is missing or False".format(attr) ) self.assertEqual(ee_state.member, self.user) self.assertEqual(ee_state.full_name, self.user.profile.name) def test_enrollment_state_to_dict(self): """verify dict representation of EmailEnrollmentState """ self.create_user() self.register_user_in_ccx() ee_state = self.create_one() ee_dict = ee_state.to_dict() expected = { 'user': True, 'member': self.user, 'in_ccx': True, } for expected_key, expected_value in expected.iteritems(): self.assertTrue(expected_key in ee_dict) self.assertEqual(expected_value, ee_dict[expected_key]) def test_enrollment_state_repr(self): self.create_user() self.register_user_in_ccx() ee_state = self.create_one() representation = repr(ee_state) self.assertTrue('user=True' in representation) self.assertTrue('in_ccx=True' in representation) member = 'member={}'.format(self.user) self.assertTrue(member in representation) @attr('shard_1') # TODO: deal with changes in behavior for auto_enroll class TestGetEmailParams(ModuleStoreTestCase): """tests for ccx.utils.get_email_params """ def setUp(self): """ Set up tests """ super(TestGetEmailParams, self).setUp() course = CourseFactory.create() coach = AdminFactory.create() role = CourseCcxCoachRole(course.id) role.add_users(coach) self.ccx = CcxFactory(course_id=course.id, coach=coach) self.all_keys = [ 'site_name', 'course', 'course_url', 'registration_url', 'course_about_url', 'auto_enroll' ] self.url_keys = [k for k in self.all_keys if 'url' in k] self.course_keys = [k for k in self.url_keys if 'course' in k] def call_fut(self, auto_enroll=False, secure=False): """ call function under test """ from ccx.utils import get_email_params # pylint: disable=import-error return get_email_params(self.ccx, auto_enroll, secure) def test_params_have_expected_keys(self): params = self.call_fut() self.assertFalse(set(params.keys()) - set(self.all_keys)) def test_ccx_id_in_params(self): expected_course_id = self.ccx.course_id.to_deprecated_string() params = self.call_fut() self.assertEqual(params['course'], self.ccx) for url_key in self.url_keys: self.assertTrue('http://' in params[url_key]) for url_key in self.course_keys: self.assertTrue(expected_course_id in params[url_key]) def test_security_respected(self): secure = self.call_fut(secure=True) for url_key in self.url_keys: self.assertTrue('https://' in secure[url_key]) insecure = self.call_fut(secure=False) for url_key in self.url_keys: self.assertTrue('http://' in insecure[url_key]) def test_auto_enroll_passed_correctly(self): not_auto = self.call_fut(auto_enroll=False) self.assertFalse(not_auto['auto_enroll']) auto = self.call_fut(auto_enroll=True) self.assertTrue(auto['auto_enroll']) @attr('shard_1') # TODO: deal with changes in behavior for auto_enroll class TestEnrollEmail(ModuleStoreTestCase): """tests for the enroll_email function from ccx.utils """ def setUp(self): super(TestEnrollEmail, self).setUp() # unbind the user created by the parent, so we can create our own when # needed. self.user = None course = CourseFactory.create() coach = AdminFactory.create() role = CourseCcxCoachRole(course.id) role.add_users(coach) self.ccx = CcxFactory(course_id=course.id, coach=coach) self.outbox = self.get_outbox() def create_user(self): """provide a legitimate django user for testing """ if getattr(self, 'user', None) is None: self.user = UserFactory() def register_user_in_ccx(self): """create registration of self.user in self.ccx registration will be inactive """ self.create_user() CcxMembershipFactory(ccx=self.ccx, student=self.user) def get_outbox(self): """Return the django mail outbox""" from django.core import mail return mail.outbox def check_membership(self, email=None, user=None, future=False): """Verify tjat an appropriate CCX Membership exists""" if not email and not user: self.fail( "must provide user or email address to check CCX Membership" ) if future and email: membership = CcxFutureMembership.objects.filter( ccx=self.ccx, email=email ) elif not future: if not user: user = self.user membership = CcxMembership.objects.filter( ccx=self.ccx, student=user ) self.assertTrue(membership.exists()) def check_enrollment_state(self, state, in_ccx, member, user): """Verify an enrollment state object against provided arguments state.in_ccx will always be a boolean state.user will always be a boolean state.member will be a Django user object or None """ self.assertEqual(in_ccx, state.in_ccx) self.assertEqual(member, state.member) self.assertEqual(user, state.user) def call_fut( self, student_email=None, auto_enroll=False, email_students=False, email_params=None ): """Call function under test""" from ccx.utils import enroll_email # pylint: disable=import-error if student_email is None: student_email = self.user.email before, after = enroll_email( self.ccx, student_email, auto_enroll, email_students, email_params ) return before, after def test_enroll_non_user_sending_email(self): """enroll a non-user email and send an enrollment email to them """ # ensure no emails are in the outbox now self.assertEqual(self.outbox, []) test_email = "nobody@nowhere.com" before, after = self.call_fut( student_email=test_email, email_students=True ) # there should be a future membership set for this email address now self.check_membership(email=test_email, future=True) for state in [before, after]: self.check_enrollment_state(state, False, None, False) # mail was sent and to the right person self.assertEqual(len(self.outbox), 1) msg = self.outbox[0] self.assertTrue(test_email in msg.recipients()) def test_enroll_non_member_sending_email(self): """register a non-member and send an enrollment email to them """ self.create_user() # ensure no emails are in the outbox now self.assertEqual(self.outbox, []) before, after = self.call_fut(email_students=True) # there should be a membership set for this email address now self.check_membership(email=self.user.email) self.check_enrollment_state(before, False, self.user, True) self.check_enrollment_state(after, True, self.user, True) # mail was sent and to the right person self.assertEqual(len(self.outbox), 1) msg = self.outbox[0] self.assertTrue(self.user.email in msg.recipients()) def test_enroll_member_sending_email(self): """register a member and send an enrollment email to them """ self.register_user_in_ccx() # ensure no emails are in the outbox now self.assertEqual(self.outbox, []) before, after = self.call_fut(email_students=True) # there should be a membership set for this email address now self.check_membership(email=self.user.email) for state in [before, after]: self.check_enrollment_state(state, True, self.user, True) # mail was sent and to the right person self.assertEqual(len(self.outbox), 1) msg = self.outbox[0] self.assertTrue(self.user.email in msg.recipients()) def test_enroll_non_user_no_email(self): """register a non-user via email address but send no email """ # ensure no emails are in the outbox now self.assertEqual(self.outbox, []) test_email = "nobody@nowhere.com" before, after = self.call_fut( student_email=test_email, email_students=False ) # there should be a future membership set for this email address now self.check_membership(email=test_email, future=True) for state in [before, after]: self.check_enrollment_state(state, False, None, False) # ensure there are still no emails in the outbox now self.assertEqual(self.outbox, []) def test_enroll_non_member_no_email(self): """register a non-member but send no email""" self.create_user() # ensure no emails are in the outbox now self.assertEqual(self.outbox, []) before, after = self.call_fut(email_students=False) # there should be a membership set for this email address now self.check_membership(email=self.user.email) self.check_enrollment_state(before, False, self.user, True) self.check_enrollment_state(after, True, self.user, True) # ensure there are still no emails in the outbox now self.assertEqual(self.outbox, []) def test_enroll_member_no_email(self): """enroll a member but send no email """ self.register_user_in_ccx() # ensure no emails are in the outbox now self.assertEqual(self.outbox, []) before, after = self.call_fut(email_students=False) # there should be a membership set for this email address now self.check_membership(email=self.user.email) for state in [before, after]: self.check_enrollment_state(state, True, self.user, True) # ensure there are still no emails in the outbox now self.assertEqual(self.outbox, []) @attr('shard_1') # TODO: deal with changes in behavior for auto_enroll class TestUnenrollEmail(ModuleStoreTestCase): """Tests for the unenroll_email function from ccx.utils""" def setUp(self): super(TestUnenrollEmail, self).setUp() # unbind the user created by the parent, so we can create our own when # needed. self.user = None course = CourseFactory.create() coach = AdminFactory.create() role = CourseCcxCoachRole(course.id) role.add_users(coach) self.ccx = CcxFactory(course_id=course.id, coach=coach) self.outbox = self.get_outbox() self.email = "nobody@nowhere.com" def get_outbox(self): """Return the django mail outbox""" from django.core import mail return mail.outbox def create_user(self): """provide a legitimate django user for testing """ if getattr(self, 'user', None) is None: self.user = UserFactory() def make_ccx_membership(self): """create registration of self.user in self.ccx registration will be inactive """ self.create_user() CcxMembershipFactory.create(ccx=self.ccx, student=self.user) def make_ccx_future_membership(self): """create future registration for email in self.ccx""" CcxFutureMembershipFactory.create( ccx=self.ccx, email=self.email ) def check_enrollment_state(self, state, in_ccx, member, user): """Verify an enrollment state object against provided arguments state.in_ccx will always be a boolean state.user will always be a boolean state.member will be a Django user object or None """ self.assertEqual(in_ccx, state.in_ccx) self.assertEqual(member, state.member) self.assertEqual(user, state.user) def check_membership(self, future=False): """ check membership """ if future: membership = CcxFutureMembership.objects.filter( ccx=self.ccx, email=self.email ) else: membership = CcxMembership.objects.filter( ccx=self.ccx, student=self.user ) return membership.exists() def call_fut(self, email_students=False): """call function under test""" from ccx.utils import unenroll_email # pylint: disable=import-error email = getattr(self, 'user', None) and self.user.email or self.email return unenroll_email(self.ccx, email, email_students=email_students) def test_unenroll_future_member_with_email(self): """unenroll a future member and send an email """ self.make_ccx_future_membership() # assert that a membership exists and that no emails have been sent self.assertTrue(self.check_membership(future=True)) self.assertEqual(self.outbox, []) # unenroll the student before, after = self.call_fut(email_students=True) # assert that membership is now gone self.assertFalse(self.check_membership(future=True)) # validate the before and after enrollment states for state in [before, after]: self.check_enrollment_state(state, False, None, False) # check that mail was sent and to the right person self.assertEqual(len(self.outbox), 1) msg = self.outbox[0] self.assertTrue(self.email in msg.recipients()) def test_unenroll_member_with_email(self): """unenroll a current member and send an email""" self.make_ccx_membership() # assert that a membership exists and that no emails have been sent self.assertTrue(self.check_membership()) self.assertEqual(self.outbox, []) # unenroll the student before, after = self.call_fut(email_students=True) # assert that membership is now gone self.assertFalse(self.check_membership()) # validate the before and after enrollment state self.check_enrollment_state(after, False, self.user, True) self.check_enrollment_state(before, True, self.user, True) # check that mail was sent and to the right person self.assertEqual(len(self.outbox), 1) msg = self.outbox[0] self.assertTrue(self.user.email in msg.recipients()) def test_unenroll_future_member_no_email(self): """unenroll a future member but send no email """ self.make_ccx_future_membership() # assert that a membership exists and that no emails have been sent self.assertTrue(self.check_membership(future=True)) self.assertEqual(self.outbox, []) # unenroll the student before, after = self.call_fut() # assert that membership is now gone self.assertFalse(self.check_membership(future=True)) # validate the before and after enrollment states for state in [before, after]: self.check_enrollment_state(state, False, None, False) # no email was sent to the student self.assertEqual(self.outbox, []) def test_unenroll_member_no_email(self): """unenroll a current member but send no email """ self.make_ccx_membership() # assert that a membership exists and that no emails have been sent self.assertTrue(self.check_membership()) self.assertEqual(self.outbox, []) # unenroll the student before, after = self.call_fut() # assert that membership is now gone self.assertFalse(self.check_membership()) # validate the before and after enrollment state self.check_enrollment_state(after, False, self.user, True) self.check_enrollment_state(before, True, self.user, True) # no email was sent to the student self.assertEqual(self.outbox, []) @attr('shard_1') class TestUserCCXList(ModuleStoreTestCase): """Unit tests for ccx.utils.get_all_ccx_for_user""" def setUp(self): """Create required infrastructure for tests""" super(TestUserCCXList, self).setUp() self.course = CourseFactory.create() coach = AdminFactory.create() role = CourseCcxCoachRole(self.course.id) role.add_users(coach) self.ccx = CcxFactory(course_id=self.course.id, coach=coach) enrollment = CourseEnrollmentFactory.create(course_id=self.course.id) self.user = enrollment.user self.anonymous = AnonymousUserFactory.create() def register_user_in_ccx(self, active=False): """create registration of self.user in self.ccx registration will be inactive unless active=True """ CcxMembershipFactory(ccx=self.ccx, student=self.user, active=active) def get_course_title(self): """Get course title""" from courseware.courses import get_course_about_section # pylint: disable=import-error return get_course_about_section(self.course, 'title') def call_fut(self, user): """Call function under test""" from ccx.utils import get_all_ccx_for_user # pylint: disable=import-error return get_all_ccx_for_user(user) def test_anonymous_sees_no_ccx(self): memberships = self.call_fut(self.anonymous) self.assertEqual(memberships, []) def test_unenrolled_sees_no_ccx(self): memberships = self.call_fut(self.user) self.assertEqual(memberships, []) def test_enrolled_inactive_sees_no_ccx(self): self.register_user_in_ccx() memberships = self.call_fut(self.user) self.assertEqual(memberships, []) def test_enrolled_sees_a_ccx(self): self.register_user_in_ccx(active=True) memberships = self.call_fut(self.user) self.assertEqual(len(memberships), 1) def test_data_structure(self): self.register_user_in_ccx(active=True) memberships = self.call_fut(self.user) this_membership = memberships[0] self.assertTrue(this_membership) # structure contains the expected keys for key in ['ccx_name', 'ccx_url']: self.assertTrue(key in this_membership.keys()) url_parts = [self.course.id.to_deprecated_string(), str(self.ccx.id)] # all parts of the ccx url are present for part in url_parts: self.assertTrue(part in this_membership['ccx_url']) actual_name = self.ccx.display_name self.assertEqual(actual_name, this_membership['ccx_name'])