578 lines
22 KiB
Python
578 lines
22 KiB
Python
"""
|
|
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'])
|