summaryrefslogtreecommitdiff
path: root/docs/tutorials/wiki/src/tests
diff options
context:
space:
mode:
authorMichael Merickel <michael@merickel.org>2020-01-12 16:08:56 -0600
committerMichael Merickel <michael@merickel.org>2020-01-12 16:08:56 -0600
commitc77f619f79b32acb1e8866db43625f108daf2a18 (patch)
treedba9d47c3dc6b70f749161876f28056f0503ce1a /docs/tutorials/wiki/src/tests
parent3c484c3333672a7ed60436e14cd731458f7bd5e6 (diff)
downloadpyramid-c77f619f79b32acb1e8866db43625f108daf2a18.tar.gz
pyramid-c77f619f79b32acb1e8866db43625f108daf2a18.tar.bz2
pyramid-c77f619f79b32acb1e8866db43625f108daf2a18.zip
upgrade the test harness
Diffstat (limited to 'docs/tutorials/wiki/src/tests')
-rw-r--r--docs/tutorials/wiki/src/tests/.gitignore3
-rw-r--r--docs/tutorials/wiki/src/tests/development.ini2
-rw-r--r--docs/tutorials/wiki/src/tests/production.ini2
-rw-r--r--docs/tutorials/wiki/src/tests/setup.py4
-rw-r--r--docs/tutorials/wiki/src/tests/testing.ini62
-rw-r--r--docs/tutorials/wiki/src/tests/tests/conftest.py84
-rw-r--r--docs/tutorials/wiki/src/tests/tests/test_functional.py80
-rw-r--r--docs/tutorials/wiki/src/tests/tests/test_it.py232
-rw-r--r--docs/tutorials/wiki/src/tests/tests/test_models.py24
-rw-r--r--docs/tutorials/wiki/src/tests/tests/test_views.py83
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/__init__.py15
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/models/__init__.py8
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/security.py55
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/templates/edit.pt3
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/templates/layout.pt20
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/templates/view.pt3
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/views/auth.py51
-rw-r--r--docs/tutorials/wiki/src/tests/tutorial/views/default.py74
18 files changed, 481 insertions, 324 deletions
diff --git a/docs/tutorials/wiki/src/tests/.gitignore b/docs/tutorials/wiki/src/tests/.gitignore
index 1853d983c..e9336274d 100644
--- a/docs/tutorials/wiki/src/tests/.gitignore
+++ b/docs/tutorials/wiki/src/tests/.gitignore
@@ -11,7 +11,7 @@ dist/
nosetests.xml
env*/
tmp/
-Data.fs*
+Data*.fs*
*.sublime-project
*.sublime-workspace
.*.sw?
@@ -19,3 +19,4 @@ Data.fs*
.DS_Store
coverage
test
+*.sqlite
diff --git a/docs/tutorials/wiki/src/tests/development.ini b/docs/tutorials/wiki/src/tests/development.ini
index 228f18f36..e8aef6b43 100644
--- a/docs/tutorials/wiki/src/tests/development.ini
+++ b/docs/tutorials/wiki/src/tests/development.ini
@@ -18,6 +18,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
retry.attempts = 3
+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/wiki/src/tests/production.ini b/docs/tutorials/wiki/src/tests/production.ini
index 46b1e331b..35ef6aabe 100644
--- a/docs/tutorials/wiki/src/tests/production.ini
+++ b/docs/tutorials/wiki/src/tests/production.ini
@@ -16,6 +16,8 @@ zodbconn.uri = file://%(here)s/Data.fs?connection_cache_size=20000
retry.attempts = 3
+auth.secret = real-seekrit
+
[pshell]
setup = tutorial.pshell.setup
diff --git a/docs/tutorials/wiki/src/tests/setup.py b/docs/tutorials/wiki/src/tests/setup.py
index f19d643e6..cdfa18e09 100644
--- a/docs/tutorials/wiki/src/tests/setup.py
+++ b/docs/tutorials/wiki/src/tests/setup.py
@@ -9,6 +9,8 @@ with open(os.path.join(here, 'CHANGES.txt')) as f:
CHANGES = f.read()
requires = [
+ 'bcrypt',
+ 'docutils',
'plaster_pastedeploy',
'pyramid',
'pyramid_chameleon',
@@ -19,8 +21,6 @@ requires = [
'pyramid_zodbconn',
'transaction',
'ZODB3',
- 'docutils',
- 'bcrypt',
]
tests_require = [
diff --git a/docs/tutorials/wiki/src/tests/testing.ini b/docs/tutorials/wiki/src/tests/testing.ini
new file mode 100644
index 000000000..81193b35a
--- /dev/null
+++ b/docs/tutorials/wiki/src/tests/testing.ini
@@ -0,0 +1,62 @@
+###
+# app configuration
+# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/environment.html
+###
+
+[app:main]
+use = egg:tutorial
+
+pyramid.reload_templates = false
+pyramid.debug_authorization = false
+pyramid.debug_notfound = false
+pyramid.debug_routematch = false
+pyramid.default_locale_name = en
+
+zodbconn.uri = file://%(here)s/Data.testing.fs?connection_cache_size=20000
+
+retry.attempts = 3
+
+auth.secret = testing-seekrit
+
+[pshell]
+setup = tutorial.pshell.setup
+
+###
+# wsgi server configuration
+###
+
+[server:main]
+use = egg:waitress#main
+listen = localhost:6543
+
+###
+# logging configuration
+# https://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
+###
+
+[loggers]
+keys = root, tutorial
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = INFO
+handlers = console
+
+[logger_tutorial]
+level = DEBUG
+handlers =
+qualname = tutorial
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(asctime)s %(levelname)-5.5s [%(name)s:%(lineno)s][%(threadName)s] %(message)s
diff --git a/docs/tutorials/wiki/src/tests/tests/conftest.py b/docs/tutorials/wiki/src/tests/tests/conftest.py
new file mode 100644
index 000000000..6a702ae12
--- /dev/null
+++ b/docs/tutorials/wiki/src/tests/tests/conftest.py
@@ -0,0 +1,84 @@
+import os
+from pyramid.paster import get_appsettings
+from pyramid.scripting import prepare
+from pyramid.testing import DummyRequest
+import pytest
+import transaction
+import webtest
+
+from tutorial import main
+
+
+def pytest_addoption(parser):
+ parser.addoption('--ini', action='store', metavar='INI_FILE')
+
+@pytest.fixture(scope='session')
+def ini_file(request):
+ # potentially grab this path from a pytest option
+ return os.path.abspath(request.config.option.ini or 'testing.ini')
+
+@pytest.fixture(scope='session')
+def app_settings(ini_file):
+ return get_appsettings(ini_file)
+
+@pytest.fixture(scope='session')
+def app(app_settings):
+ return main({}, **app_settings)
+
+@pytest.fixture
+def tm():
+ tm = transaction.manager
+ tm.begin()
+ tm.doom()
+
+ yield tm
+
+ tm.abort()
+
+@pytest.fixture
+def testapp(app, tm):
+ testapp = webtest.TestApp(app, extra_environ={
+ 'HTTP_HOST': 'example.com',
+ 'tm.active': True,
+ 'tm.manager': tm,
+ })
+
+ return testapp
+
+@pytest.fixture
+def app_request(app, tm):
+ """
+ A real request.
+
+ This request is almost identical to a real request but it has some
+ drawbacks in tests as it's harder to mock data and is heavier.
+
+ """
+ env = prepare(registry=app.registry)
+ request = env['request']
+ request.host = 'example.com'
+ request.tm = tm
+
+ yield request
+ env['closer']()
+
+@pytest.fixture
+def dummy_request(app, tm):
+ """
+ A lightweight dummy request.
+
+ This request is ultra-lightweight and should be used only when the
+ request itself is not a large focus in the call-stack.
+
+ It is way easier to mock and control side-effects using this object.
+
+ - It does not have request extensions applied.
+ - Threadlocals are not properly pushed.
+
+ """
+ request = DummyRequest()
+ request.registry = app.registry
+ request.host = 'example.com'
+ request.tm = tm
+
+ return request
diff --git a/docs/tutorials/wiki/src/tests/tests/test_functional.py b/docs/tutorials/wiki/src/tests/tests/test_functional.py
new file mode 100644
index 000000000..25555713c
--- /dev/null
+++ b/docs/tutorials/wiki/src/tests/tests/test_functional.py
@@ -0,0 +1,80 @@
+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'
+)
+editor_login = (
+ '/login?login=editor&password=editor'
+ '&came_from=FrontPage&form.submitted=Login'
+)
+
+def test_root(testapp):
+ res = testapp.get('/', status=303)
+ assert res.location == 'http://example.com/FrontPage'
+
+def test_FrontPage(testapp):
+ res = testapp.get('/FrontPage', status=200)
+ assert b'FrontPage' in res.body
+
+def test_missing_page(testapp):
+ res = testapp.get('/SomePage', status=404)
+ assert b'Not Found' in res.body
+
+def test_referrer_is_login(testapp):
+ res = testapp.get('/login', status=200)
+ assert b'name="came_from" value="/"' in res.body
+
+def test_successful_log_in(testapp):
+ res = testapp.get(viewer_login, status=303)
+ assert res.location == 'http://example.com/FrontPage'
+
+def test_failed_log_in(testapp):
+ res = testapp.get(viewer_wrong_login, status=400)
+ assert b'login' in res.body
+
+def test_logout_link_present_when_logged_in(testapp):
+ res = testapp.get(viewer_login, status=303)
+ res = testapp.get('/FrontPage', status=200)
+ assert b'Logout' in res.body
+
+def test_logout_link_not_present_after_logged_out(testapp):
+ res = testapp.get(viewer_login, status=303)
+ res = testapp.get('/FrontPage', status=200)
+ res = testapp.get('/logout', status=303)
+ assert b'Logout' not in res.body
+
+def test_anonymous_user_cannot_edit(testapp):
+ res = testapp.get('/FrontPage/edit_page', status=200)
+ assert b'Login' in res.body
+
+def test_anonymous_user_cannot_add(testapp):
+ res = testapp.get('/add_page/NewPage', status=200)
+ assert b'Login' in res.body
+
+def test_viewer_user_cannot_edit(testapp):
+ res = testapp.get(viewer_login, status=303)
+ res = testapp.get('/FrontPage/edit_page', status=200)
+ assert b'Login' in res.body
+
+def test_viewer_user_cannot_add(testapp):
+ res = testapp.get(viewer_login, status=303)
+ res = testapp.get('/add_page/NewPage', status=200)
+ assert b'Login' in res.body
+
+def test_editors_member_user_can_edit(testapp):
+ res = testapp.get(editor_login, status=303)
+ res = testapp.get('/FrontPage/edit_page', status=200)
+ assert b'Editing' in res.body
+
+def test_editors_member_user_can_add(testapp):
+ res = testapp.get(editor_login, status=303)
+ res = testapp.get('/add_page/NewPage', status=200)
+ assert b'Editing' in res.body
+
+def test_editors_member_user_can_view(testapp):
+ res = testapp.get(editor_login, status=303)
+ res = testapp.get('/FrontPage', status=200)
+ assert b'FrontPage' in res.body
diff --git a/docs/tutorials/wiki/src/tests/tests/test_it.py b/docs/tutorials/wiki/src/tests/tests/test_it.py
deleted file mode 100644
index e45380e6f..000000000
--- a/docs/tutorials/wiki/src/tests/tests/test_it.py
+++ /dev/null
@@ -1,232 +0,0 @@
-import unittest
-
-from pyramid import testing
-
-class PageModelTests(unittest.TestCase):
-
- def _getTargetClass(self):
- from tutorial.models import Page
- return Page
-
- def _makeOne(self, data='some data'):
- return self._getTargetClass()(data=data)
-
- def test_constructor(self):
- instance = self._makeOne()
- self.assertEqual(instance.data, 'some data')
-
-class WikiModelTests(unittest.TestCase):
-
- def _getTargetClass(self):
- from tutorial.models import Wiki
- return Wiki
-
- def _makeOne(self):
- return self._getTargetClass()()
-
- def test_it(self):
- wiki = self._makeOne()
- self.assertEqual(wiki.__parent__, None)
- self.assertEqual(wiki.__name__, None)
-
-class AppmakerTests(unittest.TestCase):
-
- def _callFUT(self, zodb_root):
- from tutorial.models import appmaker
- return appmaker(zodb_root)
-
- def test_it(self):
- root = {}
- self._callFUT(root)
- self.assertEqual(root['app_root']['FrontPage'].data,
- 'This is the front page')
-
-class ViewWikiTests(unittest.TestCase):
- def test_it(self):
- from tutorial.views.default import view_wiki
- context = testing.DummyResource()
- request = testing.DummyRequest()
- response = view_wiki(context, request)
- self.assertEqual(response.location, 'http://example.com/FrontPage')
-
-class ViewPageTests(unittest.TestCase):
- def _callFUT(self, context, request):
- from tutorial.views.default import view_page
- return view_page(context, request)
-
- def test_it(self):
- wiki = testing.DummyResource()
- wiki['IDoExist'] = testing.DummyResource()
- context = testing.DummyResource(data='Hello CruelWorld IDoExist')
- context.__parent__ = wiki
- context.__name__ = 'thepage'
- request = testing.DummyRequest()
- info = self._callFUT(context, request)
- self.assertEqual(info['page'], context)
- self.assertEqual(
- info['page_text'],
- '<div class="document">\n'
- '<p>Hello <a href="http://example.com/add_page/CruelWorld">'
- 'CruelWorld</a> '
- '<a href="http://example.com/IDoExist/">'
- 'IDoExist</a>'
- '</p>\n</div>\n')
- self.assertEqual(info['edit_url'],
- 'http://example.com/thepage/edit_page')
-
-
-class AddPageTests(unittest.TestCase):
- def _callFUT(self, context, request):
- from tutorial.views.default import add_page
- return add_page(context, request)
-
- def test_it_notsubmitted(self):
- context = testing.DummyResource()
- request = testing.DummyRequest()
- request.subpath = ['AnotherPage']
- info = self._callFUT(context, request)
- self.assertEqual(info['page'].data,'')
- self.assertEqual(
- info['save_url'],
- request.resource_url(context, 'add_page', 'AnotherPage'))
-
- def test_it_submitted(self):
- context = testing.DummyResource()
- request = testing.DummyRequest({'form.submitted':True,
- 'body':'Hello yo!'})
- request.subpath = ['AnotherPage']
- self._callFUT(context, request)
- page = context['AnotherPage']
- self.assertEqual(page.data, 'Hello yo!')
- self.assertEqual(page.__name__, 'AnotherPage')
- self.assertEqual(page.__parent__, context)
-
-class EditPageTests(unittest.TestCase):
- def _callFUT(self, context, request):
- from tutorial.views.default import edit_page
- return edit_page(context, request)
-
- def test_it_notsubmitted(self):
- context = testing.DummyResource()
- request = testing.DummyRequest()
- info = self._callFUT(context, request)
- self.assertEqual(info['page'], context)
- self.assertEqual(info['save_url'],
- request.resource_url(context, 'edit_page'))
-
- def test_it_submitted(self):
- context = testing.DummyResource()
- request = testing.DummyRequest({'form.submitted':True,
- 'body':'Hello yo!'})
- response = self._callFUT(context, request)
- self.assertEqual(response.location, 'http://example.com/')
- self.assertEqual(context.data, 'Hello yo!')
-
-class SecurityTests(unittest.TestCase):
- def test_hashing(self):
- from tutorial.security import hash_password, check_password
- password = 'secretpassword'
- hashed_password = hash_password(password)
- self.assertTrue(check_password(hashed_password, password))
-
- self.assertFalse(check_password(hashed_password, 'attackerpassword'))
-
- self.assertFalse(check_password(None, password))
-
-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'
- editor_login = '/login?login=editor&password=editor' \
- '&came_from=FrontPage&form.submitted=Login'
-
- def setUp(self):
- import tempfile
- import os.path
- from tutorial import main
- self.tmpdir = tempfile.mkdtemp()
-
- dbpath = os.path.join( self.tmpdir, 'test.db')
- uri = 'file://' + dbpath
- settings = { 'zodbconn.uri' : uri ,
- 'pyramid.includes': ['pyramid_zodbconn', 'pyramid_tm'] }
-
- app = main({}, **settings)
- self.db = app.registry._zodb_databases['']
- from webtest import TestApp
- self.testapp = TestApp(app)
-
- def tearDown(self):
- import shutil
- self.db.close()
- shutil.rmtree( self.tmpdir )
-
- def test_root(self):
- res = self.testapp.get('/', status=302)
- self.assertEqual(res.location, 'http://localhost/FrontPage')
-
- def test_FrontPage(self):
- res = self.testapp.get('/FrontPage', status=200)
- self.assertTrue(b'FrontPage' in res.body)
-
- def test_unexisting_page(self):
- res = self.testapp.get('/SomePage', status=404)
- self.assertTrue(b'Not Found' in res.body)
-
- def test_referrer_is_login(self):
- res = self.testapp.get('/login', status=200)
- self.assertTrue(b'name="came_from" value="/"' in res.body)
-
- def test_successful_log_in(self):
- res = self.testapp.get( self.viewer_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)
- self.assertTrue(b'login' in res.body)
-
- def test_logout_link_present_when_logged_in(self):
- res = self.testapp.get( self.viewer_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):
- res = self.testapp.get( self.viewer_login, status=302)
- res = self.testapp.get('/FrontPage', status=200)
- res = self.testapp.get('/logout', status=302)
- self.assertTrue(b'Logout' not in res.body)
-
- def test_anonymous_user_cannot_edit(self):
- res = self.testapp.get('/FrontPage/edit_page', status=200)
- self.assertTrue(b'Login' in res.body)
-
- def test_anonymous_user_cannot_add(self):
- res = self.testapp.get('/add_page/NewPage', status=200)
- self.assertTrue(b'Login' in res.body)
-
- def test_viewer_user_cannot_edit(self):
- res = self.testapp.get( self.viewer_login, status=302)
- res = self.testapp.get('/FrontPage/edit_page', status=200)
- self.assertTrue(b'Login' in res.body)
-
- def test_viewer_user_cannot_add(self):
- res = self.testapp.get( self.viewer_login, status=302)
- res = self.testapp.get('/add_page/NewPage', status=200)
- self.assertTrue(b'Login' in res.body)
-
- def test_editors_member_user_can_edit(self):
- res = self.testapp.get( self.editor_login, status=302)
- res = self.testapp.get('/FrontPage/edit_page', status=200)
- self.assertTrue(b'Editing' in res.body)
-
- def test_editors_member_user_can_add(self):
- res = self.testapp.get( self.editor_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_view(self):
- res = self.testapp.get( self.editor_login, status=302)
- res = self.testapp.get('/FrontPage', status=200)
- self.assertTrue(b'FrontPage' in res.body)
diff --git a/docs/tutorials/wiki/src/tests/tests/test_models.py b/docs/tutorials/wiki/src/tests/tests/test_models.py
new file mode 100644
index 000000000..03ba0f669
--- /dev/null
+++ b/docs/tutorials/wiki/src/tests/tests/test_models.py
@@ -0,0 +1,24 @@
+from tutorial import models
+
+def test_page_model():
+ instance = models.Page(data='some data')
+ assert instance.data == 'some data'
+
+def test_wiki_model():
+ wiki = models.Wiki()
+ assert wiki.__parent__ is None
+ assert wiki.__name__ is None
+
+def test_appmaker():
+ root = {}
+ models.appmaker(root)
+ assert root['app_root']['FrontPage'].data == 'This is the front page'
+
+def test_password_hashing():
+ from tutorial.security import hash_password, check_password
+
+ password = 'secretpassword'
+ hashed_password = hash_password(password)
+ assert check_password(hashed_password, password)
+ assert not check_password(hashed_password, 'attackerpassword')
+ assert not check_password(None, password)
diff --git a/docs/tutorials/wiki/src/tests/tests/test_views.py b/docs/tutorials/wiki/src/tests/tests/test_views.py
new file mode 100644
index 000000000..bb8025b5c
--- /dev/null
+++ b/docs/tutorials/wiki/src/tests/tests/test_views.py
@@ -0,0 +1,83 @@
+from pyramid import testing
+
+
+class Test_view_wiki:
+ def test_it_redirects_to_front_page(self):
+ from tutorial.views.default import view_wiki
+ context = testing.DummyResource()
+ request = testing.DummyRequest()
+ response = view_wiki(context, request)
+ assert response.location == 'http://example.com/FrontPage'
+
+class Test_view_page:
+ def _callFUT(self, context, request):
+ from tutorial.views.default import view_page
+ return view_page(context, request)
+
+ def test_it(self):
+ wiki = testing.DummyResource()
+ wiki['IDoExist'] = testing.DummyResource()
+ context = testing.DummyResource(data='Hello CruelWorld IDoExist')
+ context.__parent__ = wiki
+ context.__name__ = 'thepage'
+ request = testing.DummyRequest()
+ info = self._callFUT(context, request)
+ assert info['page'] == context
+ assert info['page_text'] == (
+ '<div class="document">\n'
+ '<p>Hello <a href="http://example.com/add_page/CruelWorld">'
+ 'CruelWorld</a> '
+ '<a href="http://example.com/IDoExist/">'
+ 'IDoExist</a>'
+ '</p>\n</div>\n')
+ assert info['edit_url'] == 'http://example.com/thepage/edit_page'
+
+
+class Test_add_page:
+ def _callFUT(self, context, request):
+ from tutorial.views.default import add_page
+ return add_page(context, request)
+
+ def test_it_notsubmitted(self):
+ context = testing.DummyResource()
+ request = testing.DummyRequest()
+ request.subpath = ['AnotherPage']
+ info = self._callFUT(context, request)
+ assert info['page'].data == ''
+ assert info['save_url'] == request.resource_url(
+ context, 'add_page', 'AnotherPage')
+
+ def test_it_submitted(self):
+ context = testing.DummyResource()
+ request = testing.DummyRequest({
+ 'form.submitted': True,
+ 'body': 'Hello yo!',
+ })
+ request.subpath = ['AnotherPage']
+ self._callFUT(context, request)
+ page = context['AnotherPage']
+ assert page.data == 'Hello yo!'
+ assert page.__name__ == 'AnotherPage'
+ assert page.__parent__ == context
+
+class Test_edit_page:
+ def _callFUT(self, context, request):
+ from tutorial.views.default import edit_page
+ return edit_page(context, request)
+
+ def test_it_notsubmitted(self):
+ context = testing.DummyResource()
+ request = testing.DummyRequest()
+ info = self._callFUT(context, request)
+ assert info['page'] == context
+ assert info['save_url'] == request.resource_url(context, 'edit_page')
+
+ def test_it_submitted(self):
+ context = testing.DummyResource()
+ request = testing.DummyRequest({
+ 'form.submitted': True,
+ 'body': 'Hello yo!',
+ })
+ response = self._callFUT(context, request)
+ assert response.location == 'http://example.com/'
+ assert context.data == 'Hello yo!'
diff --git a/docs/tutorials/wiki/src/tests/tutorial/__init__.py b/docs/tutorials/wiki/src/tests/tutorial/__init__.py
index 935a5d6d2..2706cc184 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/__init__.py
+++ b/docs/tutorials/wiki/src/tests/tutorial/__init__.py
@@ -1,11 +1,8 @@
from pyramid.config import Configurator
from pyramid_zodbconn import get_connection
-from pyramid.authentication import AuthTktAuthenticationPolicy
-from pyramid.authorization import ACLAuthorizationPolicy
-
from .models import appmaker
-from .security import groupfinder
+
def root_factory(request):
conn = get_connection(request)
@@ -15,17 +12,13 @@ def root_factory(request):
def main(global_config, **settings):
""" This function returns a Pyramid WSGI application.
"""
- authn_policy = AuthTktAuthenticationPolicy(
- 'sosecret', callback=groupfinder, hashalg='sha512')
- authz_policy = ACLAuthorizationPolicy()
with Configurator(settings=settings) as config:
- config.set_authentication_policy(authn_policy)
- config.set_authorization_policy(authz_policy)
+ config.include('pyramid_chameleon')
config.include('pyramid_tm')
config.include('pyramid_retry')
config.include('pyramid_zodbconn')
- config.set_root_factory(root_factory)
- config.include('pyramid_chameleon')
config.include('.routes')
+ config.include('.security')
+ config.set_root_factory(root_factory)
config.scan()
return config.make_wsgi_app()
diff --git a/docs/tutorials/wiki/src/tests/tutorial/models/__init__.py b/docs/tutorials/wiki/src/tests/tutorial/models/__init__.py
index ebd70e912..64ae4bf5c 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/models/__init__.py
+++ b/docs/tutorials/wiki/src/tests/tutorial/models/__init__.py
@@ -4,13 +4,15 @@ from persistent.mapping import PersistentMapping
from pyramid.security import (
Allow,
Everyone,
- )
+)
class Wiki(PersistentMapping):
__name__ = None
__parent__ = None
- __acl__ = [ (Allow, Everyone, 'view'),
- (Allow, 'group:editors', 'edit') ]
+ __acl__ = [
+ (Allow, Everyone, 'view'),
+ (Allow, 'group:editors', 'edit'),
+ ]
class Page(Persistent):
def __init__(self, data):
diff --git a/docs/tutorials/wiki/src/tests/tutorial/security.py b/docs/tutorials/wiki/src/tests/tutorial/security.py
index cbb3acd5d..9f51aa54c 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/security.py
+++ b/docs/tutorials/wiki/src/tests/tutorial/security.py
@@ -1,4 +1,10 @@
import bcrypt
+from pyramid.authentication import AuthTktCookieHelper
+from pyramid.authorization import ACLHelper
+from pyramid.security import (
+ Authenticated,
+ Everyone,
+)
def hash_password(pw):
@@ -11,10 +17,47 @@ def check_password(expected_hash, pw):
return bcrypt.checkpw(pw.encode('utf-8'), expected_hash.encode('utf-8'))
return False
-USERS = {'editor': hash_password('editor'),
- 'viewer': hash_password('viewer')}
-GROUPS = {'editor':['group:editors']}
+USERS = {
+ 'editor': hash_password('editor'),
+ 'viewer': hash_password('viewer'),
+}
+GROUPS = {'editor': ['group:editors']}
-def groupfinder(userid, request):
- if userid in USERS:
- return GROUPS.get(userid, [])
+class MySecurityPolicy:
+ def __init__(self, secret):
+ self.authtkt = AuthTktCookieHelper(secret)
+ self.acl = ACLHelper()
+
+ def authenticated_identity(self, request):
+ identity = self.authtkt.identify(request)
+ if identity is not None and identity['userid'] in USERS:
+ return identity
+
+ def authenticated_userid(self, request):
+ identity = self.authenticated_identity(request)
+ if identity is not None:
+ return identity['userid']
+
+ def remember(self, request, userid, **kw):
+ return self.authtkt.remember(request, userid, **kw)
+
+ def forget(self, request, **kw):
+ return self.authtkt.forget(request, **kw)
+
+ def permits(self, request, context, permission):
+ principals = self.effective_principals(request)
+ return self.acl.permits(context, principals, permission)
+
+ def effective_principals(self, request):
+ principals = [Everyone]
+ identity = self.authenticated_identity(request)
+ if identity is not None:
+ principals.append(Authenticated)
+ principals.append('u:' + identity['userid'])
+ principals.extend(GROUPS.get(identity['userid'], []))
+ return principals
+
+def includeme(config):
+ settings = config.get_settings()
+
+ config.set_security_policy(MySecurityPolicy(settings['auth.secret']))
diff --git a/docs/tutorials/wiki/src/tests/tutorial/templates/edit.pt b/docs/tutorials/wiki/src/tests/tutorial/templates/edit.pt
index 6438b1569..488e7a6af 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/templates/edit.pt
+++ b/docs/tutorials/wiki/src/tests/tutorial/templates/edit.pt
@@ -2,9 +2,6 @@
<div metal:fill-slot="content">
<div class="content">
- <p tal:condition="logged_in" class="pull-right">
- <a href="${request.application_url}/logout">Logout</a>
- </p>
<p>
Editing <strong><span tal:replace="page.__name__">
Page Name Goes Here</span></strong>
diff --git a/docs/tutorials/wiki/src/tests/tutorial/templates/layout.pt b/docs/tutorials/wiki/src/tests/tutorial/templates/layout.pt
index 06a3c8157..61042da24 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/templates/layout.pt
+++ b/docs/tutorials/wiki/src/tests/tutorial/templates/layout.pt
@@ -8,8 +8,7 @@
<meta name="author" content="Pylons Project">
<link rel="shortcut icon" href="${request.static_url('tutorial:static/pyramid-16x16.png')}">
- <title><span tal:replace="page.__name__ | title"></span> - Pyramid tutorial wiki (based on
- TurboGears 20-Minute Wiki)</title>
+ <title><span tal:replace="page.__name__ | title"></span> - Pyramid tutorial wiki (based on TurboGears 20-Minute Wiki)</title>
<!-- Bootstrap core CSS -->
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
@@ -33,6 +32,14 @@
<img class="logo img-responsive" src="${request.static_url('tutorial:static/pyramid.png')}" alt="pyramid web framework">
</div>
<div class="col-md-10">
+ <div class="content">
+ <p tal:condition="request.authenticated_userid is None" class="pull-right">
+ <a href="${request.application_url}/login">Login</a>
+ </p>
+ <p tal:condition="request.authenticated_userid is not None" class="pull-right">
+ <a href="${request.application_url}/logout">Logout</a>
+ </p>
+ </div>
<div metal:define-slot="content">No content</div>
<div class="content">
<p>You can return to the
@@ -42,6 +49,15 @@
</div>
</div>
<div class="row">
+ <div class="links">
+ <ul>
+ <li><i class="glyphicon glyphicon-cog icon-muted"></i><a href="https://github.com/Pylons/pyramid">Github Project</a></li>
+ <li><i class="glyphicon glyphicon-globe icon-muted"></i><a href="https://webchat.freenode.net/?channels=pyramid">IRC Channel</a></li>
+ <li><i class="glyphicon glyphicon-home icon-muted"></i><a href="https://pylonsproject.org">Pylons Project</a></li>
+ </ul>
+ </div>
+ </div>
+ <div class="row">
<div class="copyright">
Copyright &copy; Pylons Project
</div>
diff --git a/docs/tutorials/wiki/src/tests/tutorial/templates/view.pt b/docs/tutorials/wiki/src/tests/tutorial/templates/view.pt
index 911ab0c99..b8a6fbbaf 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/templates/view.pt
+++ b/docs/tutorials/wiki/src/tests/tutorial/templates/view.pt
@@ -2,9 +2,6 @@
<div metal:fill-slot="content">
<div class="content">
- <p tal:condition="logged_in" class="pull-right">
- <a href="${request.application_url}/logout">Logout</a>
- </p>
<div tal:replace="structure page_text">
Page text goes here.
</div>
diff --git a/docs/tutorials/wiki/src/tests/tutorial/views/auth.py b/docs/tutorials/wiki/src/tests/tutorial/views/auth.py
new file mode 100644
index 000000000..5062779a6
--- /dev/null
+++ b/docs/tutorials/wiki/src/tests/tutorial/views/auth.py
@@ -0,0 +1,51 @@
+from pyramid.httpexceptions import HTTPSeeOther
+from pyramid.security import (
+ forget,
+ remember,
+)
+from pyramid.view import (
+ forbidden_view_config,
+ view_config,
+)
+
+from ..security import check_password, USERS
+
+
+@view_config(context='..models.Wiki', name='login',
+ renderer='tutorial:templates/login.pt')
+@forbidden_view_config(renderer='tutorial:templates/login.pt')
+def login(request):
+ login_url = request.resource_url(request.root, '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)
+ message = ''
+ login = ''
+ password = ''
+ if 'form.submitted' in request.params:
+ login = request.params['login']
+ password = request.params['password']
+ if check_password(USERS.get(login), password):
+ headers = remember(request, login)
+ return HTTPSeeOther(location=came_from, headers=headers)
+ message = 'Failed login'
+ request.response.status = 400
+
+ return dict(
+ message=message,
+ url=login_url,
+ came_from=came_from,
+ login=login,
+ password=password,
+ title='Login',
+ )
+
+
+@view_config(context='..models.Wiki', name='logout')
+def logout(request):
+ headers = forget(request)
+ return HTTPSeeOther(
+ location=request.resource_url(request.context),
+ headers=headers,
+ )
diff --git a/docs/tutorials/wiki/src/tests/tutorial/views/default.py b/docs/tutorials/wiki/src/tests/tutorial/views/default.py
index 7ba99c65b..5bb21fbcd 100644
--- a/docs/tutorials/wiki/src/tests/tutorial/views/default.py
+++ b/docs/tutorials/wiki/src/tests/tutorial/views/default.py
@@ -1,30 +1,21 @@
from docutils.core import publish_parts
+from pyramid.httpexceptions import HTTPSeeOther
+from pyramid.view import view_config
import re
-from pyramid.httpexceptions import HTTPFound
-from pyramid.security import (
- forget,
- remember,
-)
-from pyramid.view import (
- forbidden_view_config,
- view_config,
- )
-
from ..models import Page
-from ..security import check_password, USERS
+
# regular expression used to find WikiWords
wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
-
-@view_config(context='..models.Wiki',
- permission='view')
+@view_config(context='..models.Wiki', permission='view')
def view_wiki(context, request):
- return HTTPFound(location=request.resource_url(context, 'FrontPage'))
+ return HTTPSeeOther(location=request.resource_url(context, 'FrontPage'))
-@view_config(context='..models.Page', renderer='tutorial:templates/view.pt',
+@view_config(context='..models.Page',
+ renderer='tutorial:templates/view.pt',
permission='view')
def view_page(context, request):
wiki = context.__parent__
@@ -42,8 +33,7 @@ def view_page(context, request):
page_text = publish_parts(context.data, writer_name='html')['html_body']
page_text = wikiwords.sub(check, page_text)
edit_url = request.resource_url(context, 'edit_page')
- return dict(page=context, page_text=page_text, edit_url=edit_url,
- logged_in=request.authenticated_userid)
+ return dict(page=context, page_text=page_text, edit_url=edit_url)
@view_config(name='add_page', context='..models.Wiki',
@@ -57,13 +47,12 @@ def add_page(context, request):
page.__name__ = pagename
page.__parent__ = context
context[pagename] = page
- return HTTPFound(location=request.resource_url(page))
+ return HTTPSeeOther(location=request.resource_url(page))
save_url = request.resource_url(context, 'add_page', pagename)
page = Page('')
page.__name__ = pagename
page.__parent__ = context
- return dict(page=page, save_url=save_url,
- logged_in=request.authenticated_userid)
+ return dict(page=page, save_url=save_url)
@view_config(name='edit_page', context='..models.Page',
@@ -72,46 +61,9 @@ def add_page(context, request):
def edit_page(context, request):
if 'form.submitted' in request.params:
context.data = request.params['body']
- return HTTPFound(location=request.resource_url(context))
-
- return dict(page=context,
- save_url=request.resource_url(context, 'edit_page'),
- logged_in=request.authenticated_userid)
-
-
-@view_config(context='..models.Wiki', name='login',
- renderer='tutorial:templates/login.pt')
-@forbidden_view_config(renderer='tutorial:templates/login.pt')
-def login(request):
- login_url = request.resource_url(request.context, '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)
- message = ''
- login = ''
- password = ''
- if 'form.submitted' in request.params:
- login = request.params['login']
- password = request.params['password']
- if check_password(USERS.get(login), password):
- headers = remember(request, login)
- return HTTPFound(location=came_from,
- headers=headers)
- message = 'Failed login'
+ return HTTPSeeOther(location=request.resource_url(context))
return dict(
- message=message,
- url=request.application_url + '/login',
- came_from=came_from,
- login=login,
- password=password,
- title='Login',
+ page=context,
+ save_url=request.resource_url(context, 'edit_page'),
)
-
-
-@view_config(context='..models.Wiki', name='logout')
-def logout(request):
- headers = forget(request)
- return HTTPFound(location=request.resource_url(request.context),
- headers=headers)