def test_add_education(self): """ Test that we handle adding an Education correctly """ education_object = { 'degree_name': DOCTORATE, 'graduation_date': '9876-04-23', 'field_of_study': 'subject', 'online_degree': True, 'school_name': 'school_name', 'school_city': 'school_city', 'school_state_or_territory': 'school_state_or_territory', 'school_country': 'school_country,' } user1 = UserFactory.create() user2 = UserFactory.create() serializer = ProfileSerializer(instance=user1.profile, data={ 'education': [education_object], 'work_history': [] }) serializer.is_valid(raise_exception=True) serializer.save() assert user1.profile.education.count() == 1 education = user1.profile.education.first() education_object['id'] = education.id assert EducationSerializer().to_representation( education) == education_object # Other profile did not get the education assigned to it assert user2.profile.education.count() == 0
def setUpTestData(cls): super(SearchTests, cls).setUpTestData() # create some students with mute_signals(post_save): cls.students = [(ProfileFactory.create()).user for _ in range(30)] # create the programs cls.program1 = ProgramFactory.create(live=True) cls.program2 = ProgramFactory.create(live=True) cls.program3 = ProgramFactory.create(live=True) # enroll the users in the programs for num, student in enumerate(cls.students): if num % 3 == 0: program = cls.program1 elif num % 3 == 1: program = cls.program2 else: program = cls.program3 ProgramEnrollmentFactory.create( user=student, program=program ) # create an user with a role for one program cls.staff = UserFactory.create() Role.objects.create( user=cls.staff, program=cls.program1, role=Staff.ROLE_ID ) # create another user without any role cls.user = UserFactory.create() # search URL cls.search_url = reverse('search_api', kwargs={'elastic_url': ''})
def test_add_employment(self): """ Test that we handle adding an employment correctly """ employment_object = { "city": "NY", "state_or_territory": "NY", "country": "USA", "company_name": "XYZ-ABC", "position": "SSE", "industry": "IT", "end_date": "2016-05-17", "start_date": "2016-05-28" } user1 = UserFactory.create() user2 = UserFactory.create() serializer = ProfileSerializer(instance=user1.profile, data={ 'work_history': [employment_object], 'education': [] }) serializer.is_valid(raise_exception=True) serializer.save() assert user1.profile.work_history.count() == 1 employment = user1.profile.work_history.first() employment_object['id'] = employment.id assert EmploymentSerializer().to_representation( employment) == employment_object # Other profile did not get the employment assigned to it assert user2.profile.work_history.count() == 0
def setUpTestData(cls): super(ProgramEnrollmentTests, cls).setUpTestData() cls.user1 = UserFactory.create() cls.user2 = UserFactory.create() cls.program1 = ProgramFactory.create(live=True) cls.program2 = ProgramFactory.create(live=True) cls.program3 = ProgramFactory.create(live=True) cls.url = reverse('user_program_enrollments')
def test_sync_discussion_users_sync_disabled(settings, mocker, patched_users_api): """ Test that sync_discussion_users doesn't call the api if disabled """ UserFactory.create() settings.FEATURES['OPEN_DISCUSSIONS_USER_SYNC'] = False mock_api = mocker.patch('discussions.api.create_or_update_discussion_user') tasks.sync_discussion_users() assert mock_api.called is False
def setUpTestData(cls): super(TasksTest, cls).setUpTestData() # create an user cls.user = UserFactory.create() cls.user2 = UserFactory.create() # create a social auth for the user for user in [cls.user, cls.user2]: user.social_auth.create( provider=EdxOrgOAuth2.name, uid="{}_edx".format(user.username), extra_data='{"access_token": "fooooootoken"}')
def test_get_membership_ids_needing_sync(patched_users_api): """ Tests that get_membership_ids_needing_sync only returns ids for the correct records """ user1 = UserFactory.create() user2 = UserFactory.create() user3 = UserFactory.create() with mute_signals(post_save): user3.profile.delete() member_channels = [ChannelFactory.create() for _ in range(4)] nonmember_channels = [ChannelFactory.create() for _ in range(3)] # these should show up in results memberships_to_add = [ PercolateQueryMembership.objects.create(user=user1, query=channel.query, needs_update=True, is_member=True) for channel in member_channels ] memberships_to_remove = [ PercolateQueryMembership.objects.create(user=user1, query=channel.query, needs_update=True, is_member=False) for channel in nonmember_channels ] # these shouldn't show up in results memberships_add_no_update = [ PercolateQueryMembership.objects.create(user=user2, query=channel.query, needs_update=False, is_member=True) for channel in member_channels ] memberships_remove_no_update = [ PercolateQueryMembership.objects.create(user=user2, query=channel.query, needs_update=False, is_member=False) for channel in nonmember_channels ] memberships_add_no_profile = [ PercolateQueryMembership.objects.create(user=user3, query=channel.query, needs_update=True, is_member=True) for channel in member_channels ] memberships_remove_no_profile = [ PercolateQueryMembership.objects.create(user=user3, query=channel.query, needs_update=True, is_member=False) for channel in nonmember_channels ] results = api.get_membership_ids_needing_sync() for membership in memberships_to_add + memberships_to_remove: assert membership.id in results for membership in ( memberships_add_no_update + memberships_remove_no_update + memberships_add_no_profile + memberships_remove_no_profile ): assert membership.id not in results
def setUp(self): super().setUp() self.request_data = self.request_data.copy() self.request_data['send_automatic_emails'] = True for email in self.email_results: UserFactory.create(email=email) self.automatic_email = AutomaticEmailFactory.create() self.search_obj = create_search_obj( user=self.staff, search_param_dict=self.request_data['search_request'], filter_on_email_optin=True, )
def setUp(self): super().setUp() self.request_data = self.request_data.copy() self.request_data['send_automatic_emails'] = True for email in self.email_results: UserFactory.create(email=email) self.automatic_email = AutomaticEmailFactory.create() self.search_obj = create_search_obj( user=self.staff, search_param_dict=self.request_data['search_request'], filter_on_email_optin=True, )
def setUpTestData(cls): """Create a program and user to test with""" super().setUpTestData() cls.program = ProgramFactory.create() cls.user = UserFactory.create() cls.context = {"request": Mock(user=cls.user)}
def test_creates_order(self): """ An order is created using create_unfulfilled_order and a payload is generated using generate_cybersource_sa_payload """ course_key = 'course_key' user = UserFactory.create() self.client.force_login(user) order = MagicMock() payload = {'a': 'payload'} with patch( 'ecommerce.views.create_unfulfilled_order', autospec=True, return_value=order, ) as create_mock, patch( 'ecommerce.views.generate_cybersource_sa_payload', autospec=True, return_value=payload, ) as generate_mock: resp = self.client.post(reverse('checkout'), {'course_id': course_key}, format='json') assert resp.status_code == status.HTTP_200_OK assert resp.json() == { 'payload': payload, 'url': CYBERSOURCE_SECURE_ACCEPTANCE_URL, } assert create_mock.call_count == 1 assert create_mock.call_args[0] == (course_key, user) assert generate_mock.call_count == 1 assert generate_mock.call_args[0] == (order, )
def test_ordering_get_membership_ids_needing_sync(patched_users_api): """Test that get_membership_ids_needing_sync returns ordered list based on is_member (True before False) and updated_on (most recent first).""" users = [UserFactory.create() for _ in range(4)] channel = ChannelFactory.create() memberships_is_member_true = [ PercolateQueryMembership.objects.create(user=user, query=channel.query, needs_update=True, is_member=True) for user in users[:2] ] memberships_is_member_false = [ PercolateQueryMembership.objects.create(user=user, query=channel.query, needs_update=True, is_member=False) for user in users[2:] ] memberships_is_member_true.reverse() memberships_is_member_false.reverse() expected_order = [] for membership in memberships_is_member_true + memberships_is_member_false: expected_order.append(membership.id) results = api.get_membership_ids_needing_sync() assert expected_order == list(results)
def test_add_channel_channel_already_exists(mock_staff_client, patched_users_api): """Channel already exists with that channel name""" response_409 = Response() response_409.status_code = statuses.HTTP_409_CONFLICT mock_staff_client.channels.create.return_value = response_409 title = "title" name = "name" description = "public description" channel_type = "private" input_search = Search.from_dict({"unmodified": "search"}) role = RoleFactory.create() mod = UserFactory.create() with pytest.raises(ChannelAlreadyExistsException): api.add_channel( original_search=input_search, title=title, name=name, description=description, channel_type=channel_type, program_id=role.program.id, creator_id=mod.id, ) mock_staff_client.channels.create.assert_called_once_with( title=title, name=name, description=description, channel_type=channel_type, )
def setUpTestData(cls): super(APITests, cls).setUpTestData() # create an user cls.user = UserFactory.create() # create the programs cls.program1 = ProgramFactory.create(live=True) cls.program2 = ProgramFactory.create(live=True)
def test_normal_users_cant_get(self): """ normal users shouldnt be able to get any data """ normal_user = UserFactory.create() self.client.force_login(normal_user) response = self.client.get(self.url) assert response.status_code == status.HTTP_403_FORBIDDEN
def setUpTestData(cls): super(RefreshTest, cls).setUpTestData() # create an user cls.user = UserFactory.create() # create a social auth for the user cls.user.social_auth.create(provider=EdxOrgOAuth2.name, uid="{}_edx".format(cls.user.username), extra_data=social_extra_data)
def setUp(self): super(CanSeeIfNotPrivateTests, self).setUp() with mute_signals(post_save): self.other_user = other_user = UserFactory.create() username = "******".format(other_user.username) social_auth = other_user.social_auth.create( provider=EdxOrgOAuth2.name, uid=username) self.other_user_id = social_auth.uid ProfileFactory.create(user=other_user, verified_micromaster_user=False) with mute_signals(post_save): self.profile_user = profile_user = UserFactory.create() username = "******".format(profile_user.username) social_auth = profile_user.social_auth.create( provider=EdxOrgOAuth2.name, uid=username) self.profile_user_id = social_auth.uid
def test_normal_users_cant_get(self): """ normal users shouldnt be able to get any data """ normal_user = UserFactory.create() self.client.force_login(normal_user) response = self.client.get(self.url) assert response.status_code == status.HTTP_403_FORBIDDEN
def create_purchasable_course_run(): """ Creates a purchasable course run and an associated user """ course_run = CourseRunFactory.create(course__program__live=True) CoursePriceFactory.create(course_run=course_run, is_valid=True) user = UserFactory.create() return course_run, user
def setUpTestData(cls): """ Create a user """ with mute_signals(post_save): cls.user1 = UserFactory.create() username = "******".format(cls.user1.username) cls.user1.social_auth.create(provider=EdxOrgOAuth2.name, uid=username) cls.url1 = reverse('profile-detail', kwargs={'user': username}) with mute_signals(post_save): cls.user2 = UserFactory.create() username = "******".format(cls.user2.username) cls.user2.social_auth.create(provider=EdxOrgOAuth2.name, uid=username) cls.url2 = reverse('profile-detail', kwargs={'user': username})
def setUpTestData(cls): """Create a program and user to test with""" super().setUpTestData() cls.program = ProgramFactory.create() cls.user = UserFactory.create() cls.context = { "request": Mock(user=cls.user) }
def setUpTestData(cls): cls.staff_user = UserFactory.create() cls.program = ProgramFactory.create() Role.objects.create( user=cls.staff_user, program=cls.program, role=Staff.ROLE_ID, ) cls.url = reverse('automatic_email_api-list')
def setUpTestData(cls): cls.staff_user = UserFactory.create() cls.program = ProgramFactory.create() Role.objects.create( user=cls.staff_user, program=cls.program, role=Staff.ROLE_ID, ) cls.url = reverse('automatic_email_api-list')
def test_valid_course_id(self): """ If course_id is not present in payload a ValidationError is raised """ user = UserFactory.create() self.client.force_login(user) resp = self.client.post(reverse('checkout'), {}, format='json') assert resp.status_code == status.HTTP_400_BAD_REQUEST assert resp.json() == ['Missing course_id']
def test_normal_users_cant_patch(self): """ A non-staff user shouldn't be able to issue a PATCH request """ normal_user = UserFactory.create() self.client.force_login(normal_user) automatic = AutomaticEmailFactory.create(staff_user=self.staff_user) url = reverse('automatic_email_api-detail', kwargs={'email_id': automatic.id}) response = self.client.patch(url, {}, format='json') assert response.status_code == status.HTTP_403_FORBIDDEN
def test_saves_linkedin_response(self): """Happy path""" backend = mock.Mock() backend.name = 'linkedin-oauth2' user = UserFactory.create() response = {'test': 'works'} update_from_linkedin(backend, user, response) profile = Profile.objects.get(user=user) assert profile.linkedin == {'test': 'works'}
def test_login_redirect_dashboard(self): """ A student should be directed to /dashboard """ student = UserFactory.create() mock_strategy = mock.Mock() mock_strategy.session.load.return_value = {} mock_strategy.session.get.return_value = {} backend = edxorg.EdxOrgOAuth2(strategy=mock_strategy) pipeline_api.update_profile_from_edx(backend, student, {}, False, **{'edx_profile': self.mocked_edx_profile}) mock_strategy.session_set.assert_called_with('next', '/dashboard')
def setUpTestData(cls): super(RefreshTest, cls).setUpTestData() # create an user cls.user = UserFactory.create() # create a social auth for the user cls.user.social_auth.create( provider=EdxOrgOAuth2.name, uid="{}_edx".format(cls.user.username), extra_data= '{"access_token": "fooooootoken", "refresh_token": "baaaarrefresh"}' )
def test_normal_users_cant_patch(self): """ A non-staff user shouldn't be able to issue a PATCH request """ normal_user = UserFactory.create() self.client.force_login(normal_user) automatic = AutomaticEmailFactory.create(staff_user=self.staff_user) url = reverse('automatic_email_api-detail', kwargs={'email_id': automatic.id}) response = self.client.patch(url, {}, format='json') assert response.status_code == status.HTTP_403_FORBIDDEN
def test_sync_discussion_users_sync_with_email_optin_enabled( settings, mocker, patched_users_api): """ Test that sync_discussion_users call the api if enabled and for only users with a profile and not already synchronized """ users = [UserFactory.create() for _ in range(5)] for user in users[1:]: # Delete DiscussionUser so it will get backfilled user.discussion_user.delete() user_no_profile = UserFactory.create() user_no_profile.profile.delete() mock_api = mocker.patch('discussions.api.create_or_update_discussion_user', autospec=True) tasks.force_sync_discussion_users() assert mock_api.call_count == len(users) for user in users: mock_api.assert_any_call(user.id, allow_email_optin=True) with pytest.raises(AssertionError): mock_api.assert_any_call(user_no_profile.id, allow_email_optin=True)
def create(cls, **kwargs): """ Overrides default ProgramEnrollment object creation for the factory. """ user = kwargs.get('user', UserFactory.create()) program = kwargs.get('program', ProgramFactory.create()) course = CourseFactory.create(program=program) course_run = CourseRunFactory.create(course=course) CachedEnrollmentFactory.create(user=user, course_run=course_run) CachedCertificateFactory.create(user=user, course_run=course_run) program_enrollment = ProgramEnrollment.objects.create(user=user, program=program) return program_enrollment
def test_login_redirect_next_for_user_already_logged_in(self): """ A student should be directed to what's in the next query parameter if user is already logged in to MM. """ student = UserFactory.create() next_url = "/discussion" mock_strategy = mock.Mock() mock_strategy.session.load.return_value = {} mock_strategy.session.get.return_value = next_url backend = edxorg.EdxOrgOAuth2(strategy=mock_strategy) pipeline_api.update_profile_from_edx(backend, student, {}, False, **{'edx_profile': self.mocked_edx_profile}) mock_strategy.session_set.assert_called_with('next', next_url)
def test_login_redirect_next(self): """ A student should be directed to what's in the next query parameter """ student = UserFactory.create() next_url = "/x/y?a=bc" mock_strategy = mock.Mock() mock_strategy.session.load.return_value = {"next": next_url} mock_strategy.session.get.return_value = {} backend = edxorg.EdxOrgOAuth2(strategy=mock_strategy) pipeline_api.update_profile_from_edx(backend, student, {}, False, **{'edx_profile': self.mocked_edx_profile}) mock_strategy.session_set.assert_called_with('next', next_url)
def test_cant_edit_if_not_owner(self): """ Users are not allowed to edit if it's not their profile. """ perm = CanEditIfOwner() other_user = UserFactory.create() for method in ('POST', 'PATCH', 'PUT'): with mute_signals(post_save): profile = ProfileFactory.create( account_privacy=Profile.PUBLIC_TO_MM) request = Mock(method=method, user=other_user) assert not perm.has_object_permission(request, None, profile)
def test_login_redirect_dashboard(self): """ A student should be directed to /dashboard """ student = UserFactory.create() mock_strategy = mock.Mock() mock_strategy.session.load.return_value = {} mock_strategy.session.get.return_value = {} backend = edxorg.EdxOrgOAuth2(strategy=mock_strategy) pipeline_api.update_profile_from_edx( backend, student, {}, False, **{'edx_profile': self.mocked_edx_profile}) mock_strategy.session_set.assert_called_with('next', '/dashboard')
def test_save_and_log_model(self): """ Tests that the save_model() function on OrderAdmin creates an OrderAudit entry """ assert OrderAudit.objects.count() == 0 order = OrderFactory.create() admin = OrderAdmin(model=order, admin_site=Mock()) mock_request = Mock(user=UserFactory.create()) admin.save_model(request=mock_request, obj=admin.model, form=Mock(), change=Mock()) assert OrderAudit.objects.count() == 1
def test_login_redirect_learners(self): """ Staff or instructors should be directed to /learners """ for role in [Staff.ROLE_ID, Instructor.ROLE_ID]: user = UserFactory.create() Role.objects.create(user=user, role=role, program=ProgramFactory.create()) mock_strategy = mock.Mock() mock_strategy.session.load.return_value = {} mock_strategy.session.get.return_value = {} backend = edxorg.EdxOrgOAuth2(strategy=mock_strategy) pipeline_api.update_profile_from_edx(backend, user, {}, False, **{'edx_profile': self.mocked_edx_profile}) mock_strategy.session_set.assert_called_with('next', '/learners')
def test_login_redirect_next_for_user_already_logged_in(self): """ A student should be directed to what's in the next query parameter if user is already logged in to MM. """ student = UserFactory.create() next_url = "/discussion" mock_strategy = mock.Mock() mock_strategy.session.load.return_value = {} mock_strategy.session.get.return_value = next_url backend = edxorg.EdxOrgOAuth2(strategy=mock_strategy) pipeline_api.update_profile_from_edx( backend, student, {}, False, **{'edx_profile': self.mocked_edx_profile}) mock_strategy.session_set.assert_called_with('next', next_url)
def test_login_redirect_next(self): """ A student should be directed to what's in the next query parameter """ student = UserFactory.create() next_url = "/x/y?a=bc" mock_strategy = mock.Mock() mock_strategy.session.load.return_value = {"next": next_url} mock_strategy.session.get.return_value = {} backend = edxorg.EdxOrgOAuth2(strategy=mock_strategy) pipeline_api.update_profile_from_edx( backend, student, {}, False, **{'edx_profile': self.mocked_edx_profile}) mock_strategy.session_set.assert_called_with('next', next_url)
def test_save_and_log_model(self): """ Tests that the save_model() function on OrderAdmin creates an OrderAudit entry """ assert OrderAudit.objects.count() == 0 order = OrderFactory.create() admin = OrderAdmin(model=order, admin_site=Mock()) mock_request = Mock(user=UserFactory.create()) admin.save_model( request=mock_request, obj=admin.model, form=Mock(), change=Mock() ) assert OrderAudit.objects.count() == 1
def test_sync_discussion_users_task_api_error(mocker): """ Test that sync_discussion_users logs errors if they occur """ mock_api = mocker.patch('discussions.api.create_or_update_discussion_user', autospec=True) user = UserFactory.create() # don't count the one triggered by signals on UserFactory.create() mock_api.reset_mock() mock_log = mocker.patch('discussions.tasks.log', autospec=True) mock_api.side_effect = DiscussionUserSyncException() tasks.sync_discussion_users() mock_api.assert_called_once_with(user.id) mock_log.error.assert_called_once_with( "Impossible to sync user_id %s to discussions", user.id)
def setUpTestData(cls): super(DashboardTokensTest, cls).setUpTestData() # create an user cls.user = UserFactory.create() # create a social auth for the user cls.user.social_auth.create( provider=EdxOrgOAuth2.name, uid="{}_edx".format(cls.user.username), extra_data= '{"access_token": "fooooootoken", "refresh_token": "baaaarrefresh"}' ) cls.enrollments = Enrollments([]) # url for the dashboard cls.url = reverse('dashboard_api')
def test_login_redirect_learners(self): """ Staff or instructors should be directed to /learners """ for role in [Staff.ROLE_ID, Instructor.ROLE_ID]: user = UserFactory.create() Role.objects.create(user=user, role=role, program=ProgramFactory.create()) mock_strategy = mock.Mock() mock_strategy.session.load.return_value = {} mock_strategy.session.get.return_value = {} backend = edxorg.EdxOrgOAuth2(strategy=mock_strategy) pipeline_api.update_profile_from_edx( backend, user, {}, False, **{'edx_profile': self.mocked_edx_profile}) mock_strategy.session_set.assert_called_with('next', '/learners')
def test_add_channel(settings, mock_staff_client, mocker, patched_users_api): """add_channel should tell open-discussions to create a channel""" mock_staff_client.channels.create.return_value.ok = True settings.FEATURES['OPEN_DISCUSSIONS_USER_UPDATE'] = True title = "title" name = "name" description = "description" channel_type = "private" input_search = Search.from_dict({"unmodified": "search"}) modified_search = Search.from_dict({"result": "modified"}) adjust_search_for_percolator_stub = mocker.patch( 'discussions.api.adjust_search_for_percolator', autospec=True, return_value=modified_search, ) program = ProgramFactory.create() contributors = [UserFactory.create() for _ in range(5)] for user in contributors: ProgramEnrollmentFactory.create(user=user, program=program) populate_memberships_task_stub = mocker.patch('search.api.populate_query_memberships', autospec=True) add_moderators_task_stub = mocker.patch('discussions.api.add_moderators_to_channel', autospec=True) add_subscriber_stub = mocker.patch('discussions.api.add_subscriber_to_channel', autospec=True) add_moderator_stub = mocker.patch('discussions.api.add_moderator_to_channel', autospec=True) mod = UserFactory.create() channel = api.add_channel( original_search=input_search, title=title, name=name, description=description, channel_type=channel_type, program_id=program.id, creator_id=mod.id, ) mock_staff_client.channels.create.assert_called_once_with( title=title, name=name, description=description, channel_type=channel_type, ) adjust_search_for_percolator_stub.assert_called_once_with(input_search) assert channel.name == name query = channel.query assert query.source_type == PercolateQuery.DISCUSSION_CHANNEL_TYPE assert query.original_query == input_search.to_dict() assert query.query == modified_search.to_dict() assert ChannelProgram.objects.count() == 1 channel_program = ChannelProgram.objects.first() assert channel_program.program == program assert channel_program.channel == channel populate_memberships_task_stub.assert_called_once_with(query.id) add_moderators_task_stub.assert_called_once_with(channel.name) add_subscriber_stub.assert_called_once_with(channel.name, mod.discussion_user.username) add_moderator_stub.assert_called_once_with(channel.name, mod.discussion_user.username) _, updated_stub = patched_users_api updated_stub.assert_any_call(mod.discussion_user, allow_email_optin=False)
def test_sync_channel_memberships_api_error(mocker, patched_users_api): """ sync_user_to_channels should not fail hard on a sync exception """ user = UserFactory.create() # member here means the user matches the percolate query of the channel channels_to_add = [ChannelFactory.create() for _ in range(4)] channels_to_remove = [ChannelFactory.create() for _ in range(3)] programs = [ ChannelProgramFactory.create(channel=channel).program for channel in (channels_to_add + channels_to_remove) ] memberships_to_add = [ PercolateQueryMembership.objects.create(user=user, query=channel.query, needs_update=True, is_member=True) for channel in channels_to_add ] memberships_to_remove = [ PercolateQueryMembership.objects.create(user=user, query=channel.query, needs_update=True, is_member=False) for channel in channels_to_remove ] # Enroll the user in all programs. This isn't technically required but it's unrealistic to have a query # matching a user if they are not enrolled in the program. for program in programs: ProgramEnrollmentFactory.create(program=program, user=user) # One percolate query per channel assert PercolateQuery.objects.count() == len(channels_to_add) + len(channels_to_remove) # these are the first calls to be made for either change add_contributor_stub = mocker.patch( 'discussions.api.add_contributor_to_channel', autospec=True, side_effect=DiscussionUserSyncException ) remove_subscriber_stub = mocker.patch( 'discussions.api.remove_subscriber_from_channel', autospec=True, side_effect=DiscussionUserSyncException ) api.sync_channel_memberships(api.get_membership_ids_needing_sync()) created_stub, _ = patched_users_api created_stub.assert_any_call(user.discussion_user) assert add_contributor_stub.call_count == len(channels_to_add) assert remove_subscriber_stub.call_count == len(channels_to_remove) # should still need updates since everything failed for membership in memberships_to_add + memberships_to_remove: membership.refresh_from_db() assert membership.needs_update is True for channel in channels_to_add: add_contributor_stub.assert_any_call(channel.name, user.discussion_user.username) for channel in channels_to_remove: remove_subscriber_stub.assert_any_call(channel.name, user.discussion_user.username)
def test_sync_channel_memberships(mocker, patched_users_api): """ sync_user_to_channels should add or remove the user's membership from channels, not touching channels where the user is a moderator of at least one program """ user = UserFactory.create() # member here means the user matches the percolate query of the channel member_channels = [ChannelFactory.create() for _ in range(4)] nonmember_channels = [ChannelFactory.create() for _ in range(3)] # first channel of members and first channel of nonmembers are skipped since user is staff channels_to_add = member_channels[1:] channels_to_remove = nonmember_channels[1:] # User is a staff of some channels and not of others. # Note that a staff user may or may not match the percolate query or a channel staff_programs = [ ChannelProgramFactory.create(channel=member_channels[0]).program, ChannelProgramFactory.create(channel=nonmember_channels[0]).program, ] non_staff_programs = [ ChannelProgramFactory.create(channel=channel).program for channel in (channels_to_add + channels_to_remove) ] memberships_to_add = [ PercolateQueryMembership.objects.create(user=user, query=channel.query, needs_update=True, is_member=True) for channel in member_channels ] memberships_to_remove = [ PercolateQueryMembership.objects.create(user=user, query=channel.query, needs_update=True, is_member=False) for channel in nonmember_channels ] for program in staff_programs: with mute_signals(post_save): RoleFactory.create(program=program, user=user, role=Staff.ROLE_ID) # Enroll the user in all programs. This isn't technically required but it's unrealistic to have a query # matching a user if they are not enrolled in the program. for program in staff_programs + non_staff_programs: ProgramEnrollmentFactory.create(program=program, user=user) # One percolate query per channel assert PercolateQuery.objects.count() == len(member_channels) + len(nonmember_channels) add_subscriber_stub = mocker.patch( 'discussions.api.add_subscriber_to_channel', autospec=True, ) add_contributor_stub = mocker.patch( 'discussions.api.add_contributor_to_channel', autospec=True, ) remove_subscriber_stub = mocker.patch( 'discussions.api.remove_subscriber_from_channel', autospec=True, ) remove_contributor_stub = mocker.patch( 'discussions.api.remove_contributor_from_channel', autospec=True, ) api.sync_channel_memberships(api.get_membership_ids_needing_sync()) created_stub, _ = patched_users_api created_stub.assert_any_call(user.discussion_user) assert add_subscriber_stub.call_count == len(channels_to_add) assert add_contributor_stub.call_count == len(channels_to_add) assert remove_subscriber_stub.call_count == len(channels_to_remove) assert remove_contributor_stub.call_count == len(channels_to_remove) for membership in memberships_to_add + memberships_to_remove: membership.refresh_from_db() assert membership.needs_update is False for channel in channels_to_add: add_subscriber_stub.assert_any_call(channel.name, user.discussion_user.username) add_contributor_stub.assert_any_call(channel.name, user.discussion_user.username) for channel in channels_to_remove: remove_contributor_stub.assert_any_call(channel.name, user.discussion_user.username) remove_subscriber_stub.assert_any_call(channel.name, user.discussion_user.username)