From 66fabb4ac707b5b4289db0094756f1a1af7269cc Mon Sep 17 00:00:00 2001 From: Michael Merickel Date: Thu, 18 Feb 2016 02:32:08 -0600 Subject: update tests chapter --- docs/tutorials/wiki2/src/tests/development.ini | 2 + docs/tutorials/wiki2/src/tests/production.ini | 2 + docs/tutorials/wiki2/src/tests/setup.py | 2 +- .../tutorials/wiki2/src/tests/tutorial/__init__.py | 19 +--- .../wiki2/src/tests/tutorial/models/user.py | 6 +- docs/tutorials/wiki2/src/tests/tutorial/routes.py | 50 +++++++++++ .../tutorials/wiki2/src/tests/tutorial/security.py | 40 +++++++++ .../wiki2/src/tests/tutorial/security/__init__.py | 0 .../wiki2/src/tests/tutorial/security/default.py | 12 --- .../wiki2/src/tests/tutorial/templates/edit.jinja2 | 93 +++++-------------- .../src/tests/tutorial/templates/layout.jinja2 | 64 +++++++++++++ .../src/tests/tutorial/templates/login.jinja2 | 100 ++++++--------------- .../wiki2/src/tests/tutorial/templates/view.jinja2 | 89 ++++-------------- .../src/tests/tutorial/tests/test_functional.py | 56 +++++++----- .../wiki2/src/tests/tutorial/tests/test_views.py | 66 ++++++++------ .../wiki2/src/tests/tutorial/views/auth.py | 25 +++--- .../wiki2/src/tests/tutorial/views/default.py | 54 +++++------ docs/tutorials/wiki2/tests.rst | 43 +++------ 18 files changed, 354 insertions(+), 369 deletions(-) create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/routes.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/security.py delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py delete mode 100644 docs/tutorials/wiki2/src/tests/tutorial/security/default.py create mode 100644 docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 diff --git a/docs/tutorials/wiki2/src/tests/development.ini b/docs/tutorials/wiki2/src/tests/development.ini index 99c4ff0fe..f3079727e 100644 --- a/docs/tutorials/wiki2/src/tests/development.ini +++ b/docs/tutorials/wiki2/src/tests/development.ini @@ -17,6 +17,8 @@ pyramid.includes = sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite +auth.secret = seekrit + # By default, the toolbar only appears for clients from IP addresses # '127.0.0.1' and '::1'. # debugtoolbar.hosts = 127.0.0.1 ::1 diff --git a/docs/tutorials/wiki2/src/tests/production.ini b/docs/tutorials/wiki2/src/tests/production.ini index cb1db3211..686dba48a 100644 --- a/docs/tutorials/wiki2/src/tests/production.ini +++ b/docs/tutorials/wiki2/src/tests/production.ini @@ -14,6 +14,8 @@ pyramid.default_locale_name = en sqlalchemy.url = sqlite:///%(here)s/tutorial.sqlite +auth.secret = real-seekrit + [server:main] use = egg:waitress#main host = 0.0.0.0 diff --git a/docs/tutorials/wiki2/src/tests/setup.py b/docs/tutorials/wiki2/src/tests/setup.py index e06aa06e4..57538f2d0 100644 --- a/docs/tutorials/wiki2/src/tests/setup.py +++ b/docs/tutorials/wiki2/src/tests/setup.py @@ -43,8 +43,8 @@ setup(name='tutorial', include_package_data=True, zip_safe=False, test_suite='tutorial', - install_requires=requires, tests_require=tests_require, + install_requires=requires, entry_points="""\ [paste.app_factory] main = tutorial:main diff --git a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py index a62c42378..f5c033b8b 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/__init__.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/__init__.py @@ -1,28 +1,13 @@ from pyramid.config import Configurator -from pyramid.authentication import AuthTktAuthenticationPolicy -from pyramid.authorization import ACLAuthorizationPolicy - -from .security.default import groupfinder def main(global_config, **settings): """ This function returns a Pyramid WSGI application. """ - authn_policy = AuthTktAuthenticationPolicy( - 'sosecret', callback=groupfinder, hashalg='sha512') - authz_policy = ACLAuthorizationPolicy() config = Configurator(settings=settings) config.include('pyramid_jinja2') config.include('.models') - config.set_root_factory('.models.mymodel.RootFactory') - config.set_authentication_policy(authn_policy) - config.set_authorization_policy(authz_policy) - config.add_static_view('static', 'static', cache_max_age=3600) - config.add_route('view_wiki', '/') - config.add_route('login', '/login') - config.add_route('logout', '/logout') - config.add_route('view_page', '/{pagename}') - config.add_route('add_page', '/add_page/{pagename}') - config.add_route('edit_page', '/{pagename}/edit_page') + config.include('.routes') + config.include('.security') config.scan() return config.make_wsgi_app() diff --git a/docs/tutorials/wiki2/src/tests/tutorial/models/user.py b/docs/tutorials/wiki2/src/tests/tutorial/models/user.py index 25b0a8187..6fb32a1b2 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/models/user.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/models/user.py @@ -18,10 +18,12 @@ class User(Base): password_hash = Column(Text) def set_password(self, pw): - pwhash = bcrypt.hashpw(pw, bcrypt.gensalt()) + pwhash = bcrypt.hashpw(pw.encode('utf8'), bcrypt.gensalt()) self.password_hash = pwhash def check_password(self, pw): if self.password_hash is not None: - return bcrypt.hashpw(pw, self.password_hash) == self.password_hash + expected_hash = self.password_hash.encode('utf8') + actual_hash = bcrypt.hashpw(pw.encode('utf8'), expected_hash) + return expected_hash == actual_hash return False diff --git a/docs/tutorials/wiki2/src/tests/tutorial/routes.py b/docs/tutorials/wiki2/src/tests/tutorial/routes.py new file mode 100644 index 000000000..c7c3a2120 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/routes.py @@ -0,0 +1,50 @@ +from pyramid.httpexceptions import HTTPNotFound +from pyramid.security import ( + Allow, + Everyone, +) + +from .models import Page + +def includeme(config): + config.add_static_view('static', 'static', cache_max_age=3600) + config.add_route('view_wiki', '/') + config.add_route('login', '/login') + config.add_route('logout', '/logout') + config.add_route('view_page', '/{pagename}', factory=page_factory) + config.add_route('add_page', '/add_page/{pagename}', + factory=new_page_factory) + config.add_route('edit_page', '/{pagename}/edit_page', + factory=page_factory) + +def new_page_factory(request): + pagename = request.matchdict['pagename'] + return NewPage(pagename) + +class NewPage(object): + def __init__(self, pagename): + self.pagename = pagename + + def __acl__(self): + return [ + (Allow, 'role:editor', 'create'), + (Allow, 'role:basic', 'create'), + ] + +def page_factory(request): + pagename = request.matchdict['pagename'] + page = request.dbsession.query(Page).filter_by(name=pagename).first() + if page is None: + raise HTTPNotFound + return PageResource(page) + +class PageResource(object): + def __init__(self, page): + self.page = page + + def __acl__(self): + return [ + (Allow, Everyone, 'view'), + (Allow, 'role:editor', 'edit'), + (Allow, str(self.page.creator_id), 'edit'), + ] diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security.py b/docs/tutorials/wiki2/src/tests/tutorial/security.py new file mode 100644 index 000000000..25cff7b05 --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/security.py @@ -0,0 +1,40 @@ +from pyramid.authentication import AuthTktAuthenticationPolicy +from pyramid.authorization import ACLAuthorizationPolicy +from pyramid.security import ( + Authenticated, + Everyone, +) + +from .models import User + + +class MyAuthenticationPolicy(AuthTktAuthenticationPolicy): + def authenticated_userid(self, request): + user = request.user + if user is not None: + return user.id + + def effective_principals(self, request): + principals = [Everyone] + user = request.user + if user is not None: + principals.append(Authenticated) + principals.append(str(user.id)) + principals.append('role:' + user.role) + return principals + +def get_user(request): + user_id = request.unauthenticated_userid + if user_id is not None: + user = request.dbsession.query(User).get(user_id) + return user + +def includeme(config): + settings = config.get_settings() + authn_policy = MyAuthenticationPolicy( + settings['auth.secret'], + hashalg='sha512', + ) + config.set_authentication_policy(authn_policy) + config.set_authorization_policy(ACLAuthorizationPolicy()) + config.add_request_method(get_user, 'user', reify=True) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py b/docs/tutorials/wiki2/src/tests/tutorial/security/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/tutorials/wiki2/src/tests/tutorial/security/default.py b/docs/tutorials/wiki2/src/tests/tutorial/security/default.py deleted file mode 100644 index 7fc1ea7c8..000000000 --- a/docs/tutorials/wiki2/src/tests/tutorial/security/default.py +++ /dev/null @@ -1,12 +0,0 @@ -USERS = { - 'editor': 'editor', - 'viewer': 'viewer', -} - -GROUPS = { - 'editor': ['group:editors'], -} - -def groupfinder(userid, request): - if userid in USERS: - return GROUPS.get(userid, []) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 index 4d767cfbe..7db25c674 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/edit.jinja2 @@ -1,73 +1,20 @@ - - - - - - - - - - - Edit{% if page.name %} {{page.name}}{% endif %} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) - - - - - - - - - - - - - -
-
-
-
- -
-
-
- {% if request.authenticated_userid is not none %} -

