JIRA: TNL-710 IMPORTANT: this commit converts the course_groups package to using migrations. When deploying to an existing openedx instance, migration 0001 may fail with an error indicating that the CourseUserGroup table already exists. If this happens, running the 0001 migration first, with the --fake option, is recommended. After performing this step, remaining migrations should work as expected.
256 lines
9.6 KiB
Python
256 lines
9.6 KiB
Python
import itertools
|
|
|
|
import ddt
|
|
from django.conf import settings
|
|
from django.test.client import RequestFactory
|
|
from django.test.utils import override_settings
|
|
|
|
from openedx.core.djangoapps.course_groups.models import CourseUserGroup
|
|
from django_comment_common.models import Role, Permission
|
|
from lang_pref import LANGUAGE_KEY
|
|
from notification_prefs import NOTIFICATION_PREF_KEY
|
|
from notifier_api.views import NotifierUsersViewSet
|
|
from opaque_keys.edx.locator import CourseLocator
|
|
from student.models import CourseEnrollment
|
|
from student.tests.factories import UserFactory, CourseEnrollmentFactory
|
|
from openedx.core.djangoapps.user_api.models import UserPreference
|
|
from openedx.core.djangoapps.user_api.tests.factories import UserPreferenceFactory
|
|
from util.testing import UrlResetMixin
|
|
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
|
from xmodule.modulestore.tests.factories import CourseFactory
|
|
|
|
|
|
@ddt.ddt
|
|
@override_settings(EDX_API_KEY="test_api_key")
|
|
class NotifierUsersViewSetTest(UrlResetMixin, ModuleStoreTestCase):
|
|
def setUp(self):
|
|
super(NotifierUsersViewSetTest, self).setUp()
|
|
self.courses = []
|
|
self.cohorts = []
|
|
self.user = UserFactory()
|
|
self.notification_pref = UserPreferenceFactory(
|
|
user=self.user,
|
|
key=NOTIFICATION_PREF_KEY,
|
|
value="notification pref test value"
|
|
)
|
|
|
|
self.list_view = NotifierUsersViewSet.as_view({"get": "list"})
|
|
self.detail_view = NotifierUsersViewSet.as_view({"get": "retrieve"})
|
|
|
|
def _set_up_course(self, is_course_cohorted, is_user_cohorted, is_moderator):
|
|
cohort_config = {"cohorted": True} if is_course_cohorted else {}
|
|
course = CourseFactory(
|
|
number=("TestCourse{}".format(len(self.courses))),
|
|
cohort_config=cohort_config
|
|
)
|
|
self.courses.append(course)
|
|
CourseEnrollmentFactory(user=self.user, course_id=course.id)
|
|
if is_user_cohorted:
|
|
cohort = CourseUserGroup.objects.create(
|
|
name="Test Cohort",
|
|
course_id=course.id,
|
|
group_type=CourseUserGroup.COHORT
|
|
)
|
|
cohort.users.add(self.user)
|
|
self.cohorts.append(cohort)
|
|
if is_moderator:
|
|
moderator_perm, _ = Permission.objects.get_or_create(name="see_all_cohorts")
|
|
moderator_role = Role.objects.create(name="Moderator", course_id=course.id)
|
|
moderator_role.permissions.add(moderator_perm)
|
|
self.user.roles.add(moderator_role)
|
|
|
|
def _assert_basic_user_info_correct(self, user, result_user):
|
|
self.assertEqual(result_user["id"], user.id)
|
|
self.assertEqual(result_user["email"], user.email)
|
|
self.assertEqual(result_user["name"], user.profile.name)
|
|
|
|
def test_without_api_key(self):
|
|
request = RequestFactory().get("dummy")
|
|
for view in [self.list_view, self.detail_view]:
|
|
response = view(request)
|
|
self.assertEqual(response.status_code, 403)
|
|
|
|
# Detail view tests
|
|
|
|
def _make_detail_request(self):
|
|
request = RequestFactory().get("dummy", HTTP_X_EDX_API_KEY=settings.EDX_API_KEY)
|
|
return self.detail_view(
|
|
request,
|
|
**{NotifierUsersViewSet.lookup_field: str(self.user.id)}
|
|
)
|
|
|
|
def _get_detail(self):
|
|
response = self._make_detail_request()
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(
|
|
set(response.data.keys()),
|
|
{"id", "email", "name", "preferences", "course_info"}
|
|
)
|
|
return response.data
|
|
|
|
def test_detail_invalid_user(self):
|
|
UserPreference.objects.all().delete()
|
|
response = self._make_detail_request()
|
|
self.assertEqual(response.status_code, 404)
|
|
|
|
def test_basic_user_info(self):
|
|
result = self._get_detail()
|
|
self._assert_basic_user_info_correct(self.user, result)
|
|
|
|
def test_course_info(self):
|
|
expected_course_info = {}
|
|
for is_course_cohorted, is_user_cohorted, is_moderator in (
|
|
itertools.product([True, False], [True, False], [True, False])
|
|
):
|
|
self._set_up_course(is_course_cohorted, is_user_cohorted, is_moderator)
|
|
expected_course_info[unicode(self.courses[-1].id)] = {
|
|
"cohort_id": self.cohorts[-1].id if is_user_cohorted else None,
|
|
"see_all_cohorts": is_moderator or not is_course_cohorted
|
|
}
|
|
result = self._get_detail()
|
|
self.assertEqual(result["course_info"], expected_course_info)
|
|
|
|
def test_course_info_unenrolled(self):
|
|
self._set_up_course(False, False, False)
|
|
course_id = self.courses[0].id
|
|
CourseEnrollment.unenroll(self.user, course_id)
|
|
result = self._get_detail()
|
|
self.assertNotIn(unicode(course_id), result["course_info"])
|
|
|
|
def test_course_info_no_enrollments(self):
|
|
result = self._get_detail()
|
|
self.assertEqual(result["course_info"], {})
|
|
|
|
def test_course_info_non_existent_course_enrollment(self):
|
|
CourseEnrollmentFactory(
|
|
user=self.user,
|
|
course_id=CourseLocator(org="dummy", course="dummy", run="non_existent")
|
|
)
|
|
result = self._get_detail()
|
|
self.assertEqual(result["course_info"], {})
|
|
|
|
def test_preferences(self):
|
|
lang_pref = UserPreferenceFactory(
|
|
user=self.user,
|
|
key=LANGUAGE_KEY,
|
|
value="language pref test value"
|
|
)
|
|
UserPreferenceFactory(user=self.user, key="non_included_key")
|
|
result = self._get_detail()
|
|
self.assertEqual(
|
|
result["preferences"],
|
|
{
|
|
NOTIFICATION_PREF_KEY: self.notification_pref.value,
|
|
LANGUAGE_KEY: lang_pref.value,
|
|
}
|
|
)
|
|
|
|
# List view tests
|
|
|
|
def _make_list_request(self, page, page_size):
|
|
request = RequestFactory().get(
|
|
"dummy",
|
|
{"page": page, "page_size": page_size},
|
|
HTTP_X_EDX_API_KEY=settings.EDX_API_KEY
|
|
)
|
|
return self.list_view(request)
|
|
|
|
def _get_list(self, page=1, page_size=None):
|
|
response = self._make_list_request(page, page_size)
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertEqual(
|
|
set(response.data.keys()),
|
|
{"count", "next", "previous", "results"}
|
|
)
|
|
return response.data["results"]
|
|
|
|
def test_no_users(self):
|
|
UserPreference.objects.all().delete()
|
|
results = self._get_list()
|
|
self.assertEqual(len(results), 0)
|
|
|
|
def test_multiple_users(self):
|
|
other_user = UserFactory()
|
|
other_notification_pref = UserPreferenceFactory(
|
|
user=other_user,
|
|
key=NOTIFICATION_PREF_KEY,
|
|
value="other value"
|
|
)
|
|
self._set_up_course(is_course_cohorted=True, is_user_cohorted=True, is_moderator=False)
|
|
self._set_up_course(is_course_cohorted=False, is_user_cohorted=False, is_moderator=False)
|
|
# Users have different sets of enrollments
|
|
CourseEnrollmentFactory(user=other_user, course_id=self.courses[0].id)
|
|
|
|
result_map = {result["id"]: result for result in self._get_list()}
|
|
self.assertEqual(set(result_map.keys()), {self.user.id, other_user.id})
|
|
for user in [self.user, other_user]:
|
|
self._assert_basic_user_info_correct(user, result_map[user.id])
|
|
self.assertEqual(
|
|
result_map[self.user.id]["preferences"],
|
|
{NOTIFICATION_PREF_KEY: self.notification_pref.value}
|
|
)
|
|
self.assertEqual(
|
|
result_map[other_user.id]["preferences"],
|
|
{NOTIFICATION_PREF_KEY: other_notification_pref.value}
|
|
)
|
|
self.assertEqual(
|
|
result_map[self.user.id]["course_info"],
|
|
{
|
|
unicode(self.courses[0].id): {
|
|
"cohort_id": self.cohorts[0].id,
|
|
"see_all_cohorts": False,
|
|
},
|
|
unicode(self.courses[1].id): {
|
|
"cohort_id": None,
|
|
"see_all_cohorts": True,
|
|
},
|
|
}
|
|
)
|
|
self.assertEqual(
|
|
result_map[other_user.id]["course_info"],
|
|
{
|
|
unicode(self.courses[0].id): {
|
|
"cohort_id": None,
|
|
"see_all_cohorts": False,
|
|
},
|
|
}
|
|
)
|
|
|
|
@ddt.data(
|
|
3, # Factor of num of results
|
|
5, # Non-factor of num of results
|
|
12, # Num of results
|
|
15 # More than num of results
|
|
)
|
|
def test_pagination(self, page_size):
|
|
num_users = 12
|
|
users = [self.user]
|
|
while len(users) < num_users:
|
|
new_user = UserFactory()
|
|
users.append(new_user)
|
|
UserPreferenceFactory(user=new_user, key=NOTIFICATION_PREF_KEY)
|
|
|
|
num_pages = (num_users - 1) / page_size + 1
|
|
result_list = []
|
|
for i in range(1, num_pages + 1):
|
|
result_list.extend(self._get_list(page=i, page_size=page_size))
|
|
result_map = {result["id"]: result for result in result_list}
|
|
|
|
self.assertEqual(len(result_list), num_users)
|
|
for user in users:
|
|
self._assert_basic_user_info_correct(user, result_map[user.id])
|
|
self.assertEqual(
|
|
self._make_list_request(page=(num_pages + 1), page_size=page_size).status_code,
|
|
404
|
|
)
|
|
|
|
def test_db_access(self):
|
|
for _ in range(10):
|
|
new_user = UserFactory()
|
|
UserPreferenceFactory(user=new_user, key=NOTIFICATION_PREF_KEY)
|
|
|
|
# The number of queries is one for the users plus one for each prefetch
|
|
# in NotifierUsersViewSet (roles__permissions does one for each table).
|
|
with self.assertNumQueries(6):
|
|
self._get_list()
|