Newer
Older
"""
Tests for the teams API at the HTTP request level.
"""
from contextlib import contextmanager
from datetime import datetime
from opaque_keys.edx.keys import CourseKey
Nathan Sprenkle
committed
from course_modes.models import CourseMode
Nathan Sprenkle
committed
from lms.djangoapps.teams.errors import AddToIncompatibleTeamError
from lms.djangoapps.teams.models import CourseTeam, CourseTeamMembership
from lms.djangoapps.teams.tests.factories import CourseTeamFactory, CourseTeamMembershipFactory
from openedx.core.djangoapps.django_comment_common.signals import (
comment_created,
comment_deleted,
thread_created,
thread_deleted,
thread_edited,
thread_voted
from student.tests.factories import CourseEnrollmentFactory, UserFactory
from util.testing import EventTestMixin
from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase
COURSE_KEY1 = CourseKey.from_string('edx/history/1')
COURSE_KEY2 = CourseKey.from_string('edx/history/2')
TEAMSET_1_ID = "the-teamset"
TEAMSET_2_ID = "the-teamset-2"
class TestModelStrings(SharedModuleStoreTestCase):
"""
Test `__repr__` and `__str__` methods of this app's models.
"""
@classmethod
def setUpClass(cls):
super(TestModelStrings, cls).setUpClass()
cls.user = UserFactory.create(username="the-user")
CourseEnrollmentFactory.create(user=cls.user, course_id="edx/the-course/1")
cls.team = CourseTeamFactory(
course_id="edx/the-course/1",
team_id="the-team",
topic_id=TEAMSET_1_ID,
name="The Team"
)
cls.team_membership = cls.team.add_user(cls.user)
def test_team_repr(self):
assert repr(self.team) == (
"<CourseTeam"
" id=1"
" team_id=the-team"
" team_size=1"
" topic_id=the-teamset"
" course_id=edx/the-course/1"
">"
)
def test_team_text(self):
assert six.text_type(self.team) == (
"The Team in edx/the-course/1"
)
def test_team_membership_repr(self):
assert repr(self.team_membership) == (
"<CourseTeamMembership id=1 user_id=1 team_id=1>"
)
def test_team_membership_text_type(self):
assert six.text_type(self.team_membership) == (
"the-user is member of The Team in edx/the-course/1"
)
Nathan Sprenkle
committed
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
class CourseTeamTest(SharedModuleStoreTestCase):
"""Tests for the CourseTeam model."""
@classmethod
def setUpClass(cls):
super(CourseTeamTest, cls).setUpClass()
cls.audit_learner = UserFactory.create(username="audit")
CourseEnrollmentFactory.create(user=cls.audit_learner, course_id="edx/the-course/1", mode=CourseMode.AUDIT)
cls.audit_team = CourseTeamFactory(
course_id="edx/the-course/1",
team_id="audit-team",
topic_id=TEAMSET_1_ID,
name="The Team"
)
cls.masters_learner = UserFactory.create(username="masters")
CourseEnrollmentFactory.create(user=cls.masters_learner, course_id="edx/the-course/1", mode=CourseMode.MASTERS)
cls.masters_team = CourseTeamFactory(
course_id="edx/the-course/1",
team_id="masters-team",
topic_id=TEAMSET_1_ID,
name="The Team",
organization_protected=True
)
def test_add_user(self):
"""Test that we can add users with correct protection status to a team"""
self.assertIsNotNone(self.masters_team.add_user(self.masters_learner))
self.assertIsNotNone(self.audit_team.add_user(self.audit_learner))
def test_add_user_bad_team_access(self):
"""Test that we are blocked from adding a user to a team of mixed enrollment types"""
with self.assertRaises(AddToIncompatibleTeamError):
self.audit_team.add_user(self.masters_learner)
with self.assertRaises(AddToIncompatibleTeamError):
self.masters_team.add_user(self.audit_learner)
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
def test_defaults_with_create(self):
field = CourseTeam._meta.get_field('last_activity_at')
default_date = datetime(1978, 1, 1)
mock_now = lambda: default_date
with patch.object(field, 'default', new=mock_now):
team = CourseTeam.create(
name='foo',
course_id='sedx/the-course/1',
description='Import from csv',
topic_id=TEAMSET_1_ID,
organization_protected=True
)
self.assert_team_default_values(default_date, team)
def test_defaults_with_constructor(self):
field = CourseTeam._meta.get_field('last_activity_at')
default_date = datetime(1978, 1, 1)
mock_now = lambda: default_date
with patch.object(field, 'default', new=mock_now):
team = CourseTeam(
name='foo',
course_id='sedx/the-course/1',
description='Import from csv',
topic_id=TEAMSET_1_ID,
organization_protected=True
)
self.assert_team_default_values(default_date, team)
def assert_team_default_values(self, default_date, team):
self.assertEqual(default_date, team.last_activity_at)
self.assertEqual('', team.language)
self.assertEqual('', team.country)
Nathan Sprenkle
committed
@ddt.ddt
class TeamMembershipTest(SharedModuleStoreTestCase):
"""Tests for the TeamMembership model."""
def setUp(self):
"""
Set up tests.
"""
super(TeamMembershipTest, self).setUp()
self.user1 = UserFactory.create(username='user1')
self.user2 = UserFactory.create(username='user2')
self.user3 = UserFactory.create(username='user3')
for user in (self.user1, self.user2, self.user3):
CourseEnrollmentFactory.create(user=user, course_id=COURSE_KEY1)
CourseEnrollmentFactory.create(user=self.user1, course_id=COURSE_KEY2)
self.team1 = CourseTeamFactory(
course_id=COURSE_KEY1,
team_id='team1',
topic_id=TEAMSET_1_ID,
)
self.team2 = CourseTeamFactory(
course_id=COURSE_KEY2,
team_id='team2',
topic_id=TEAMSET_2_ID,
)
self.team_membership11 = self.team1.add_user(self.user1)
self.team_membership12 = self.team1.add_user(self.user2)
self.team_membership21 = self.team2.add_user(self.user1)
def test_membership_last_activity_set(self):
current_last_activity = self.team_membership11.last_activity_at
# Assert that the first save in the setUp sets a value.
self.assertIsNotNone(current_last_activity)
self.team_membership11.save()
# Verify that we only change the last activity_at when it doesn't
# already exist.
self.assertEqual(self.team_membership11.last_activity_at, current_last_activity)
def test_team_size_delete_membership(self):
"""Test that the team size field is correctly updated when deleting a
team membership.
"""
self.assertEqual(self.team1.team_size, 2)
self.team_membership11.delete()
team = CourseTeam.objects.get(id=self.team1.id)
self.assertEqual(team.team_size, 1)
def test_team_size_create_membership(self):
"""Test that the team size field is correctly updated when creating a
team membership.
"""
self.assertEqual(self.team1.team_size, 2)
self.team1.add_user(self.user3)
team = CourseTeam.objects.get(id=self.team1.id)
self.assertEqual(team.team_size, 3)
@ddt.data(
(None, None, None, 3),
('user1', None, None, 2),
('user1', [COURSE_KEY1], None, 1),
('user1', None, 'team1', 1),
('user2', None, None, 1),
)
@ddt.unpack
def test_get_memberships(self, username, course_ids, team_id, expected_count):
self.assertEqual(
CourseTeamMembership.get_memberships(username=username, course_ids=course_ids, team_id=team_id).count(),
expected_count
)
@ddt.data(
('user1', COURSE_KEY1, True),
('user2', COURSE_KEY1, True),
('user2', COURSE_KEY2, False),
)
@ddt.unpack
def test_user_in_team_for_course(self, username, course_id, expected_value):
user = getattr(self, username)
self.assertEqual(
CourseTeamMembership.user_in_team_for_course(user, course_id),
expected_value
)
@ddt.data(
('user1', COURSE_KEY1, TEAMSET_1_ID, True),
('user1', COURSE_KEY1, TEAMSET_2_ID, False),
('user2', COURSE_KEY1, TEAMSET_1_ID, True),
('user2', COURSE_KEY1, TEAMSET_2_ID, False),
('user1', COURSE_KEY2, TEAMSET_1_ID, False),
('user2', COURSE_KEY2, TEAMSET_1_ID, False),
)
@ddt.unpack
def test_user_in_team_for_course_teamset(self, username, course_id, teamset_id, expected_value):
user = getattr(self, username)
self.assertEqual(
CourseTeamMembership.user_in_team_for_course(user, course_id, teamset_id),
expected_value
)
class TeamSignalsTest(EventTestMixin, SharedModuleStoreTestCase):
"""Tests for handling of team-related signals."""
SIGNALS = {
'thread_created': thread_created,
'thread_edited': thread_edited,
'thread_deleted': thread_deleted,
'thread_voted': thread_voted,
'comment_created': comment_created,
'comment_edited': comment_edited,
'comment_deleted': comment_deleted,
'comment_voted': comment_voted,
'comment_endorsed': comment_endorsed,
}
DISCUSSION_TOPIC_ID = 'test_topic'
def setUp(self): # pylint: disable=arguments-differ
"""Create a user with a team to test signals."""
super(TeamSignalsTest, self).setUp('lms.djangoapps.teams.utils.tracker')
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
self.user = UserFactory.create(username="user")
self.moderator = UserFactory.create(username="moderator")
self.team = CourseTeamFactory(discussion_topic_id=self.DISCUSSION_TOPIC_ID)
self.team_membership = CourseTeamMembershipFactory(user=self.user, team=self.team)
def mock_comment(self, context=TEAM_DISCUSSION_CONTEXT, user=None):
"""Create a mock comment service object with the given context."""
if user is None:
user = self.user
return Mock(
user_id=user.id,
commentable_id=self.DISCUSSION_TOPIC_ID,
context=context,
**{'thread.user_id': self.user.id}
)
@contextmanager
def assert_last_activity_updated(self, should_update):
"""If `should_update` is True, assert that the team and team
membership have had their `last_activity_at` updated. Otherwise,
assert that it was not updated.
"""
team_last_activity = self.team.last_activity_at
team_membership_last_activity = self.team_membership.last_activity_at
yield
# Reload team and team membership from the database in order to pick up changes
team = CourseTeam.objects.get(id=self.team.id)
team_membership = CourseTeamMembership.objects.get(id=self.team_membership.id)
if should_update:
self.assertGreater(team.last_activity_at, team_last_activity)
self.assertGreater(team_membership.last_activity_at, team_membership_last_activity)
now = datetime.utcnow().replace(tzinfo=pytz.utc)
self.assertGreater(now, team.last_activity_at)
self.assertGreater(now, team_membership.last_activity_at)
self.assert_event_emitted(
'edx.team.activity_updated',
Jeff LaJoie
committed
team_id=team.team_id,
else:
self.assertEqual(team.last_activity_at, team_last_activity)
self.assertEqual(team_membership.last_activity_at, team_membership_last_activity)
self.assert_no_events_were_emitted()
@ddt.data(
*itertools.product(
(('user', True), ('moderator', False))
)
)
@ddt.unpack
def test_signals(self, signal_name, user_should_update):
"""Test that `last_activity_at` is correctly updated when team-related
signals are sent.
"""
(user, should_update) = user_should_update
with self.assert_last_activity_updated(should_update):
user = getattr(self, user)
signal.send(sender=None, user=user, post=self.mock_comment())
@ddt.data('thread_voted', 'comment_voted')
def test_vote_others_post(self, signal_name):
"""Test that voting on another user's post correctly fires a
signal."""
with self.assert_last_activity_updated(True):
signal.send(sender=None, user=self.user, post=self.mock_comment(user=self.moderator))
"""Test that `last_activity_at` is not updated when activity takes
place in discussions outside of a team.
"""
with self.assert_last_activity_updated(False):
signal.send(sender=None, user=self.user, post=self.mock_comment(context='course'))