- Logout -

- {% endif %} -

- Editing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} -

-

You can return to the - FrontPage. -

-
-
- -
-
- -
-
-
-
-
-
- -
-
-
- - - - - - - - +{% extends 'layout.jinja2' %} + +{% block subtitle %}Edit {{pagename}} - {% endblock subtitle %} + +{% block content %} +

+Editing {{pagename}} +

+

You can return to the +FrontPage. +

+
+
+ +
+
+ +
+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 new file mode 100644 index 000000000..44d14304e --- /dev/null +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/layout.jinja2 @@ -0,0 +1,64 @@ + + + + + + + + + + + {% block subtitle %}{% endblock %}Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) + + + + + + + + + + + + + +
+
+
+
+ +
+
+
+ {% if request.user is none %} +

+ Login +

+ {% else %} +

+ {{request.user.name}} Logout +

+ {% endif %} + {% block content %}{% endblock %} +
+
+
+
+ +
+
+
+ + + + + + + + diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/login.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/login.jinja2 index a80a2a165..1806de0ff 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/login.jinja2 +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/login.jinja2 @@ -1,74 +1,26 @@ - - - - - - - - - - - Login - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) - - - - - - - - - - - - - -
-
-
-
- -
-
-
-

- - Login -
- {{ message }} -

-
- -
- - -
-
- - -
-
- -
-
-
-
-
-
- -
-
-
- - - - - - - - +{% extends 'layout.jinja2' %} + +{% block title %}Login - {% endblock title %} + +{% block content %} +

