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
예제 #2
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
예제 #4
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')
예제 #5
0
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
예제 #6
0
    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"}')
예제 #7
0
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
예제 #8
0
    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,
        )
예제 #9
0
    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,
        )
예제 #10
0
    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)}
예제 #11
0
    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, )
예제 #12
0
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)
예제 #13
0
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,
    )
예제 #14
0
 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)
예제 #15
0
 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
예제 #16
0
 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
예제 #18
0
 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
예제 #19
0
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
예제 #20
0
    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})
예제 #21
0
    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)
        }
예제 #22
0
 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')
예제 #23
0
 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')
예제 #24
0
 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']
예제 #25
0
 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
예제 #26
0
    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'}
예제 #27
0
 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')
예제 #28
0
 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"}'
     )
예제 #29
0
 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
예제 #30
0
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)
예제 #31
0
 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
예제 #32
0
 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)
예제 #33
0
 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)
예제 #35
0
 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')
예제 #36
0
 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
예제 #37
0
 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')
예제 #38
0
 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)
예제 #39
0
 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)
예제 #40
0
 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
예제 #41
0
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)
예제 #42
0
    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')
예제 #43
0
 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')
예제 #44
0
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)
예제 #45
0
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)
예제 #46
0
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)