Holger Frey
7 years ago
16 changed files with 294 additions and 27 deletions
@ -0,0 +1,72 @@ |
|||||||
|
''' custom events and event subsribers ''' |
||||||
|
|
||||||
|
from pyramid.events import subscriber |
||||||
|
from pyramid.renderers import render |
||||||
|
from pyramid_mailer import get_mailer |
||||||
|
from pyramid_mailer.message import Message |
||||||
|
|
||||||
|
|
||||||
|
# custom events |
||||||
|
|
||||||
|
class UserNotification(object): |
||||||
|
''' base class for user notification emails |
||||||
|
|
||||||
|
:param pyramid.request.Request request: current request object |
||||||
|
:param ordr.models.account.Users account: send notification to this user |
||||||
|
:param dict data: additional data to pass to the mail template |
||||||
|
''' |
||||||
|
|
||||||
|
#: subject of the notification |
||||||
|
subject = None |
||||||
|
|
||||||
|
#: template to render |
||||||
|
template = None |
||||||
|
|
||||||
|
def __init__(self, request, account, data=None): |
||||||
|
self.request = request |
||||||
|
self.account = account |
||||||
|
self.data = data |
||||||
|
|
||||||
|
|
||||||
|
class ActivationNotification(UserNotification): |
||||||
|
''' user notification for account activation ''' |
||||||
|
subject = '[ordr] Your account was activated' |
||||||
|
template = 'ordr:templates/emails/activation.jinja2' |
||||||
|
|
||||||
|
|
||||||
|
class OrderStatusNotification(UserNotification): |
||||||
|
''' user notification for order status change ''' |
||||||
|
subject = '[ordr] Order Status Change' |
||||||
|
template = 'ordr:templates/emails/order.jinja2' |
||||||
|
|
||||||
|
|
||||||
|
class PasswordResetNotification(UserNotification): |
||||||
|
''' user notification for password reset link ''' |
||||||
|
subject = '[ordr] Password Reset' |
||||||
|
template = 'ordr:templates/emails/password_reset.jinja2' |
||||||
|
|
||||||
|
|
||||||
|
class RegistrationNotification(UserNotification): |
||||||
|
''' user notification for account activation ''' |
||||||
|
subject = '[ordr] Please verify your email address' |
||||||
|
template = 'ordr:templates/emails/registration.jinja2' |
||||||
|
|
||||||
|
|
||||||
|
# subsribers for events |
||||||
|
|
||||||
|
@subscriber(UserNotification) |
||||||
|
def notify_user(event): |
||||||
|
''' notify a user about an event ''' |
||||||
|
body = render( |
||||||
|
event.template, |
||||||
|
{'user': event.account, 'data': event.data}, |
||||||
|
event.request |
||||||
|
) |
||||||
|
message = Message( |
||||||
|
subject=event.subject, |
||||||
|
sender=event.request.registry.settings['mail.default_sender'], |
||||||
|
recipients=[event.account.email], |
||||||
|
html=body |
||||||
|
) |
||||||
|
mailer = get_mailer(event.request.registry) |
||||||
|
mailer.send(message) |
@ -0,0 +1,25 @@ |
|||||||
|
<!DOCTYPE html> |
||||||
|
<html> |
||||||
|
<head> |
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"> |
||||||
|
<title>[ordr] verify your email address</title> |
||||||
|
<link href='http://fonts.googleapis.com/css?family=Anton&subset=latin,latin-ext' rel='stylesheet' type='text/css'> |
||||||
|
</head> |
||||||
|
<body> |
||||||
|
<h1>Hi there!</h1> |
||||||
|
<p> |
||||||
|
Please verify your email address for the account "{{ user.username }}" by following this link |
||||||
|
<a href="{{ request.resource_url(context, data.token.hash) }}">{{ request.resource_url(context, data.token.hash) }}</a> |
||||||
|
</p> |
||||||
|
<p> The link will expire on {{ data.token.expires.strftime('%d.%m.%y at %H:%M') }}. |
||||||
|
<p class="signature"> |
||||||
|
Regards, |
||||||
|
<br/> |
||||||
|
<span class="brand">ordr</span> |
||||||
|
</p> |
||||||
|
<p class="footprint"> |
||||||
|
<small>Please don't respont to this email! This is an automatically generated notification by the system.</small> |
||||||
|
<a href="http://distractedbysquirrels.com/" target="_blank" title="This software was originally written by Sebastian Sebald." class="icon-dbs"></a> |
||||||
|
</p> |
||||||
|
</body> |
||||||
|
</html> |
@ -0,0 +1,36 @@ |
|||||||
|
''' Tests for ordr.events ''' |
||||||
|
|
||||||
|
from datetime import datetime |
||||||
|
from pyramid.testing import DummyRequest |
||||||
|
from pyramid_mailer import get_mailer |
||||||
|
|
||||||
|
from . import app_config, get_example_user # noqa: F401 |
||||||
|
|
||||||
|
|
||||||
|
def test_user_notification_init(app_config): # noqa: F811 |
||||||
|
''' test creation of user notification events ''' |
||||||
|
from ordr.events import UserNotification |
||||||
|
from ordr.models.account import Role |
||||||
|
|
||||||
|
user = get_example_user(Role.USER) |
||||||
|
notification = UserNotification('request', user, 'data') |
||||||
|
|
||||||
|
assert notification.request == 'request' |
||||||
|
assert notification.account == user |
||||||
|
assert notification.data == 'data' |
||||||
|
|
||||||
|
|
||||||
|
def test_notify_user(app_config): # noqa: F811 |
||||||
|
from ordr.events import RegistrationNotification, notify_user |
||||||
|
from ordr.models.account import Token, Role |
||||||
|
|
||||||
|
request = DummyRequest() |
||||||
|
user = get_example_user(Role.USER) |
||||||
|
token = Token(expires=datetime.utcnow(), hash='some_hash') |
||||||
|
notification = RegistrationNotification(request, user, {'token': token}) |
||||||
|
notify_user(notification) |
||||||
|
mailer = get_mailer(request.registry) |
||||||
|
last_mail = mailer.outbox[-1] |
||||||
|
|
||||||
|
assert 'Please verify your email address ' in last_mail.html |
||||||
|
assert 'http://example.com//some_hash' in last_mail.html |
@ -1,12 +1,88 @@ |
|||||||
import pytest |
import pytest |
||||||
|
import deform |
||||||
|
|
||||||
from pyramid.httpexceptions import HTTPFound |
from pyramid.httpexceptions import HTTPFound |
||||||
from pyramid.testing import DummyRequest |
from pyramid.testing import DummyRequest |
||||||
|
|
||||||
from .. import app_config, dbsession # noqa: F401 |
from .. import app_config, dbsession, get_post_request # noqa: F401 |
||||||
|
|
||||||
|
|
||||||
def test_faq(): |
REGISTRATION_FORM_DATA = { |
||||||
|
'username': 'AmyMcDonald', |
||||||
|
'first_name': 'Amy', |
||||||
|
'last_name': 'Mc Donald', |
||||||
|
'email': 'amy.mcdonald@example.com', |
||||||
|
'__start__': 'password:mapping', |
||||||
|
'password': 'Make Amy McDonald A Rich Girl Fund', |
||||||
|
'password-confirm': 'Make Amy McDonald A Rich Girl Fund', |
||||||
|
'__end__': 'password:mapping', |
||||||
|
'create': 'create account' |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
def test_registration_form(): |
||||||
|
from ordr.resources.account import RegistrationResource |
||||||
|
from ordr.schemas.account import RegistrationSchema |
||||||
from ordr.views.registration import registration_form |
from ordr.views.registration import registration_form |
||||||
result = registration_form(None, None) |
|
||||||
assert result == {} |
request = DummyRequest() |
||||||
|
context = RegistrationResource(request=request, name=None, parent=None) |
||||||
|
result = registration_form(context, None) |
||||||
|
form = result['form'] |
||||||
|
|
||||||
|
assert isinstance(form, deform.Form) |
||||||
|
assert isinstance(form.schema, RegistrationSchema) |
||||||
|
|
||||||
|
|
||||||
|
def test_registration_form_valid(dbsession): # noqa: F811 |
||||||
|
from ordr.models.account import User, Role, TokenSubject |
||||||
|
from ordr.resources.account import RegistrationResource |
||||||
|
from ordr.views.registration import registration_form_processing |
||||||
|
|
||||||
|
data = REGISTRATION_FORM_DATA.copy() |
||||||
|
request = get_post_request(dbsession, data) |
||||||
|
context = RegistrationResource(request=request, name=None, parent=None) |
||||||
|
result = registration_form_processing(context, request) |
||||||
|
|
||||||
|
# return value of function call |
||||||
|
assert isinstance(result, HTTPFound) |
||||||
|
assert result.location == 'http://example.com/verify' |
||||||
|
|
||||||
|
# user should be added to database |
||||||
|
user = dbsession.query(User).first() |
||||||
|
assert user.username == data['username'] |
||||||
|
assert user.first_name == data['first_name'] |
||||||
|
assert user.last_name == data['last_name'] |
||||||
|
assert user.email == data['email'] |
||||||
|
assert user.check_password(data['password']) |
||||||
|
assert user.role == Role.UNVALIDATED |
||||||
|
|
||||||
|
# a token should be created |
||||||
|
token = user.tokens[0] |
||||||
|
assert token.subject == TokenSubject.REGISTRATION |
||||||
|
|
||||||
|
# a verification email should be sent |
||||||
|
# this is tested in the functional test since request.registry.notify |
||||||
|
# doesn't know about event subscribers in the unittest |
||||||
|
|
||||||
|
|
||||||
|
def test_registration_form_invalid(dbsession): # noqa: F811 |
||||||
|
from ordr.views.registration import registration_form_processing |
||||||
|
from ordr.resources.account import RegistrationResource |
||||||
|
data = REGISTRATION_FORM_DATA.copy() |
||||||
|
data['email'] = 'not an email address' |
||||||
|
request = get_post_request(dbsession, data) |
||||||
|
context = RegistrationResource(request=request, name=None, parent=None) |
||||||
|
result = registration_form_processing(context, request) |
||||||
|
assert result['form'].error is not None |
||||||
|
|
||||||
|
|
||||||
|
def test_registration_form_no_create_button(dbsession): # noqa: F811 |
||||||
|
from ordr.views.registration import registration_form_processing |
||||||
|
from ordr.resources.account import RegistrationResource |
||||||
|
data = REGISTRATION_FORM_DATA.copy() |
||||||
|
data.pop('create') |
||||||
|
request = get_post_request(dbsession, data) |
||||||
|
context = RegistrationResource(request=request, name=None, parent=None) |
||||||
|
result = registration_form_processing(context, request) |
||||||
|
assert result.location == 'http://example.com//' |
||||||
|
Reference in new issue