+ + Login +
+{{ message }} +

+
+ +
+ + +
+
+ + +
+
+ +
+
+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 index 942b8479b..94419e228 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 +++ b/docs/tutorials/wiki2/src/tests/tutorial/templates/view.jinja2 @@ -1,71 +1,18 @@ - - - - - - - - - - - {{page.name}} - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki) - - - - - - - - - - - - - -
-
-
-
- -
-
-
- {% if request.authenticated_userid is not none %} -

- Logout -

- {% endif %} -

{{ content|safe }}

-

- - Edit this page - -

-

- Viewing {% if page.name %}{{page.name}}{% else %}Page Name Goes Here{% endif %} -

-

You can return to the - FrontPage. -

-
-
-
-
- -
-
-
- - - - - - - - +{% extends 'layout.jinja2' %} + +{% block subtitle %}{{page.name}} - {% endblock subtitle %} + +{% block content %} +

{{ content|safe }}

+

+ + Edit this page + +

+

+ Viewing {{page.name}}, created by {{page.creator.name}}. +

+

You can return to the +FrontPage. +

+{% endblock content %} diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py index c716537ae..b2c6e0975 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_functional.py @@ -5,26 +5,30 @@ from webtest import TestApp class FunctionalTests(unittest.TestCase): - viewer_login = ( - '/login?login=viewer&password=viewer' - '&came_from=FrontPage&form.submitted=Login') - viewer_wrong_login = ( - '/login?login=viewer&password=incorrect' - '&came_from=FrontPage&form.submitted=Login') + basic_login = ( + '/login?login=basic&password=basic' + '&next=FrontPage&form.submitted=Login') + basic_wrong_login = ( + '/login?login=basic&password=incorrect' + '&next=FrontPage&form.submitted=Login') editor_login = ( '/login?login=editor&password=editor' - '&came_from=FrontPage&form.submitted=Login') + '&next=FrontPage&form.submitted=Login') @classmethod def setUpClass(cls): from tutorial.models.meta import Base from tutorial.models import ( + User, Page, get_tm_session, ) from tutorial import main - settings = {'sqlalchemy.url': 'sqlite://'} + settings = { + 'sqlalchemy.url': 'sqlite://', + 'auth.secret': 'seekrit', + } app = main({}, **settings) cls.testapp = TestApp(app) @@ -34,8 +38,15 @@ class FunctionalTests(unittest.TestCase): with transaction.manager: dbsession = get_tm_session(session_factory, transaction.manager) - model = Page(name='FrontPage', data='This is the front page') - dbsession.add(model) + editor = User(name='editor', role='editor') + editor.set_password('editor') + basic = User(name='basic', role='basic') + basic.set_password('basic') + page1 = Page(name='FrontPage', data='This is the front page') + page1.creator = editor + page2 = Page(name='BackPage', data='This is the back page') + page2.creator = basic + dbsession.add_all([basic, editor, page1, page2]) @classmethod def tearDownClass(cls): @@ -54,20 +65,20 @@ class FunctionalTests(unittest.TestCase): self.testapp.get('/SomePage', status=404) def test_successful_log_in(self): - res = self.testapp.get(self.viewer_login, status=302) + res = self.testapp.get(self.basic_login, status=302) self.assertEqual(res.location, 'http://localhost/FrontPage') def test_failed_log_in(self): - res = self.testapp.get(self.viewer_wrong_login, status=200) + res = self.testapp.get(self.basic_wrong_login, status=200) self.assertTrue(b'login' in res.body) def test_logout_link_present_when_logged_in(self): - self.testapp.get(self.viewer_login, status=302) + self.testapp.get(self.basic_login, status=302) res = self.testapp.get('/FrontPage', status=200) self.assertTrue(b'Logout' in res.body) def test_logout_link_not_present_after_logged_out(self): - self.testapp.get(self.viewer_login, status=302) + self.testapp.get(self.basic_login, status=302) self.testapp.get('/FrontPage', status=200) res = self.testapp.get('/logout', status=302) self.assertTrue(b'Logout' not in res.body) @@ -80,15 +91,20 @@ class FunctionalTests(unittest.TestCase): res = self.testapp.get('/add_page/NewPage', status=302).follow() self.assertTrue(b'Login' in res.body) - def test_viewer_user_cannot_edit(self): - self.testapp.get(self.viewer_login, status=302) + def test_basic_user_cannot_edit_front(self): + self.testapp.get(self.basic_login, status=302) res = self.testapp.get('/FrontPage/edit_page', status=302).follow() self.assertTrue(b'Login' in res.body) - def test_viewer_user_cannot_add(self): - self.testapp.get(self.viewer_login, status=302) - res = self.testapp.get('/add_page/NewPage', status=302).follow() - self.assertTrue(b'Login' in res.body) + def test_basic_user_can_edit_back(self): + self.testapp.get(self.basic_login, status=302) + res = self.testapp.get('/BackPage/edit_page', status=200) + self.assertTrue(b'Editing' in res.body) + + def test_basic_user_can_add(self): + self.testapp.get(self.basic_login, status=302) + res = self.testapp.get('/add_page/NewPage', status=200) + self.assertTrue(b'Editing' in res.body) def test_editors_member_user_can_edit(self): self.testapp.get(self.editor_login, status=302) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py index b2830d070..5253183df 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/tests/test_views.py @@ -8,12 +8,6 @@ def dummy_request(dbsession): return testing.DummyRequest(dbsession=dbsession) -def _register_routes(config): - config.add_route('view_page', '{pagename}') - config.add_route('add_page', 'add_page/{pagename}') - config.add_route('edit_page', '{pagename}/edit_page') - - class BaseTest(unittest.TestCase): def setUp(self): from ..models import get_tm_session @@ -21,7 +15,7 @@ class BaseTest(unittest.TestCase): 'sqlalchemy.url': 'sqlite:///:memory:' }) self.config.include('..models') - self.config.include(_register_routes) + self.config.include('..routes') session_factory = self.config.registry['dbsession_factory'] self.session = get_tm_session(session_factory, transaction.manager) @@ -38,11 +32,21 @@ class BaseTest(unittest.TestCase): testing.tearDown() transaction.abort() + def makeUser(self, name, role, password='dummy'): + from ..models import User + user = User(name=name, role=role) + user.set_password(password) + return user + + def makePage(self, name, data, creator): + from ..models import Page + return Page(name=name, data=data, creator=creator) + class ViewWikiTests(unittest.TestCase): def setUp(self): self.config = testing.setUp() - _register_routes(self.config) + self.config.include('..routes') def tearDown(self): testing.tearDown() @@ -63,14 +67,16 @@ class ViewPageTests(BaseTest): return view_page(request) def test_it(self): + from ..routes import PageResource + # add a page to the db - from ..models.mymodel import Page - page = Page(name='IDoExist', data='Hello CruelWorld IDoExist') - self.session.add(page) + user = self.makeUser('foo', 'editor') + page = self.makePage('IDoExist', 'Hello CruelWorld IDoExist', user) + self.session.add_all([page, user]) # create a request asking for the page we've created request = dummy_request(self.session) - request.matchdict['pagename'] = 'IDoExist' + request.context = PageResource(page) # call the view we're testing and check its behavior info = self._callFUT(request) @@ -93,19 +99,23 @@ class AddPageTests(BaseTest): return add_page(request) def test_it_notsubmitted(self): + from ..routes import NewPage request = dummy_request(self.session) - request.matchdict = {'pagename': 'AnotherPage'} + request.user = self.makeUser('foo', 'editor') + request.context = NewPage('AnotherPage') info = self._callFUT(request) - self.assertEqual(info['page'].data, '') + self.assertEqual(info['pagedata'], '') self.assertEqual(info['save_url'], 'http://example.com/add_page/AnotherPage') def test_it_submitted(self): - from ..models.mymodel import Page + from ..models import Page + from ..routes import NewPage request = testing.DummyRequest({'form.submitted': True, 'body': 'Hello yo!'}, dbsession=self.session) - request.matchdict = {'pagename': 'AnotherPage'} + request.user = self.makeUser('foo', 'editor') + request.context = NewPage('AnotherPage') self._callFUT(request) page = self.session.query(Page).filter_by(name='AnotherPage').one() self.assertEqual(page.data, 'Hello yo!') @@ -116,25 +126,31 @@ class EditPageTests(BaseTest): from tutorial.views.default import edit_page return edit_page(request) + def makeContext(self, page): + from ..routes import PageResource + return PageResource(page) + def test_it_notsubmitted(self): - from ..models.mymodel import Page + user = self.makeUser('foo', 'editor') + page = self.makePage('abc', 'hello', user) + self.session.add_all([page, user]) + request = dummy_request(self.session) - request.matchdict = {'pagename': 'abc'} - page = Page(name='abc', data='hello') - self.session.add(page) + request.context = self.makeContext(page) info = self._callFUT(request) - self.assertEqual(info['page'], page) + self.assertEqual(info['pagename'], 'abc') self.assertEqual(info['save_url'], 'http://example.com/abc/edit_page') def test_it_submitted(self): - from ..models.mymodel import Page + user = self.makeUser('foo', 'editor') + page = self.makePage('abc', 'hello', user) + self.session.add_all([page, user]) + request = testing.DummyRequest({'form.submitted': True, 'body': 'Hello yo!'}, dbsession=self.session) - request.matchdict = {'pagename': 'abc'} - page = Page(name='abc', data='hello') - self.session.add(page) + request.context = self.makeContext(page) response = self._callFUT(request) self.assertEqual(response.location, 'http://example.com/abc') self.assertEqual(page.data, 'Hello yo!') diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/auth.py b/docs/tutorials/wiki2/src/tests/tutorial/views/auth.py index 08aa2bfad..2b993b430 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views/auth.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/views/auth.py @@ -8,33 +8,30 @@ from pyramid.view import ( view_config, ) -from ..security.default import USERS +from ..models import User -@view_config(route_name='login', renderer='templates/login.jinja2') +@view_config(route_name='login', renderer='../templates/login.jinja2') def login(request): - login_url = request.route_url('login') - referrer = request.url - if referrer == login_url: - referrer = '/' # never use the login form itself as came_from - came_from = request.params.get('came_from', referrer) + next_url = request.params.get('next', request.referrer) + if not next_url: + next_url = request.route_url('view_wiki') message = '' login = '' - password = '' if 'form.submitted' in request.params: login = request.params['login'] password = request.params['password'] - if USERS.get(login) == password: - headers = remember(request, login) - return HTTPFound(location=came_from, headers=headers) + user = request.dbsession.query(User).filter_by(name=login).first() + if user is not None and user.check_password(password): + headers = remember(request, user.id) + return HTTPFound(location=next_url, headers=headers) message = 'Failed login' return dict( message=message, url=request.route_url('login'), - came_from=came_from, + next_url=next_url, login=login, - password=password, ) @view_config(route_name='logout') @@ -45,5 +42,5 @@ def logout(request): @forbidden_view_config() def forbidden_view(request): - next_url = request.route_url('login', _query={'came_from': request.url}) + next_url = request.route_url('login', _query={'next': request.url}) return HTTPFound(location=next_url) diff --git a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py index 6fb3c8744..9358993ea 100644 --- a/docs/tutorials/wiki2/src/tests/tutorial/views/default.py +++ b/docs/tutorials/wiki2/src/tests/tutorial/views/default.py @@ -2,10 +2,7 @@ import cgi import re from docutils.core import publish_parts -from pyramid.httpexceptions import ( - HTTPFound, - HTTPNotFound, - ) +from pyramid.httpexceptions import HTTPFound from pyramid.view import view_config from ..models import Page @@ -13,7 +10,7 @@ from ..models import Page # regular expression used to find WikiWords wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)") -@view_config(route_name='view_wiki', permission='view') +@view_config(route_name='view_wiki') def view_wiki(request): next_url = request.route_url('view_page', pagename='FrontPage') return HTTPFound(location=next_url) @@ -21,12 +18,9 @@ def view_wiki(request): @view_config(route_name='view_page', renderer='../templates/view.jinja2', permission='view') def view_page(request): - pagename = request.matchdict['pagename'] - page = request.dbsession.query(Page).filter_by(name=pagename).first() - if page is None: - return HTTPNotFound('No such page') + page = request.context.page - def check(match): + def add_link(match): word = match.group(1) exists = request.dbsession.query(Page).filter_by(name=word).all() if exists: @@ -37,34 +31,34 @@ def view_page(request): return '%s' % (add_url, cgi.escape(word)) content = publish_parts(page.data, writer_name='html')['html_body'] - content = wikiwords.sub(check, content) - edit_url = request.route_url('edit_page', pagename=pagename) + content = wikiwords.sub(add_link, content) + edit_url = request.route_url('edit_page', pagename=page.name) return dict(page=page, content=content, edit_url=edit_url) -@view_config(route_name='add_page', renderer='../templates/edit.jinja2', +@view_config(route_name='edit_page', renderer='../templates/edit.jinja2', permission='edit') +def edit_page(request): + page = request.context.page + if 'form.submitted' in request.params: + page.data = request.params['body'] + next_url = request.route_url('view_page', pagename=page.name) + return HTTPFound(location=next_url) + return dict( + pagename=page.name, + pagedata=page.data, + save_url=request.route_url('edit_page', pagename=page.name), + ) + +@view_config(route_name='add_page', renderer='../templates/edit.jinja2', + permission='create') def add_page(request): - pagename = request.matchdict['pagename'] + pagename = request.context.pagename if 'form.submitted' in request.params: body = request.params['body'] page = Page(name=pagename, data=body) + page.creator = request.user request.dbsession.add(page) next_url = request.route_url('view_page', pagename=pagename) return HTTPFound(location=next_url) save_url = request.route_url('add_page', pagename=pagename) - page = Page(name='', data='') - return dict(page=page, save_url=save_url) - -@view_config(route_name='edit_page', renderer='../templates/edit.jinja2', - permission='edit') -def edit_page(request): - pagename = request.matchdict['pagename'] - page = request.dbsession.query(Page).filter_by(name=pagename).one() - if 'form.submitted' in request.params: - page.data = request.params['body'] - next_url = request.route_url('view_page', pagename=pagename) - return HTTPFound(location=next_url) - return dict( - page=page, - save_url=request.route_url('edit_page', pagename=pagename), - ) + return dict(pagename=pagename, pagedata='', save_url=save_url) diff --git a/docs/tutorials/wiki2/tests.rst b/docs/tutorials/wiki2/tests.rst index a99cd68cc..667550467 100644 --- a/docs/tutorials/wiki2/tests.rst +++ b/docs/tutorials/wiki2/tests.rst @@ -43,7 +43,7 @@ Functional tests We'll test the whole application, covering security aspects that are not tested in the unit tests, like logging in, logging out, checking that -the ``viewer`` user cannot add or edit pages, but the ``editor`` user +the ``basic`` user cannot edit pages it didn't create, but the ``editor`` user can, and so on. @@ -65,39 +65,20 @@ follows: :language: python -Running the tests -================= - -We can run these tests by using ``setup.py test`` in the same way we did in -:ref:`running_tests`. However, first we must edit our ``setup.py`` to include -a dependency on `WebTest -`_, which we've used -in our ``tests.py``. Change the ``requires`` list in ``setup.py`` to include -``WebTest``. - -.. literalinclude:: src/tests/setup.py - :linenos: - :language: python - :lines: 11-22 - :emphasize-lines: 11 +.. note:: -After we've added a dependency on WebTest in ``setup.py``, we need to run -``setup.py develop`` to get WebTest installed into our virtualenv. Assuming -our shell's current working directory is the "tutorial" distribution directory: + We're utilizing the excellent WebTest_ package to do functional testing + of the application. This is defined in the ``tests_require`` section of + our ``setup.py``. Any other dependencies needed only for testing purposes + can be added there and will be installed automatically when running + ``setup.py test``. -On UNIX: - -.. code-block:: bash - $ $VENV/bin/python setup.py develop - -On Windows: - -.. code-block:: text - - c:\pyramidtut\tutorial> %VENV%\Scripts\python setup.py develop +Running the tests +================= -Once that command has completed successfully, we can run the tests themselves: +We can run these tests by using ``setup.py test`` in the same way we did in +:ref:`running_tests`: On UNIX: @@ -122,3 +103,5 @@ The expected result should look like the following: OK Process finished with exit code 0 + +.. _webtest: http://docs.pylonsproject.org/projects/webtest/en/latest/ -- cgit v1.2.3