Commit 5eb38a5f by Clinton Blackburn

Merge pull request #301 from edx/django-1.8-fixes

Django 1.8 Fixes
parents e12b321c f67b2e1f
import logging
import uuid
import requests
from ecommerce_api_client.client import EcommerceApiClient
import requests
from acceptance_tests.api import EnrollmentApiClient
from acceptance_tests.config import (ENABLE_LMS_AUTO_AUTH, APP_SERVER_URL, LMS_PASSWORD, LMS_EMAIL, LMS_URL,
BASIC_AUTH_USERNAME, BASIC_AUTH_PASSWORD, ECOMMERCE_API_SERVER_URL,
LMS_USERNAME, ECOMMERCE_API_TOKEN)
from acceptance_tests.pages import LMSLoginPage, LMSDashboardPage
from acceptance_tests.pages import LMSLoginPage, LMSDashboardPage, LMSRegistrationPage
log = logging.getLogger(__name__)
class LoginMixin(object):
def setUp(self):
super(LoginMixin, self).setUp()
self.lms_login_page = LMSLoginPage(self.browser)
def login(self):
self.login_with_lms()
def login_with_lms(self, email=None, password=None, course_id=None):
""" Visit LMS and login. """
email = email or LMS_EMAIL
password = password or LMS_PASSWORD
# Note: We use Selenium directly here (as opposed to bok-choy) to avoid issues with promises being broken.
self.lms_login_page.browser.get(self.lms_login_page.url(course_id)) # pylint: disable=not-callable
self.lms_login_page.login(email, password)
class LogoutMixin(object):
def logout(self):
url = '{}/accounts/logout/'.format(APP_SERVER_URL)
self.browser.get(url)
class LmsUserMixin(object):
password = 'edx'
......@@ -46,10 +22,14 @@ class LmsUserMixin(object):
return LMS_USERNAME, LMS_PASSWORD, LMS_EMAIL
def create_lms_user(self, username=None, password=None, email=None):
username = username or ('auto_auth_' + uuid.uuid4().hex[0:20])
password = password or 'edx'
email = email or '{}@example.com'.format(username)
def generate_user_credentials(self, username_prefix):
username = username_prefix + uuid.uuid4().hex[0:20]
password = self.password
email = '{}@example.com'.format(username)
return username, email, password
def create_lms_user(self):
username, email, password = self.generate_user_credentials(username_prefix='auto_auth_')
url = '{host}/auto_auth?no_login=true&username={username}&password={password}&email={email}'.format(
host=LMS_URL, username=username, password=password, email=email)
......@@ -63,6 +43,40 @@ class LmsUserMixin(object):
return username, password, email
class LogistrationMixin(LmsUserMixin):
def setUp(self):
super(LogistrationMixin, self).setUp()
self.lms_login_page = LMSLoginPage(self.browser)
self.lms_registration_page = LMSRegistrationPage(self.browser)
def login(self):
self.login_with_lms()
def login_with_lms(self, email=None, password=None, course_id=None):
""" Visit LMS and login. """
email = email or LMS_EMAIL
password = password or LMS_PASSWORD
# Note: We use Selenium directly here (as opposed to bok-choy) to avoid issues with promises being broken.
self.lms_login_page.browser.get(self.lms_login_page.url(course_id)) # pylint: disable=not-callable
self.lms_login_page.login(email, password)
def register_via_ui(self, course_id=None):
""" Creates a new account via the normal user interface. """
username, email, password = self.generate_user_credentials(username_prefix='otto_acceptance_')
url = self.lms_registration_page.url(course_id) # pylint: disable=not-callable
self.lms_registration_page.browser.get(url)
self.lms_registration_page.register_and_login(username, username, email, password)
return username, email, password
class LogoutMixin(object):
def logout(self):
url = '{}/accounts/logout/'.format(APP_SERVER_URL)
self.browser.get(url)
class EnrollmentApiMixin(object):
def setUp(self):
super(EnrollmentApiMixin, self).setUp()
......
......@@ -39,6 +39,9 @@ class LMSPage(PageObject): # pylint: disable=abstract-method
return url
def _is_browser_on_lms_dashboard(self):
return lambda: self.browser.title.startswith('Dashboard')
class LMSLoginPage(LMSPage):
def url(self, course_id=None): # pylint: disable=arguments-differ
......@@ -51,10 +54,7 @@ class LMSLoginPage(LMSPage):
return url
def is_browser_on_page(self):
return self.browser.title.startswith('Sign in')
def _is_browser_on_lms_dashboard(self):
return lambda: self.browser.title.startswith('Dashboard')
return self.q(css='form#login').visible
def login(self, username, password):
self.q(css='input#login-email').fill(username)
......@@ -65,6 +65,31 @@ class LMSLoginPage(LMSPage):
EmptyPromise(self._is_browser_on_lms_dashboard(), "LMS login redirected to dashboard").fulfill()
class LMSRegistrationPage(LMSPage):
def url(self, course_id=None): # pylint: disable=arguments-differ
url = self._build_url('register')
if course_id:
params = {'enrollment_action': 'enroll', 'course_id': course_id}
url = '{0}?{1}'.format(url, urllib.urlencode(params))
return url
def is_browser_on_page(self):
return self.q(css='form#register').visible
def register_and_login(self, username, name, email, password):
self.q(css='input#register-username').fill(username)
self.q(css='input#register-name').fill(name)
self.q(css='input#register-email').fill(email)
self.q(css='input#register-password').fill(password)
self.q(css='input#register-honor_code').click()
self.q(css='button.register-button').click()
# Wait for LMS to redirect to the dashboard
EmptyPromise(self._is_browser_on_lms_dashboard(), "LMS login redirected to dashboard").fulfill()
class LMSCourseModePage(LMSPage):
def is_browser_on_page(self):
return self.browser.title.lower().startswith('enroll in')
......
......@@ -3,12 +3,12 @@ from unittest import skipUnless
from bok_choy.web_app_test import WebAppTest
from acceptance_tests.config import ENABLE_OAUTH_TESTS
from acceptance_tests.mixins import LoginMixin
from acceptance_tests.mixins import LogistrationMixin
from acceptance_tests.pages import DashboardHomePage
@skipUnless(ENABLE_OAUTH_TESTS, 'OAuth tests are not enabled.')
class OAuth2FlowTests(LoginMixin, WebAppTest):
class OAuth2FlowTests(LogistrationMixin, WebAppTest):
def setUp(self):
"""
Instantiate the page objects.
......
from bok_choy.web_app_test import WebAppTest
from acceptance_tests.config import COURSE_ID
from acceptance_tests.mixins import LoginMixin, EcommerceApiMixin, EnrollmentApiMixin, LmsUserMixin, UnenrollmentMixin
from acceptance_tests.mixins import LogistrationMixin, EcommerceApiMixin, EnrollmentApiMixin, UnenrollmentMixin
class LoginEnrollmentTests(UnenrollmentMixin, EcommerceApiMixin, EnrollmentApiMixin, LmsUserMixin, LoginMixin,
WebAppTest):
class LoginEnrollmentTests(UnenrollmentMixin, EcommerceApiMixin, EnrollmentApiMixin, LogistrationMixin, WebAppTest):
def setUp(self):
super(LoginEnrollmentTests, self).setUp()
self.course_id = COURSE_ID
......@@ -18,3 +17,9 @@ class LoginEnrollmentTests(UnenrollmentMixin, EcommerceApiMixin, EnrollmentApiMi
self.login_with_lms(self.email, self.password, self.course_id)
self.assert_order_created_and_completed()
self.assert_user_enrolled(self.username, self.course_id)
def test_honor_enrollment_and_registration(self):
""" Verifies that a user can register and enroll in a course via the login page. """
username, __, __ = self.register_via_ui(self.course_id)
self.assert_order_created_and_completed()
self.assert_user_enrolled(username, self.course_id)
......@@ -7,13 +7,13 @@ from selenium.webdriver.support.select import Select
from selenium.webdriver.support.ui import WebDriverWait
from acceptance_tests.config import VERIFIED_COURSE_ID, HTTPS_RECEIPT_PAGE, PAYPAL_PASSWORD, PAYPAL_EMAIL
from acceptance_tests.mixins import LoginMixin, EnrollmentApiMixin, EcommerceApiMixin, LmsUserMixin, UnenrollmentMixin
from acceptance_tests.mixins import LogistrationMixin, EnrollmentApiMixin, EcommerceApiMixin, UnenrollmentMixin
from acceptance_tests.pages import LMSCourseModePage
@ddt.ddt
class VerifiedCertificatePaymentTests(UnenrollmentMixin, EcommerceApiMixin, EnrollmentApiMixin, LmsUserMixin,
LoginMixin, WebAppTest):
class VerifiedCertificatePaymentTests(UnenrollmentMixin, EcommerceApiMixin, EnrollmentApiMixin, LogistrationMixin,
WebAppTest):
def setUp(self):
super(VerifiedCertificatePaymentTests, self).setUp()
self.course_id = VERIFIED_COURSE_ID
......
......@@ -2,12 +2,12 @@ from unittest import skip
from bok_choy.web_app_test import WebAppTest
from acceptance_tests.mixins import LoginMixin, LmsUserMixin, EnrollmentApiMixin
from acceptance_tests.mixins import LogistrationMixin, EnrollmentApiMixin
from acceptance_tests.pages import LMSCourseModePage
@skip('Prof. Ed. tests should be run on an as-needed basis.')
class ProfessionalEducationEnrollmentTests(EnrollmentApiMixin, LmsUserMixin, LoginMixin, WebAppTest):
class ProfessionalEducationEnrollmentTests(EnrollmentApiMixin, LogistrationMixin, WebAppTest):
def test_payment_required(self):
""" Verify payment is required before enrolling in a professional education course. """
......
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import django.core.validators
import django.contrib.auth.models
class Migration(migrations.Migration):
dependencies = [
('core', '0001_initial'),
]
operations = [
migrations.AlterModelManagers(
name='user',
managers=[
('objects', django.contrib.auth.models.UserManager()),
],
),
migrations.AlterField(
model_name='user',
name='email',
field=models.EmailField(max_length=254, verbose_name='email address', blank=True),
),
migrations.AlterField(
model_name='user',
name='groups',
field=models.ManyToManyField(related_query_name='user', related_name='user_set', to='auth.Group', blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', verbose_name='groups'),
),
migrations.AlterField(
model_name='user',
name='last_login',
field=models.DateTimeField(null=True, verbose_name='last login', blank=True),
),
migrations.AlterField(
model_name='user',
name='username',
field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, max_length=30, validators=[django.core.validators.RegexValidator('^[\\w.@+-]+$', 'Enter a valid username. This value may contain only letters, numbers and @/./+/-/_ characters.', 'invalid')], help_text='Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.', unique=True, verbose_name='username'),
),
]
"""JWT authentication scheme for use with DRF."""
import logging
from django.conf import settings
from django.contrib.auth import get_user_model
import requests
......@@ -7,7 +9,7 @@ from rest_framework.authentication import get_authorization_header, BaseAuthenti
from rest_framework.status import HTTP_200_OK
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
logger = logging.getLogger(__name__)
User = get_user_model()
......@@ -59,8 +61,10 @@ class JwtAuthentication(JSONWebTokenAuthentication):
is_update = True
if is_update:
user.save()
except: # pragma: no cover
raise exceptions.AuthenticationFailed('User retrieval failed.')
except:
msg = 'User retrieval failed.'
logger.exception(msg)
raise exceptions.AuthenticationFailed(msg)
return user
......
# -*- coding: UTF-8 -*-
from datetime import datetime
import json
from logging import Logger
from django.conf import settings
from django.contrib.auth import get_user_model
from django.http import HttpResponse
from django.conf.urls import url
from django.test import TestCase, override_settings, RequestFactory
import httpretty
import mock
from oscar.test import factories
from rest_framework import permissions
from rest_framework.exceptions import AuthenticationFailed
......@@ -19,6 +22,8 @@ from ecommerce.tests.mixins import JwtMixin, UserMixin
OAUTH2_PROVIDER_URL = 'https://example.com/oauth2'
User = get_user_model()
class MockView(APIView):
permission_classes = (permissions.IsAuthenticated,)
......@@ -185,3 +190,15 @@ class JwtAuthenticationTests(JwtMixin, UserMixin, TestCase):
self.assertEquals(user.username, username)
self.assertEquals(user.email, email)
self.assertEquals(user.full_name, full_name)
def test_user_retrieval_failed(self):
""" Verify exceptions raised during user retrieval are properly logged. """
with mock.patch.object(User.objects, 'get_or_create', side_effect=ValueError):
with mock.patch.object(Logger, 'exception') as logger:
msg = 'User retrieval failed.'
with self.assertRaisesRegexp(AuthenticationFailed, msg):
payload = {'username': 'test', 'email': 'test@example.com', 'full_name': 'Testy'}
JwtAuthentication().authenticate_credentials(payload)
logger.assert_called_with(msg)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment