Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 96 additions & 45 deletions cms/djangoapps/appsembler/tests/test_multi_tenant_with_login.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,52 @@
"""
Tests for APPSEMBLER_MULTI_TENANT_EMAILS in Studio login.

Special note:

This test module needs to patch `cms.urls.urlpatterns` to include urlpatterns
from `cms.djangoapps.appsembler.urls`. This works by overriding the
`doango.conf.settings.ROOT_URLCONF` with `django.test.utils.override_settings`
at the TestCase class level with the `urlpatterns` list declared in the module
containing the TestCase class.

For this test module, we've added a `urlpatterns` module level variable and
assigned it the value of `cms.urls.urlpatterns` then appended the conditionally
included urlpatterns we need to run the tests.

Then we add `@override_settings(ROOT_URLCONF=__name__)` to the TestClass

There are other ways to do this. However, this is simple and does not require
our code to explicitly hack `sys.modules` reloading
"""

import ddt
import pytest
from unittest import skipIf
from django.core.exceptions import MultipleObjectsReturned
from django.conf.urls import include, url
from django.urls import reverse
from bs4 import BeautifulSoup as soup
from mock import patch
from django.conf import settings
from django.test import TestCase
from django.urls import reverse
from django.test.utils import override_settings
from rest_framework import status

from student.roles import CourseAccessRole, CourseCreatorRole, CourseInstructorRole, CourseStaffRole

from student.tests.factories import UserFactory

import cms.urls
from cms.djangoapps.appsembler.views import LoginView


# Set the urlpatterns we want to use for our tests in this module only
urlpatterns = cms.urls.urlpatterns + [
url(r'', include('cms.djangoapps.appsembler.urls'))
]


@ddt.ddt
@skipIf(settings.TAHOE_TEMP_MONKEYPATCHING_JUNIPER_TESTS, 'RED-1571: Refactor')
@override_settings(ROOT_URLCONF=__name__) # the module that contains `urlpatterns`
@patch.dict('django.conf.settings.FEATURES', {'APPSEMBLER_MULTI_TENANT_EMAILS': True})
@patch.dict('django.conf.settings.FEATURES', {'TAHOE_STUDIO_LOCAL_LOGIN': True})
class MultiTenantStudioLoginTestCase(TestCase):
"""
Testing the APPSEMBLER_MULTI_TENANT_EMAILS feature when enabled in Studio.
Expand All @@ -35,20 +62,12 @@ class MultiTenantStudioLoginTestCase(TestCase):

def setUp(self):
super(MultiTenantStudioLoginTestCase, self).setUp()
self.url = reverse('login_post') # CMS login endpoint.
self.url = reverse('login')
self.customer = UserFactory.create(email=self.EMAIL, password=self.PASSWORD)

def test_login_with_course_creator_role(self):
"""
Test the APPSEMBLER_MULTI_TENANT_EMAILS feature when enabled in Studio for CourseCreatorRole.
"""
CourseAccessRole.objects.create(user=self.customer, role=CourseCreatorRole.ROLE)
response = self.client.post(self.url, {
'email': self.EMAIL,
'password': self.PASSWORD,
})
assert response.status_code == status.HTTP_200_OK, response.content
assert response.json()['success'], response.content
def get_error_message_text(self, response):
return soup(response.content,
'html.parser').find(id='login_error').p.get_text()

def test_login_no_course_creator(self):
"""
Expand All @@ -59,23 +78,33 @@ def test_login_no_course_creator(self):
'password': self.PASSWORD,
})
assert response.status_code == status.HTTP_200_OK, response.content
assert not response.json()['success'], response.content
assert response.json()['value'] == self.FAILURE_MESSAGE, response.content
# Assert we do NOT have a logged-in session (authorized user)
self.assertNotIn('_auth_user_id', self.client.session)
assert response['Content-Type'] == 'text/html; charset=utf-8'
error_message = self.get_error_message_text(response)
assert error_message == LoginView.error_messages['invalid_login']

@ddt.data(CourseStaffRole.ROLE, CourseInstructorRole.ROLE)
def test_login_for_course_staff(self, course_role_name):
@ddt.data(CourseStaffRole.ROLE, CourseInstructorRole.ROLE, CourseCreatorRole.ROLE)
def test_login_for_course_access_role(self, course_role_name):
"""
Test the APPSEMBLER_MULTI_TENANT_EMAILS feature when enabled in Studio for Course{Instructor,Staff}Role's.
Test the APPSEMBLER_MULTI_TENANT_EMAILS feature when enabled in Studio
for Course{Instructor,Staff}Role's.
"""
CourseAccessRole.objects.create(user=self.customer, role=course_role_name)
response = self.client.post(self.url, {
'email': self.EMAIL,
'password': self.PASSWORD,
})
assert response.status_code == status.HTTP_200_OK, response.content
assert response.json()['success'], response.content

@patch('student.views.login.log')
assert response.status_code == status.HTTP_302_FOUND, response.content
assert not response.content
new_url = response.url
response = self.client.get(new_url)
assert response.status_code == status.HTTP_200_OK
# Assert we DO have a logged-in session (authorized user)
self.assertIn('_auth_user_id', self.client.session)
assert response['Content-Type'] == 'text/html; charset=utf-8'

@patch('cms.djangoapps.appsembler.views.logger')
def test_error_on_two_emails_found(self, mock_log):
"""
Test that two users with CourseCreatorRole if found 500 shows up.
Expand All @@ -97,35 +126,48 @@ def test_error_on_two_emails_found(self, mock_log):
})
assert mock_log.exception.called, 'Should be called to log our custom message'

@ddt.data(CourseStaffRole.ROLE, CourseInstructorRole.ROLE)
def test_login_for_course_staff(self, course_role_name):
@pytest.mark.skip(reason="For now, we mandate Studio users have a unique email address")
def test_login_for_course_staff_but_learner_on_another_site_original(self):
"""
Test the APPSEMBLER_MULTI_TENANT_EMAILS feature when enabled in Studio for Course{Instructor,Staff}Role's.
Test the login for a learner in a site but a staff in another.

When APPSEMBLER_MULTI_TENANT_EMAILS feature when enabled in Studio
"""
CourseAccessRole.objects.create(user=self.customer, role=course_role_name)
# Add a learner with the same email.
UserFactory.create(email=self.EMAIL, password='another_password')

CourseAccessRole.objects.create(user=self.customer, role=CourseStaffRole.ROLE)
response = self.client.post(self.url, {
'email': self.EMAIL,
'password': self.PASSWORD,
})
assert response.status_code == status.HTTP_200_OK, response.content
assert response.json()['success'], response.content

def test_login_for_course_staff_but_learner_on_another_site(self):
assert response.status_code == status.HTTP_302_FOUND, response.content
assert not response.content
new_url = response.url
response = self.client.get(new_url)
assert response.status_code == status.HTTP_200_OK
# Assert we DO have a logged-in session (authorized user)
self.assertIn('_auth_user_id', self.client.session)
assert response['Content-Type'] == 'text/html; charset=utf-8'

@patch('cms.djangoapps.appsembler.views.logger')
def test_login_for_course_staff_but_learner_on_another_site(self, mock_log):
"""
Test the login for a learner in a site but a staff in another.

When APPSEMBLER_MULTI_TENANT_EMAILS feature when enabled in Studio
"""
# Add a learner with the same email.
_learner_2 = UserFactory.create(email=self.EMAIL, password='another_password')
UserFactory.create(email=self.EMAIL, password='another_password')

CourseAccessRole.objects.create(user=self.customer, role=CourseStaffRole.ROLE)
response = self.client.post(self.url, {
'email': self.EMAIL,
'password': self.PASSWORD,
})
assert response.status_code == status.HTTP_200_OK, response.content
assert response.json()['success'], response.content
assert not mock_log.exception.called, 'Not to be called yet'
with pytest.raises(MultipleObjectsReturned):
self.client.post(self.url, {
'email': self.EMAIL,
'password': self.PASSWORD,
})
assert mock_log.exception.called, 'Should be called to log our custom message'

def test_login_for_course_staff_in_two_courses(self):
"""
Expand All @@ -140,8 +182,14 @@ def test_login_for_course_staff_in_two_courses(self):
'email': self.EMAIL,
'password': self.PASSWORD,
})
assert response.status_code == status.HTTP_200_OK, response.content
assert response.json()['success'], response.content
assert response.status_code == status.HTTP_302_FOUND, response.content
assert not response.content
new_url = response.url
response = self.client.get(new_url)
assert response.status_code == status.HTTP_200_OK
# Assert we DO have a logged-in session (authorized user)
self.assertIn('_auth_user_id', self.client.session)
assert response['Content-Type'] == 'text/html; charset=utf-8'

def test_failed_login(self):
"""
Expand All @@ -153,5 +201,8 @@ def test_failed_login(self):
'password': 'wrong_password',
})
assert response.status_code == status.HTTP_200_OK, response.content
assert not response.json()['success'], response.content
assert response.json()['value'] == self.FAILURE_MESSAGE
# Assert we do NOT have a logged-in session (authorized user)
self.assertNotIn('_auth_user_id', self.client.session)
assert response['Content-Type'] == 'text/html; charset=utf-8'
error_message = self.get_error_message_text(response)
assert error_message == LoginView.error_messages['invalid_login']
Loading