Commit 257dae63 by Calen Pennington

Merge pull request #2090 from edx/fix/cale/amazon-ses-errors

Fix/cale/amazon ses errors
parents 9e542aa5 f521b8e8
from student.models import (User, UserProfile, Registration, from student.models import (User, UserProfile, Registration,
CourseEnrollmentAllowed, CourseEnrollment) CourseEnrollmentAllowed, CourseEnrollment,
PendingEmailChange)
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from datetime import datetime from datetime import datetime
from factory import DjangoModelFactory, Factory, SubFactory, PostGenerationMethodCall, post_generation from factory import DjangoModelFactory, SubFactory, PostGenerationMethodCall, post_generation, Sequence
from uuid import uuid4 from uuid import uuid4
# Factories don't have __init__ methods, and are self documenting
# pylint: disable=W0232
class GroupFactory(DjangoModelFactory): class GroupFactory(DjangoModelFactory):
FACTORY_FOR = Group FACTORY_FOR = Group
name = 'staff_MITx/999/Robot_Super_Course' name = u'staff_MITx/999/Robot_Super_Course'
class UserProfileFactory(DjangoModelFactory): class UserProfileFactory(DjangoModelFactory):
FACTORY_FOR = UserProfile FACTORY_FOR = UserProfile
user = None user = None
name = 'Robot Test' name = u'Robot Test'
level_of_education = None level_of_education = None
gender = 'm' gender = u'm'
mailing_address = None mailing_address = None
goals = 'World domination' goals = u'World domination'
class RegistrationFactory(DjangoModelFactory): class RegistrationFactory(DjangoModelFactory):
FACTORY_FOR = Registration FACTORY_FOR = Registration
user = None user = None
activation_key = uuid4().hex activation_key = uuid4().hex.decode('ascii')
class UserFactory(DjangoModelFactory): class UserFactory(DjangoModelFactory):
FACTORY_FOR = User FACTORY_FOR = User
username = 'robot' username = Sequence(u'robot{0}'.format)
email = 'robot+test@edx.org' email = Sequence(u'robot+test+{0}@edx.org'.format)
password = PostGenerationMethodCall('set_password', password = PostGenerationMethodCall('set_password',
'test') 'test')
first_name = 'Robot' first_name = Sequence(u'Robot{0}'.format)
last_name = 'Test' last_name = 'Test'
is_staff = False is_staff = False
is_active = True is_active = True
...@@ -64,7 +68,7 @@ class CourseEnrollmentFactory(DjangoModelFactory): ...@@ -64,7 +68,7 @@ class CourseEnrollmentFactory(DjangoModelFactory):
FACTORY_FOR = CourseEnrollment FACTORY_FOR = CourseEnrollment
user = SubFactory(UserFactory) user = SubFactory(UserFactory)
course_id = 'edX/toy/2012_Fall' course_id = u'edX/toy/2012_Fall'
class CourseEnrollmentAllowedFactory(DjangoModelFactory): class CourseEnrollmentAllowedFactory(DjangoModelFactory):
...@@ -72,3 +76,17 @@ class CourseEnrollmentAllowedFactory(DjangoModelFactory): ...@@ -72,3 +76,17 @@ class CourseEnrollmentAllowedFactory(DjangoModelFactory):
email = 'test@edx.org' email = 'test@edx.org'
course_id = 'edX/test/2012_Fall' course_id = 'edX/test/2012_Fall'
class PendingEmailChangeFactory(DjangoModelFactory):
"""Factory for PendingEmailChange objects
user: generated by UserFactory
new_email: sequence of new+email+{}@edx.org
activation_key: sequence of integers, padded to 30 characters
"""
FACTORY_FOR = PendingEmailChange
user = SubFactory(UserFactory)
new_email = Sequence(u'new+email+{0}@edx.org'.format)
activation_key = Sequence(u'{:0<30d}'.format)
...@@ -19,7 +19,7 @@ from django.core.context_processors import csrf ...@@ -19,7 +19,7 @@ from django.core.context_processors import csrf
from django.core.mail import send_mail from django.core.mail import send_mail
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.validators import validate_email, validate_slug, ValidationError from django.core.validators import validate_email, validate_slug, ValidationError
from django.db import IntegrityError from django.db import IntegrityError, transaction
from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotAllowed, HttpResponseRedirect, Http404 from django.http import HttpResponse, HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotAllowed, HttpResponseRedirect, Http404
from django.shortcuts import redirect from django.shortcuts import redirect
from django_future.csrf import ensure_csrf_cookie, csrf_exempt from django_future.csrf import ensure_csrf_cookie, csrf_exempt
...@@ -655,7 +655,7 @@ def create_account(request, post_override=None): ...@@ -655,7 +655,7 @@ def create_account(request, post_override=None):
elif not settings.GENERATE_RANDOM_USER_CREDENTIALS: elif not settings.GENERATE_RANDOM_USER_CREDENTIALS:
res = user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL) res = user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
except: except:
log.exception(sys.exc_info()) log.warning('Unable to send activation email to user', exc_info=True)
js['value'] = 'Could not send activation e-mail.' js['value'] = 'Could not send activation e-mail.'
return HttpResponse(json.dumps(js)) return HttpResponse(json.dumps(js))
...@@ -975,7 +975,11 @@ def reactivation_email_for_user(user): ...@@ -975,7 +975,11 @@ def reactivation_email_for_user(user):
subject = ''.join(subject.splitlines()) subject = ''.join(subject.splitlines())
message = render_to_string('emails/activation_email.txt', d) message = render_to_string('emails/activation_email.txt', d)
try:
res = user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL) res = user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
except:
log.warning('Unable to send reactivation email', exc_info=True)
return HttpResponse(json.dumps({'success': False, 'error': 'Unable to send reactivation email'}))
return HttpResponse(json.dumps({'success': True})) return HttpResponse(json.dumps({'success': True}))
...@@ -1001,7 +1005,7 @@ def change_email_request(request): ...@@ -1001,7 +1005,7 @@ def change_email_request(request):
return HttpResponse(json.dumps({'success': False, return HttpResponse(json.dumps({'success': False,
'error': 'Valid e-mail address required.'})) 'error': 'Valid e-mail address required.'}))
if len(User.objects.filter(email=new_email)) != 0: if User.objects.filter(email=new_email).count() != 0:
## CRITICAL TODO: Handle case sensitivity for e-mails ## CRITICAL TODO: Handle case sensitivity for e-mails
return HttpResponse(json.dumps({'success': False, return HttpResponse(json.dumps({'success': False,
'error': 'An account with this e-mail already exists.'})) 'error': 'An account with this e-mail already exists.'}))
...@@ -1036,25 +1040,31 @@ def change_email_request(request): ...@@ -1036,25 +1040,31 @@ def change_email_request(request):
@ensure_csrf_cookie @ensure_csrf_cookie
@transaction.commit_manually
def confirm_email_change(request, key): def confirm_email_change(request, key):
''' User requested a new e-mail. This is called when the activation ''' User requested a new e-mail. This is called when the activation
link is clicked. We confirm with the old e-mail, and update link is clicked. We confirm with the old e-mail, and update
''' '''
try: try:
try:
pec = PendingEmailChange.objects.get(activation_key=key) pec = PendingEmailChange.objects.get(activation_key=key)
except PendingEmailChange.DoesNotExist: except PendingEmailChange.DoesNotExist:
transaction.rollback()
return render_to_response("invalid_email_key.html", {}) return render_to_response("invalid_email_key.html", {})
user = pec.user user = pec.user
d = {'old_email': user.email, address_context = {
'new_email': pec.new_email} 'old_email': user.email,
'new_email': pec.new_email
}
if len(User.objects.filter(email=pec.new_email)) != 0: if len(User.objects.filter(email=pec.new_email)) != 0:
return render_to_response("email_exists.html", d) transaction.rollback()
return render_to_response("email_exists.html", {})
subject = render_to_string('emails/email_change_subject.txt', d) subject = render_to_string('emails/email_change_subject.txt', address_context)
subject = ''.join(subject.splitlines()) subject = ''.join(subject.splitlines())
message = render_to_string('emails/confirm_email_change.txt', d) message = render_to_string('emails/confirm_email_change.txt', address_context)
up = UserProfile.objects.get(user=user) up = UserProfile.objects.get(user=user)
meta = up.get_meta() meta = up.get_meta()
if 'old_emails' not in meta: if 'old_emails' not in meta:
...@@ -1063,14 +1073,30 @@ def confirm_email_change(request, key): ...@@ -1063,14 +1073,30 @@ def confirm_email_change(request, key):
up.set_meta(meta) up.set_meta(meta)
up.save() up.save()
# Send it to the old email... # Send it to the old email...
try:
user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL) user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
except Exception:
transaction.rollback()
log.warning('Unable to send confirmation email to old address', exc_info=True)
return render_to_response("email_change_failed.html", {'email': user.email})
user.email = pec.new_email user.email = pec.new_email
user.save() user.save()
pec.delete() pec.delete()
# And send it to the new email... # And send it to the new email...
try:
user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL) user.email_user(subject, message, settings.DEFAULT_FROM_EMAIL)
except Exception:
transaction.rollback()
log.warning('Unable to send confirmation email to new address', exc_info=True)
return render_to_response("email_change_failed.html", {'email': pec.new_email})
return render_to_response("email_change_successful.html", d) transaction.commit()
return render_to_response("email_change_successful.html", address_context)
except Exception:
# If we get an unexpected exception, be sure to rollback the transaction
transaction.rollback()
raise
@ensure_csrf_cookie @ensure_csrf_cookie
......
...@@ -117,12 +117,11 @@ xmodule can be tested independently, with this: ...@@ -117,12 +117,11 @@ xmodule can be tested independently, with this:
To run a single django test class: To run a single django test class:
django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/courseware/tests/tests.py:TestViewAuth rake test_lms[courseware.tests.tests:testViewAuth]
To run a single django test: To run a single django test:
django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/courseware/tests/tests.py:TestViewAuth.test_dark_launch rake test_lms[courseware.tests.tests:TestViewAuth.test_dark_launch]
To run a single nose test file: To run a single nose test file:
......
...@@ -73,8 +73,8 @@ rake pylint > pylint.log || cat pylint.log ...@@ -73,8 +73,8 @@ rake pylint > pylint.log || cat pylint.log
TESTS_FAILED=0 TESTS_FAILED=0
# Run the python unit tests # Run the python unit tests
rake test_cms[false] || TESTS_FAILED=1 rake test_cms || TESTS_FAILED=1
rake test_lms[false] || TESTS_FAILED=1 rake test_lms || TESTS_FAILED=1
rake test_common/lib/capa || TESTS_FAILED=1 rake test_common/lib/capa || TESTS_FAILED=1
rake test_common/lib/xmodule || TESTS_FAILED=1 rake test_common/lib/xmodule || TESTS_FAILED=1
......
<h1>E-mail change failed.</h1>
<p>We were unable to send a confirmation email to ${email}</p>
...@@ -110,7 +110,9 @@ generated-members= ...@@ -110,7 +110,9 @@ generated-members=
get_url, get_url,
size, size,
content, content,
status_code status_code,
# For factory_body factories
create
[BASIC] [BASIC]
......
...@@ -12,10 +12,11 @@ def run_under_coverage(cmd, root) ...@@ -12,10 +12,11 @@ def run_under_coverage(cmd, root)
return cmd return cmd
end end
def run_tests(system, report_dir, stop_on_failure=true) def run_tests(system, report_dir, test_id=nil, stop_on_failure=true)
ENV['NOSE_XUNIT_FILE'] = File.join(report_dir, "nosetests.xml") ENV['NOSE_XUNIT_FILE'] = File.join(report_dir, "nosetests.xml")
dirs = Dir["common/djangoapps/*"] + Dir["#{system}/djangoapps/*"] dirs = Dir["common/djangoapps/*"] + Dir["#{system}/djangoapps/*"]
cmd = django_admin(system, :test, 'test', '--logging-clear-handlers', *dirs.each) test_id = dirs.join(' ') if test_id.nil? or test_id == ''
cmd = django_admin(system, :test, 'test', '--logging-clear-handlers', test_id)
sh(run_under_coverage(cmd, system)) do |ok, res| sh(run_under_coverage(cmd, system)) do |ok, res|
if !ok and stop_on_failure if !ok and stop_on_failure
abort "Test failed!" abort "Test failed!"
...@@ -44,13 +45,13 @@ TEST_TASK_DIRS = [] ...@@ -44,13 +45,13 @@ TEST_TASK_DIRS = []
# Per System tasks # Per System tasks
desc "Run all django tests on our djangoapps for the #{system}" desc "Run all django tests on our djangoapps for the #{system}"
task "test_#{system}", [:stop_on_failure] => ["clean_test_files", :predjango, "#{system}:gather_assets:test", "fasttest_#{system}"] task "test_#{system}", [:test_id, :stop_on_failure] => ["clean_test_files", :predjango, "#{system}:gather_assets:test", "fasttest_#{system}"]
# Have a way to run the tests without running collectstatic -- useful when debugging without # Have a way to run the tests without running collectstatic -- useful when debugging without
# messing with static files. # messing with static files.
task "fasttest_#{system}", [:stop_on_failure] => [report_dir, :install_prereqs, :predjango] do |t, args| task "fasttest_#{system}", [:test_id, :stop_on_failure] => [report_dir, :install_prereqs, :predjango] do |t, args|
args.with_defaults(:stop_on_failure => 'true') args.with_defaults(:stop_on_failure => 'true', :test_id => nil)
run_tests(system, report_dir, args.stop_on_failure) run_tests(system, report_dir, args.test_id, args.stop_on_failure)
end end
# Run acceptance tests # Run acceptance tests
...@@ -100,7 +101,7 @@ end ...@@ -100,7 +101,7 @@ end
task :test do task :test do
TEST_TASK_DIRS.each do |dir| TEST_TASK_DIRS.each do |dir|
Rake::Task["test_#{dir}"].invoke(false) Rake::Task["test_#{dir}"].invoke(nil, false)
end end
if $failed_tests > 0 if $failed_tests > 0
......
...@@ -71,7 +71,7 @@ transifex-client==0.8 ...@@ -71,7 +71,7 @@ transifex-client==0.8
coverage==3.6 coverage==3.6
factory_boy==2.0.2 factory_boy==2.0.2
lettuce==0.2.16 lettuce==0.2.16
mock==0.8.0 mock==1.0.1
nosexcover==1.0.7 nosexcover==1.0.7
pep8==1.4.5 pep8==1.4.5
pylint==0.28 pylint==0.28
......
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