From 747623cc04d58d17595cab318ef4a190de60088d Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sat, 4 Jun 2011 07:41:31 -0500 Subject: [PATCH 01/29] I shouldn't have removed the .save() entirely :) --- mediagoblin/edit/views.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index 5cfb2297..c3aedac9 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -48,6 +48,7 @@ def edit_media(request, media): media['title'] = request.POST['title'] media['description'] = request.POST['description'] media['slug'] = request.POST['slug'] + media.save() # redirect return exc.HTTPFound( From 8ee83b43331bbe4c9d45554178dfa6c98f4f10ab Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sat, 4 Jun 2011 08:48:17 -0500 Subject: [PATCH 02/29] Let's redirect back to the media homepage using the slug instead of the id --- mediagoblin/edit/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index c3aedac9..0e0faa53 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -53,7 +53,7 @@ def edit_media(request, media): # redirect return exc.HTTPFound( location=request.urlgen("mediagoblin.user_pages.media_home", - user=media.uploader()['username'], media=media['_id'])) + user=media.uploader()['username'], media=media['slug'])) # render template = request.template_env.get_template( From a1556d3f496efe3fe37cdf474c611af97ab82426 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sat, 4 Jun 2011 08:48:51 -0500 Subject: [PATCH 03/29] Mount media editing under /u/{username}/m/{media}/edit/ --- mediagoblin/edit/routing.py | 4 ++-- mediagoblin/user_pages/routing.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mediagoblin/edit/routing.py b/mediagoblin/edit/routing.py index 54f2661a..bf0b2498 100644 --- a/mediagoblin/edit/routing.py +++ b/mediagoblin/edit/routing.py @@ -14,9 +14,9 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . + from routes.route import Route edit_routes = [ - Route('mediagoblin.edit.edit_media', "/{user}/{media}/", - controller="mediagoblin.edit.views:edit_media"), + # Media editing view handled in user_pages/routing.py ] diff --git a/mediagoblin/user_pages/routing.py b/mediagoblin/user_pages/routing.py index 96f97427..c5e9a984 100644 --- a/mediagoblin/user_pages/routing.py +++ b/mediagoblin/user_pages/routing.py @@ -22,5 +22,7 @@ user_routes = [ Route('mediagoblin.user_pages.media_home', '/{user}/m/{media}/', requirements=dict(m_id="[0-9a-fA-F]{24}"), controller="mediagoblin.user_pages.views:media_home"), + Route('mediagoblin.edit.edit_media', "/{user}/m/{media}/edit/", + controller="mediagoblin.edit.views:edit_media"), Route('mediagoblin.user_pages.atom_feed', '/{user}/atom/', controller="mediagoblin.user_pages.views:atom_feed")] From c5678c1ab3deb3f7a2961225c35260d5bbd69604 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sat, 4 Jun 2011 13:20:48 -0500 Subject: [PATCH 04/29] Proper webtest infrastructure... seems to be about right anyway :) --- mediagoblin/app.py | 13 ++-- mediagoblin/celery_setup/from_tests.py | 43 ++++++++++++ mediagoblin/tests/mgoblin_test_app.ini | 46 +++++++++++++ mediagoblin/tests/tools.py | 94 ++++++++++++++++++++++++++ mediagoblin/util.py | 37 ++++++++-- setup.py | 1 + 6 files changed, 222 insertions(+), 12 deletions(-) create mode 100644 mediagoblin/celery_setup/from_tests.py create mode 100644 mediagoblin/tests/mgoblin_test_app.ini create mode 100644 mediagoblin/tests/tools.py diff --git a/mediagoblin/app.py b/mediagoblin/app.py index 714404de..e5949531 100644 --- a/mediagoblin/app.py +++ b/mediagoblin/app.py @@ -139,12 +139,13 @@ def paste_app_factory(global_config, **app_config): raise ImproperlyConfigured( "One of direct_remote_path or direct_remote_paths must be provided") - if asbool(os.environ.get('CELERY_ALWAYS_EAGER')): - setup_celery_from_config( - app_config, global_config, - force_celery_always_eager=True) - else: - setup_celery_from_config(app_config, global_config) + if not asbool(app_config.get('celery_setup_elsewhere')): + if asbool(os.environ.get('CELERY_ALWAYS_EAGER')): + setup_celery_from_config( + app_config, global_config, + force_celery_always_eager=True) + else: + setup_celery_from_config(app_config, global_config) mgoblin_app = MediaGoblinApp( connection, db, diff --git a/mediagoblin/celery_setup/from_tests.py b/mediagoblin/celery_setup/from_tests.py new file mode 100644 index 00000000..fe7d7314 --- /dev/null +++ b/mediagoblin/celery_setup/from_tests.py @@ -0,0 +1,43 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011 Free Software Foundation, Inc +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import os + +from mediagoblin.tests.tools import TEST_APP_CONFIG +from mediagoblin import util +from mediagoblin.celery_setup import setup_celery_from_config +from mediagoblin.globals import setup_globals + + +OUR_MODULENAME = 'mediagoblin.celery_setup.from_tests' + + +def setup_self(setup_globals_func=setup_globals): + """ + Set up celery for testing's sake, which just needs to set up + celery and celery only. + """ + mgoblin_conf = util.read_config_file(TEST_APP_CONFIG) + mgoblin_section = mgoblin_conf['app:mediagoblin'] + + setup_celery_from_config( + mgoblin_section, mgoblin_conf, + settings_module=OUR_MODULENAME, + set_environ=False) + + +if os.environ.get('CELERY_CONFIG_MODULE') == OUR_MODULENAME: + setup_self() diff --git a/mediagoblin/tests/mgoblin_test_app.ini b/mediagoblin/tests/mgoblin_test_app.ini new file mode 100644 index 00000000..abed2615 --- /dev/null +++ b/mediagoblin/tests/mgoblin_test_app.ini @@ -0,0 +1,46 @@ +[DEFAULT] +debug = true + +[composite:main] +use = egg:Paste#urlmap +/ = mediagoblin +/mgoblin_media/ = publicstore_serve +/mgoblin_static/ = mediagoblin_static + +[app:mediagoblin] +use = egg:mediagoblin#app +filter-with = beaker +queuestore_base_dir = %(here)s/test_user_dev/media/queue +publicstore_base_dir = %(here)s/test_user_dev/media/public +publicstore_base_url = /mgoblin_media/ +direct_remote_path = /mgoblin_static/ +email_sender_address = "notice@mediagoblin.example.org" +email_debug_mode = true +db_name = __mediagoblin_tests__ +# Celery shouldn't be set up by the paste app factory as it's set up +# elsewhere +celery_setup_elsewhere = true + +[app:publicstore_serve] +use = egg:Paste#static +document_root = %(here)s/user_dev/media/public + +[app:mediagoblin_static] +use = egg:Paste#static +document_root = %(here)s/mediagoblin/static/ + +[filter:beaker] +use = egg:Beaker#beaker_session +cache_dir = %(here)s/test_user_dev/beaker +beaker.session.key = mediagoblin +# beaker.session.secret = somesupersecret +beaker.session.data_dir = %(here)s/test_user_dev/beaker/sessions/data +beaker.session.lock_dir = %(here)s/test_user_dev/beaker/sessions/lock + +[celery] +celery_always_eager = true + +[server:main] +use = egg:Paste#http +host = 127.0.0.1 +port = 6543 diff --git a/mediagoblin/tests/tools.py b/mediagoblin/tests/tools.py new file mode 100644 index 00000000..70b74b89 --- /dev/null +++ b/mediagoblin/tests/tools.py @@ -0,0 +1,94 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011 Free Software Foundation, Inc +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + + +import pkg_resources +import os, shutil + +from paste.deploy import appconfig +from webtest import TestApp + +from mediagoblin import app +from mediagoblin.db.open import setup_connection_and_db_from_config + + +MEDIAGOBLIN_TEST_DB_NAME = '__mediagoblinunittests__' +TEST_APP_CONFIG = pkg_resources.resource_filename( + 'mediagoblin.tests', 'mgoblin_test_app.ini') +TEST_USER_DEV = pkg_resources.resource_filename( + 'mediagoblin.tests', 'test_user_dev') +MGOBLIN_APP = None + +USER_DEV_DIRECTORIES_TO_SETUP = [ + 'media/public', 'media/queue', + 'beaker/sessions/data', 'beaker/sessions/lock'] + + +class BadCeleryEnviron(Exception): pass + + +def get_test_app(dump_old_app=True): + if not os.environ.get('CELERY_CONFIG_MODULE') == \ + 'mediagoblin.celery_setup.from_tests': + raise BadCeleryEnviron( + u"Sorry, you *absolutely* must run nosetests with the\n" + u"mediagoblin.celery_setup.from_tests module. Like so:\n" + u"$ CELERY_CONFIG_MODULE=mediagoblin.celery_setup.from_tests ./bin/nosetests") + + # Just return the old app if that exists and it's okay to set up + # and return + if MGOBLIN_APP and not dump_old_app: + return MGOBLIN_APP + + # Remove and reinstall user_dev directories + if os.path.exists(TEST_USER_DEV): + shutil.rmtree(TEST_USER_DEV) + + for directory in USER_DEV_DIRECTORIES_TO_SETUP: + full_dir = os.path.join(TEST_USER_DEV, directory) + os.makedirs(full_dir) + + # Get app config + config = appconfig( + 'config:' + os.path.basename(TEST_APP_CONFIG), + relative_to=os.path.dirname(TEST_APP_CONFIG), + name='mediagoblin') + + # Wipe database + # @@: For now we're dropping collections, but we could also just + # collection.remove() ? + connection, db = setup_connection_and_db_from_config( + config.local_conf) + + collections_to_wipe = [ + collection + for collection in db.collection_names() + if not collection.startswith('system.')] + + for collection in collections_to_wipe: + db.drop_collection(collection) + + # Don't need these anymore... + del(connection) + del(db) + + # TODO: Drop and recreate indexes + + # setup app and return + test_app = app.paste_app_factory( + config.global_conf, **config.local_conf) + + return TestApp(test_app) diff --git a/mediagoblin/util.py b/mediagoblin/util.py index 2865cf11..fdb2c3f5 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -18,18 +18,21 @@ from email.MIMEText import MIMEText import gettext import pkg_resources import smtplib +import os import sys import re -import jinja2 -from mediagoblin.db.util import ObjectId -import translitcodec - -from mediagoblin import globals as mgoblin_globals - import urllib from math import ceil import copy +import jinja2 +import translitcodec +from paste.deploy.loadwsgi import NicerConfigParser + +from mediagoblin import globals as mgoblin_globals +from mediagoblin.db.util import ObjectId + + TESTS_ENABLED = False def _activate_testing(): """ @@ -278,6 +281,28 @@ def get_locale_from_request(request): return locale_to_lower_upper(target_lang) +def read_config_file(conf_file): + """ + Read a paste deploy style config file and process it. + """ + if not os.path.exists(conf_file): + raise IOError( + "MEDIAGOBLIN_CONFIG not set or file does not exist") + + parser = NicerConfigParser(conf_file) + parser.read(conf_file) + parser._defaults.setdefault( + 'here', os.path.dirname(os.path.abspath(conf_file))) + parser._defaults.setdefault( + '__file__', os.path.abspath(conf_file)) + + mgoblin_conf = dict( + [(section_name, dict(parser.items(section_name))) + for section_name in parser.sections()]) + + return mgoblin_conf + + def setup_gettext(locale): """ Setup the gettext instance based on this locale diff --git a/setup.py b/setup.py index 097dd7f2..46da7276 100644 --- a/setup.py +++ b/setup.py @@ -41,6 +41,7 @@ setup( 'Babel', 'translitcodec', 'argparse', + 'webtest', ], test_suite='nose.collector', From e9279f21376feb5c43675c31b6f25e9fabac2ac6 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sat, 4 Jun 2011 16:32:13 -0500 Subject: [PATCH 05/29] Added new render_template method which will make our lives nicer and also will be useful for unit testing purposes :) --- mediagoblin/util.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/mediagoblin/util.py b/mediagoblin/util.py index fdb2c3f5..41f8a92a 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -78,6 +78,33 @@ def get_jinja_env(template_loader, locale): return template_env +# We'll store context information here when doing unit tests +TEMPLATE_TEST_CONTEXT = {} + + +def render_template(request, template, context): + """ + Render a template with context. + + Always inserts the request into the context, so you don't have to. + Also stores the context if we're doing unit tests. Helpful! + """ + template = request.template_env.get_template( + template) + context['request'] = request + rendered = template.render(context) + + if TESTS_ENABLED: + TEMPLATE_TEST_CONTEXT[template] = context + + return rendered + + +def clear_test_template_context(): + global TEMPLATE_TEST_CONTEXT + TEMPLATE_TEST_CONTEXT = {} + + def setup_user_in_request(request): """ Examine a request and tack on a request.user parameter if that's From f99f61c65c7937910d7170f7f4a43c18e083a010 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sat, 4 Jun 2011 16:44:22 -0500 Subject: [PATCH 06/29] Cache template environments and gettexts so we don't have to reproduce them on every request. --- mediagoblin/util.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/mediagoblin/util.py b/mediagoblin/util.py index 41f8a92a..3649b6c3 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -25,6 +25,7 @@ import urllib from math import ceil import copy +from babel.localedata import exists import jinja2 import translitcodec from paste.deploy.loadwsgi import NicerConfigParser @@ -58,6 +59,9 @@ def get_jinja_loader(user_template_path=None): return jinja2.PackageLoader('mediagoblin', 'templates') +SETUP_JINJA_ENVS = {} + + def get_jinja_env(template_loader, locale): """ Set up the Jinja environment, @@ -67,6 +71,11 @@ def get_jinja_env(template_loader, locale): """ setup_gettext(locale) + # If we have a jinja environment set up with this locale, just + # return that one. + if SETUP_JINJA_ENVS.has_key(locale): + return SETUP_JINJA_ENVS[locale] + template_env = jinja2.Environment( loader=template_loader, autoescape=True, extensions=['jinja2.ext.i18n']) @@ -75,6 +84,9 @@ def get_jinja_env(template_loader, locale): mgoblin_globals.translations.gettext, mgoblin_globals.translations.ngettext) + if exists(locale): + SETUP_JINJA_ENVS[locale] = template_env + return template_env @@ -330,6 +342,8 @@ def read_config_file(conf_file): return mgoblin_conf +SETUP_GETTEXTS = {} + def setup_gettext(locale): """ Setup the gettext instance based on this locale @@ -340,8 +354,13 @@ def setup_gettext(locale): # TODO: fallback nicely on translations from pt_PT to pt if not # available, etc. - this_gettext = gettext.translation( - 'mediagoblin', TRANSLATIONS_PATH, [locale], fallback=True) + if SETUP_GETTEXTS.has_key(locale): + this_gettext = SETUP_GETTEXTS[locale] + else: + this_gettext = gettext.translation( + 'mediagoblin', TRANSLATIONS_PATH, [locale], fallback=True) + if exists(locale): + SETUP_GETTEXTS[locale] = this_gettext mgoblin_globals.setup_globals( translations=this_gettext) From b5d3aec615fd32439c9fc708d2266dc1cdfecc9d Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sat, 4 Jun 2011 17:36:36 -0500 Subject: [PATCH 07/29] Moving all views over to using util.render_template()! --- mediagoblin/auth/views.py | 44 ++++++++++++--------------------- mediagoblin/edit/views.py | 9 +++---- mediagoblin/submit/views.py | 15 +++++------ mediagoblin/user_pages/views.py | 20 ++++++--------- mediagoblin/views.py | 10 ++++---- 5 files changed, 39 insertions(+), 59 deletions(-) diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py index e4f1a7b1..2d24328d 100644 --- a/mediagoblin/auth/views.py +++ b/mediagoblin/auth/views.py @@ -18,6 +18,7 @@ import uuid from webob import Response, exc +from mediagoblin.util import render_template from mediagoblin.db.util import ObjectId from mediagoblin.auth import lib as auth_lib from mediagoblin.auth import forms as auth_forms @@ -58,20 +59,16 @@ def register(request): location=request.urlgen("mediagoblin.auth.register_success")) # render - template = request.template_env.get_template( - 'mediagoblin/auth/register.html') return Response( - template.render( - {'request': request, - 'register_form': register_form})) + render_template( + request, 'mediagoblin/auth/register.html', + {'register_form': register_form})) def register_success(request): - template = request.template_env.get_template( - 'mediagoblin/auth/register_success.html') return Response( - template.render( - {'request': request})) + render_template( + request, 'mediagoblin/auth/register_success.html', {})) def login(request): @@ -106,12 +103,10 @@ def login(request): login_failed = True # render - template = request.template_env.get_template( - 'mediagoblin/auth/login.html') return Response( - template.render( - {'request': request, - 'login_form': login_form, + render_template( + request, 'mediagoblin/auth/login.html', + {'login_form': login_form, 'next': request.GET.get('next') or request.POST.get('next'), 'login_failed': login_failed})) @@ -146,12 +141,10 @@ def verify_email(request): else: verification_successful = False - template = request.template_env.get_template( - 'mediagoblin/auth/verify_email.html') return Response( - template.render( - {'request': request, - 'user': user, + render_template( + request, 'mediagoblin/auth/verify_email.html', + {'user': user, 'verification_successful': verification_successful})) def verify_email_notice(request): @@ -161,12 +154,9 @@ def verify_email_notice(request): When the user tries to do some action that requires their account to be verified beforehand, this view is called upon! """ - - template = request.template_env.get_template( - 'mediagoblin/auth/verification_needed.html') return Response( - template.render( - {'request': request})) + render_template( + request, 'mediagoblin/auth/verification_needed.html', {})) def resend_activation(request): @@ -186,8 +176,6 @@ def resend_activation(request): def resend_activation_success(request): - template = request.template_env.get_template( - 'mediagoblin/auth/resent_verification_email.html') return Response( - template.render( - {'request': request})) + render_template( + request, 'mediagoblin/auth/resent_verification_email.html', {})) diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index 0e0faa53..027a426c 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -17,6 +17,7 @@ from webob import Response, exc +from mediagoblin.util import render_template from mediagoblin.edit import forms from mediagoblin.edit.lib import may_edit_media from mediagoblin.decorators import require_active_login, get_user_media_entry @@ -56,10 +57,8 @@ def edit_media(request, media): user=media.uploader()['username'], media=media['slug'])) # render - template = request.template_env.get_template( - 'mediagoblin/edit/edit.html') return Response( - template.render( - {'request': request, - 'media': media, + render_template( + request, 'mediagoblin/edit/edit.html', + {'media': media, 'form': form})) diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py index 262f2b12..256f5be9 100644 --- a/mediagoblin/submit/views.py +++ b/mediagoblin/submit/views.py @@ -20,6 +20,7 @@ from cgi import FieldStorage from webob import Response, exc from werkzeug.utils import secure_filename +from mediagoblin.util import render_template from mediagoblin.decorators import require_active_login from mediagoblin.submit import forms as submit_forms from mediagoblin.process_media import process_media_initial @@ -80,18 +81,14 @@ def submit_start(request): location=request.urlgen("mediagoblin.submit.success")) # render - template = request.template_env.get_template( - 'mediagoblin/submit/start.html') return Response( - template.render( - {'request': request, - 'submit_form': submit_form})) + render_template( + request, 'mediagoblin/submit/start.html', + {'submit_form': submit_form})) def submit_success(request): # render - template = request.template_env.get_template( - 'mediagoblin/submit/success.html') return Response( - template.render( - {'request': request})) + render_template( + request, 'mediagoblin/submit/success.html', {})) diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index cc9c7b21..6ea3fe36 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -16,7 +16,7 @@ from webob import Response, exc from mediagoblin.db.util import DESCENDING -from mediagoblin.util import Pagination +from mediagoblin.util import Pagination, render_template from mediagoblin.decorators import uses_pagination, get_user_media_entry @@ -42,13 +42,10 @@ def user_home(request, page): if media_entries == None: return exc.HTTPNotFound() - template = request.template_env.get_template( - 'mediagoblin/user_pages/user.html') - return Response( - template.render( - {'request': request, - 'user': user, + render_template( + request, 'mediagoblin/user_pages/user.html', + {'user': user, 'media_entries': media_entries, 'pagination': pagination})) @@ -56,12 +53,11 @@ def user_home(request, page): @get_user_media_entry def media_home(request, media): """'Homepage' of a MediaEntry()""" - template = request.template_env.get_template( - 'mediagoblin/user_pages/media.html') return Response( - template.render( - {'request': request, - 'media': media})) + render_template( + request, 'mediagoblin/user_pages/media.html', + {'media': media})) + ATOM_DEFAULT_NR_OF_UPDATED_ITEMS = 5 diff --git a/mediagoblin/views.py b/mediagoblin/views.py index dd722c63..ee0b520a 100644 --- a/mediagoblin/views.py +++ b/mediagoblin/views.py @@ -15,15 +15,15 @@ # along with this program. If not, see . from webob import Response + +from mediagoblin.util import render_template from mediagoblin.db.util import DESCENDING def root_view(request): media_entries = request.db.MediaEntry.find( {u'state': u'processed'}).sort('created', DESCENDING) - template = request.template_env.get_template( - 'mediagoblin/root.html') return Response( - template.render( - {'request': request, - 'media_entries': media_entries})) + render_template( + request, 'mediagoblin/root.html', + {'media_entries': media_entries})) From 99619a625b20d4c5825eeb52753657b088db5381 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 09:14:38 -0500 Subject: [PATCH 08/29] Firefox 3.X shows hidden fields for some reason, adding display: none; to be rid of them for sure --- mediagoblin/templates/mediagoblin/auth/login.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mediagoblin/templates/mediagoblin/auth/login.html b/mediagoblin/templates/mediagoblin/auth/login.html index 22a57b70..c2e27c15 100644 --- a/mediagoblin/templates/mediagoblin/auth/login.html +++ b/mediagoblin/templates/mediagoblin/auth/login.html @@ -33,7 +33,8 @@ {% if next %} - + {% endif %}

Don't have an account yet? Create one here!

From 1c63ad5d352f5eb38a4d634b9aea84cbeee269a4 Mon Sep 17 00:00:00 2001 From: Elrond Date: Sun, 5 Jun 2011 15:25:45 +0200 Subject: [PATCH 09/29] Create render_to_reponse and use it everywhere. Just a shortcut for Response(render_template(...)) --- mediagoblin/auth/views.py | 34 ++++++++++++++------------------- mediagoblin/edit/views.py | 9 ++++----- mediagoblin/submit/views.py | 14 ++++++-------- mediagoblin/user_pages/views.py | 14 ++++++-------- mediagoblin/util.py | 6 ++++++ mediagoblin/views.py | 9 +++------ 6 files changed, 39 insertions(+), 47 deletions(-) diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py index 2d24328d..a5112299 100644 --- a/mediagoblin/auth/views.py +++ b/mediagoblin/auth/views.py @@ -16,9 +16,9 @@ import uuid -from webob import Response, exc +from webob import exc -from mediagoblin.util import render_template +from mediagoblin.util import render_to_response from mediagoblin.db.util import ObjectId from mediagoblin.auth import lib as auth_lib from mediagoblin.auth import forms as auth_forms @@ -59,16 +59,14 @@ def register(request): location=request.urlgen("mediagoblin.auth.register_success")) # render - return Response( - render_template( + return render_to_response( request, 'mediagoblin/auth/register.html', - {'register_form': register_form})) + {'register_form': register_form}) def register_success(request): - return Response( - render_template( - request, 'mediagoblin/auth/register_success.html', {})) + return render_to_response( + request, 'mediagoblin/auth/register_success.html', {}) def login(request): @@ -103,12 +101,11 @@ def login(request): login_failed = True # render - return Response( - render_template( + return render_to_response( request, 'mediagoblin/auth/login.html', {'login_form': login_form, 'next': request.GET.get('next') or request.POST.get('next'), - 'login_failed': login_failed})) + 'login_failed': login_failed}) def logout(request): @@ -141,11 +138,10 @@ def verify_email(request): else: verification_successful = False - return Response( - render_template( + return render_to_response( request, 'mediagoblin/auth/verify_email.html', {'user': user, - 'verification_successful': verification_successful})) + 'verification_successful': verification_successful}) def verify_email_notice(request): """ @@ -154,9 +150,8 @@ def verify_email_notice(request): When the user tries to do some action that requires their account to be verified beforehand, this view is called upon! """ - return Response( - render_template( - request, 'mediagoblin/auth/verification_needed.html', {})) + return render_to_response( + request, 'mediagoblin/auth/verification_needed.html', {}) def resend_activation(request): @@ -176,6 +171,5 @@ def resend_activation(request): def resend_activation_success(request): - return Response( - render_template( - request, 'mediagoblin/auth/resent_verification_email.html', {})) + return render_to_response( + request, 'mediagoblin/auth/resent_verification_email.html', {}) diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index 027a426c..ac286bec 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -15,9 +15,9 @@ # along with this program. If not, see . -from webob import Response, exc +from webob import exc -from mediagoblin.util import render_template +from mediagoblin.util import render_to_response from mediagoblin.edit import forms from mediagoblin.edit.lib import may_edit_media from mediagoblin.decorators import require_active_login, get_user_media_entry @@ -57,8 +57,7 @@ def edit_media(request, media): user=media.uploader()['username'], media=media['slug'])) # render - return Response( - render_template( + return render_to_response( request, 'mediagoblin/edit/edit.html', {'media': media, - 'form': form})) + 'form': form}) diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py index 256f5be9..a51e14e6 100644 --- a/mediagoblin/submit/views.py +++ b/mediagoblin/submit/views.py @@ -17,10 +17,10 @@ from os.path import splitext from cgi import FieldStorage -from webob import Response, exc +from webob import exc from werkzeug.utils import secure_filename -from mediagoblin.util import render_template +from mediagoblin.util import render_to_response from mediagoblin.decorators import require_active_login from mediagoblin.submit import forms as submit_forms from mediagoblin.process_media import process_media_initial @@ -81,14 +81,12 @@ def submit_start(request): location=request.urlgen("mediagoblin.submit.success")) # render - return Response( - render_template( + return render_to_response( request, 'mediagoblin/submit/start.html', - {'submit_form': submit_form})) + {'submit_form': submit_form}) def submit_success(request): # render - return Response( - render_template( - request, 'mediagoblin/submit/success.html', {})) + return render_to_response( + request, 'mediagoblin/submit/success.html', {}) diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index 6ea3fe36..0442e736 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -14,9 +14,9 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from webob import Response, exc +from webob import exc from mediagoblin.db.util import DESCENDING -from mediagoblin.util import Pagination, render_template +from mediagoblin.util import Pagination, render_to_response from mediagoblin.decorators import uses_pagination, get_user_media_entry @@ -42,21 +42,19 @@ def user_home(request, page): if media_entries == None: return exc.HTTPNotFound() - return Response( - render_template( + return render_to_response( request, 'mediagoblin/user_pages/user.html', {'user': user, 'media_entries': media_entries, - 'pagination': pagination})) + 'pagination': pagination}) @get_user_media_entry def media_home(request, media): """'Homepage' of a MediaEntry()""" - return Response( - render_template( + return render_to_response( request, 'mediagoblin/user_pages/media.html', - {'media': media})) + {'media': media}) ATOM_DEFAULT_NR_OF_UPDATED_ITEMS = 5 diff --git a/mediagoblin/util.py b/mediagoblin/util.py index 3649b6c3..f69c91f2 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -29,6 +29,7 @@ from babel.localedata import exists import jinja2 import translitcodec from paste.deploy.loadwsgi import NicerConfigParser +from webob import Response from mediagoblin import globals as mgoblin_globals from mediagoblin.db.util import ObjectId @@ -117,6 +118,11 @@ def clear_test_template_context(): TEMPLATE_TEST_CONTEXT = {} +def render_to_response(request, template, context): + """Much like Django's shortcut.render()""" + return Response(render_template(request, template, context)) + + def setup_user_in_request(request): """ Examine a request and tack on a request.user parameter if that's diff --git a/mediagoblin/views.py b/mediagoblin/views.py index ee0b520a..dc2d45ba 100644 --- a/mediagoblin/views.py +++ b/mediagoblin/views.py @@ -14,16 +14,13 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from webob import Response - -from mediagoblin.util import render_template +from mediagoblin.util import render_to_response from mediagoblin.db.util import DESCENDING def root_view(request): media_entries = request.db.MediaEntry.find( {u'state': u'processed'}).sort('created', DESCENDING) - return Response( - render_template( + return render_to_response( request, 'mediagoblin/root.html', - {'media_entries': media_entries})) + {'media_entries': media_entries}) From c9c24934357300c436ac63531f91b7608f80fd21 Mon Sep 17 00:00:00 2001 From: Elrond Date: Sun, 5 Jun 2011 16:02:12 +0200 Subject: [PATCH 10/29] Reformat render_to_response calls Just a simple indentation and ordering change, no functional change. --- mediagoblin/auth/views.py | 38 ++++++++++++++++----------------- mediagoblin/edit/views.py | 9 ++++---- mediagoblin/submit/views.py | 12 +++++------ mediagoblin/user_pages/views.py | 16 +++++++------- mediagoblin/views.py | 6 +++--- 5 files changed, 38 insertions(+), 43 deletions(-) diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py index a5112299..d54e673c 100644 --- a/mediagoblin/auth/views.py +++ b/mediagoblin/auth/views.py @@ -58,15 +58,14 @@ def register(request): return exc.HTTPFound( location=request.urlgen("mediagoblin.auth.register_success")) - # render - return render_to_response( - request, 'mediagoblin/auth/register.html', - {'register_form': register_form}) + return render_to_response(request, + 'mediagoblin/auth/register.html', + {'register_form': register_form}) def register_success(request): - return render_to_response( - request, 'mediagoblin/auth/register_success.html', {}) + return render_to_response(request, + 'mediagoblin/auth/register_success.html', {}) def login(request): @@ -100,12 +99,11 @@ def login(request): auth_lib.fake_login_attempt() login_failed = True - # render - return render_to_response( - request, 'mediagoblin/auth/login.html', - {'login_form': login_form, - 'next': request.GET.get('next') or request.POST.get('next'), - 'login_failed': login_failed}) + return render_to_response(request, + 'mediagoblin/auth/login.html', + {'login_form': login_form, + 'next': request.GET.get('next') or request.POST.get('next'), + 'login_failed': login_failed}) def logout(request): @@ -138,10 +136,10 @@ def verify_email(request): else: verification_successful = False - return render_to_response( - request, 'mediagoblin/auth/verify_email.html', - {'user': user, - 'verification_successful': verification_successful}) + return render_to_response(request, + 'mediagoblin/auth/verify_email.html', + {'user': user, + 'verification_successful': verification_successful}) def verify_email_notice(request): """ @@ -150,8 +148,8 @@ def verify_email_notice(request): When the user tries to do some action that requires their account to be verified beforehand, this view is called upon! """ - return render_to_response( - request, 'mediagoblin/auth/verification_needed.html', {}) + return render_to_response(request, + 'mediagoblin/auth/verification_needed.html', {}) def resend_activation(request): @@ -171,5 +169,5 @@ def resend_activation(request): def resend_activation_success(request): - return render_to_response( - request, 'mediagoblin/auth/resent_verification_email.html', {}) + return render_to_response(request, + 'mediagoblin/auth/resent_verification_email.html', {}) diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index ac286bec..258f14e3 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -56,8 +56,7 @@ def edit_media(request, media): location=request.urlgen("mediagoblin.user_pages.media_home", user=media.uploader()['username'], media=media['slug'])) - # render - return render_to_response( - request, 'mediagoblin/edit/edit.html', - {'media': media, - 'form': form}) + return render_to_response(request, + 'mediagoblin/edit/edit.html', + {'media': media, + 'form': form}) diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py index a51e14e6..95257b72 100644 --- a/mediagoblin/submit/views.py +++ b/mediagoblin/submit/views.py @@ -80,13 +80,11 @@ def submit_start(request): return exc.HTTPFound( location=request.urlgen("mediagoblin.submit.success")) - # render - return render_to_response( - request, 'mediagoblin/submit/start.html', - {'submit_form': submit_form}) + return render_to_response(request, + 'mediagoblin/submit/start.html', + {'submit_form': submit_form}) def submit_success(request): - # render - return render_to_response( - request, 'mediagoblin/submit/success.html', {}) + return render_to_response(request, + 'mediagoblin/submit/success.html', {}) diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index 0442e736..e2fbcc80 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -42,19 +42,19 @@ def user_home(request, page): if media_entries == None: return exc.HTTPNotFound() - return render_to_response( - request, 'mediagoblin/user_pages/user.html', - {'user': user, - 'media_entries': media_entries, - 'pagination': pagination}) + return render_to_response(request, + 'mediagoblin/user_pages/user.html', + {'user': user, + 'media_entries': media_entries, + 'pagination': pagination}) @get_user_media_entry def media_home(request, media): """'Homepage' of a MediaEntry()""" - return render_to_response( - request, 'mediagoblin/user_pages/media.html', - {'media': media}) + return render_to_response(request, + 'mediagoblin/user_pages/media.html', + {'media': media}) ATOM_DEFAULT_NR_OF_UPDATED_ITEMS = 5 diff --git a/mediagoblin/views.py b/mediagoblin/views.py index dc2d45ba..22673d59 100644 --- a/mediagoblin/views.py +++ b/mediagoblin/views.py @@ -21,6 +21,6 @@ def root_view(request): media_entries = request.db.MediaEntry.find( {u'state': u'processed'}).sort('created', DESCENDING) - return render_to_response( - request, 'mediagoblin/root.html', - {'media_entries': media_entries}) + return render_to_response(request, + 'mediagoblin/root.html', + {'media_entries': media_entries}) From 9150244afa45628dd752a67272129d30d6c72224 Mon Sep 17 00:00:00 2001 From: Elrond Date: Sun, 5 Jun 2011 15:49:08 +0200 Subject: [PATCH 11/29] Create redirect shortcut and use it around This is just replacing exc.HTTPFound(location=request.urlgen(...)) by redirect(request, ...). No magic. --- mediagoblin/auth/views.py | 15 +++++---------- mediagoblin/decorators.py | 6 +++--- mediagoblin/edit/views.py | 8 +++----- mediagoblin/submit/views.py | 7 ++----- mediagoblin/util.py | 7 ++++++- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py index d54e673c..36d23e53 100644 --- a/mediagoblin/auth/views.py +++ b/mediagoblin/auth/views.py @@ -18,7 +18,7 @@ import uuid from webob import exc -from mediagoblin.util import render_to_response +from mediagoblin.util import render_to_response, redirect from mediagoblin.db.util import ObjectId from mediagoblin.auth import lib as auth_lib from mediagoblin.auth import forms as auth_forms @@ -54,9 +54,7 @@ def register(request): send_verification_email(entry, request) - # Redirect to register_success - return exc.HTTPFound( - location=request.urlgen("mediagoblin.auth.register_success")) + return redirect(request, "mediagoblin.auth.register_success") return render_to_response(request, 'mediagoblin/auth/register.html', @@ -90,8 +88,7 @@ def login(request): if request.POST.get('next'): return exc.HTTPFound(location=request.POST['next']) else: - return exc.HTTPFound( - location=request.urlgen("index")) + return redirect(request, "index") else: # Prevent detecting who's on this system by testing login @@ -110,8 +107,7 @@ def logout(request): # Maybe deleting the user_id parameter would be enough? request.session.delete() - return exc.HTTPFound( - location=request.urlgen("index")) + return redirect(request, "index") def verify_email(request): @@ -164,8 +160,7 @@ def resend_activation(request): send_verification_email(request.user, request) - return exc.HTTPFound( - location=request.urlgen('mediagoblin.auth.resend_verification_success')) + return redirect(request, 'mediagoblin.auth.resend_verification_success') def resend_activation_success(request): diff --git a/mediagoblin/decorators.py b/mediagoblin/decorators.py index 34575320..c2fe3f9f 100644 --- a/mediagoblin/decorators.py +++ b/mediagoblin/decorators.py @@ -18,6 +18,7 @@ from bson.errors import InvalidId from webob import exc +from mediagoblin.util import redirect from mediagoblin.db.util import ObjectId @@ -38,9 +39,8 @@ def require_active_login(controller): def new_controller_func(request, *args, **kwargs): if request.user and \ request.user.get('status') == u'needs_email_verification': - return exc.HTTPFound( - location = request.urlgen( - 'mediagoblin.auth.verify_email_notice')) + return redirect(request, + 'mediagoblin.auth.verify_email_notice') elif not request.user or request.user.get('status') != u'active': return exc.HTTPFound( location="%s?next=%s" % ( diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index 258f14e3..04b73567 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -17,7 +17,7 @@ from webob import exc -from mediagoblin.util import render_to_response +from mediagoblin.util import render_to_response, redirect from mediagoblin.edit import forms from mediagoblin.edit.lib import may_edit_media from mediagoblin.decorators import require_active_login, get_user_media_entry @@ -51,10 +51,8 @@ def edit_media(request, media): media['slug'] = request.POST['slug'] media.save() - # redirect - return exc.HTTPFound( - location=request.urlgen("mediagoblin.user_pages.media_home", - user=media.uploader()['username'], media=media['slug'])) + return redirect(request, "mediagoblin.user_pages.media_home", + user=media.uploader()['username'], media=media['slug']) return render_to_response(request, 'mediagoblin/edit/edit.html', diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py index 95257b72..d4ecc75a 100644 --- a/mediagoblin/submit/views.py +++ b/mediagoblin/submit/views.py @@ -17,10 +17,9 @@ from os.path import splitext from cgi import FieldStorage -from webob import exc from werkzeug.utils import secure_filename -from mediagoblin.util import render_to_response +from mediagoblin.util import render_to_response, redirect from mediagoblin.decorators import require_active_login from mediagoblin.submit import forms as submit_forms from mediagoblin.process_media import process_media_initial @@ -76,9 +75,7 @@ def submit_start(request): # queue it for processing process_media_initial.delay(unicode(entry['_id'])) - # redirect - return exc.HTTPFound( - location=request.urlgen("mediagoblin.submit.success")) + return redirect(request, "mediagoblin.submit.success") return render_to_response(request, 'mediagoblin/submit/start.html', diff --git a/mediagoblin/util.py b/mediagoblin/util.py index f69c91f2..a0a09adf 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -29,7 +29,7 @@ from babel.localedata import exists import jinja2 import translitcodec from paste.deploy.loadwsgi import NicerConfigParser -from webob import Response +from webob import Response, exc from mediagoblin import globals as mgoblin_globals from mediagoblin.db.util import ObjectId @@ -123,6 +123,11 @@ def render_to_response(request, template, context): return Response(render_template(request, template, context)) +def redirect(request, *args, **kwargs): + """Returns a HTTPFound(), takes a request and then urlgen params""" + return exc.HTTPFound(location=request.urlgen(*args, **kwargs)) + + def setup_user_in_request(request): """ Examine a request and tack on a request.user parameter if that's From 67e8c45d2acba1b58ec2b69a367c3b10d0758c51 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 10:31:29 -0500 Subject: [PATCH 12/29] We should store the template path, not the template object, as the key in our testing cache --- mediagoblin/util.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mediagoblin/util.py b/mediagoblin/util.py index 3649b6c3..b675662e 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -94,7 +94,7 @@ def get_jinja_env(template_loader, locale): TEMPLATE_TEST_CONTEXT = {} -def render_template(request, template, context): +def render_template(request, template_path, context): """ Render a template with context. @@ -102,12 +102,12 @@ def render_template(request, template, context): Also stores the context if we're doing unit tests. Helpful! """ template = request.template_env.get_template( - template) + template_path) context['request'] = request rendered = template.render(context) if TESTS_ENABLED: - TEMPLATE_TEST_CONTEXT[template] = context + TEMPLATE_TEST_CONTEXT[template_path] = context return rendered From 0a791a94de138f1a80989f46856bfd2ccd56e1c7 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 10:33:59 -0500 Subject: [PATCH 13/29] Actually it's a lot better of an idea to load the full application out of the paste config file the way paste would than to load components of it ourselves. Aside from this being nicer, it's also necessary for the sake of getting the middleware working nicely. We could do it ourselves, but why bother when paste can just do it for us? --- mediagoblin/tests/tools.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/mediagoblin/tests/tools.py b/mediagoblin/tests/tools.py index 70b74b89..a51402e9 100644 --- a/mediagoblin/tests/tools.py +++ b/mediagoblin/tests/tools.py @@ -18,10 +18,9 @@ import pkg_resources import os, shutil -from paste.deploy import appconfig +from paste.deploy import appconfig, loadapp from webtest import TestApp -from mediagoblin import app from mediagoblin.db.open import setup_connection_and_db_from_config @@ -88,7 +87,7 @@ def get_test_app(dump_old_app=True): # TODO: Drop and recreate indexes # setup app and return - test_app = app.paste_app_factory( - config.global_conf, **config.local_conf) + test_app = loadapp( + 'config:' + TEST_APP_CONFIG) return TestApp(test_app) From 460ce56493442b1d89270313e7789fd455ef71e6 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 10:34:29 -0500 Subject: [PATCH 14/29] The first bit of the registration tests working. Not fully there, but it's clear that the webtest part is working, without having tested the database yet. :) --- mediagoblin/tests/test_auth.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py index 94ce6bba..b0355732 100644 --- a/mediagoblin/tests/test_auth.py +++ b/mediagoblin/tests/test_auth.py @@ -17,6 +17,10 @@ from mediagoblin.auth import lib as auth_lib +from mediagoblin.tests.tools import get_test_app + +from mediagoblin import util + ######################## # Test bcrypt auth funcs @@ -57,3 +61,25 @@ def test_bcrypt_gen_password_hash(): pw, hashed_pw, '3><7R45417') assert not auth_lib.bcrypt_check_password( 'notthepassword', hashed_pw, '3><7R45417') + + +def test_register_views(): + util.clear_test_template_context() + test_app = get_test_app() + + # Test doing a simple GET on the page + test_app.get('/auth/register/') + # Make sure it rendered with the appropriate template + assert util.TEMPLATE_TEST_CONTEXT.has_key( + 'mediagoblin/auth/register.html') + + # Try to register without providing anything, should error + util.clear_test_template_context() + test_app.post( + '/auth/register/', {}) + context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html'] + form = context['register_form'] + assert form.username.errors == [u'This field is required.'] + assert form.password.errors == [u'This field is required.'] + assert form.confirm_password.errors == [u'This field is required.'] + assert form.email.errors == [u'This field is required.'] From 651403f02553da25a8e611804ce36ac92bbaa8cd Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 15:30:07 -0500 Subject: [PATCH 15/29] Test registration form integrity --- mediagoblin/tests/test_auth.py | 64 ++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py index b0355732..6631c675 100644 --- a/mediagoblin/tests/test_auth.py +++ b/mediagoblin/tests/test_auth.py @@ -19,6 +19,7 @@ from mediagoblin.auth import lib as auth_lib from mediagoblin.tests.tools import get_test_app +from mediagoblin import globals as mgoblin_globals from mediagoblin import util @@ -68,12 +69,16 @@ def test_register_views(): test_app = get_test_app() # Test doing a simple GET on the page + # ----------------------------------- + test_app.get('/auth/register/') # Make sure it rendered with the appropriate template assert util.TEMPLATE_TEST_CONTEXT.has_key( 'mediagoblin/auth/register.html') # Try to register without providing anything, should error + # -------------------------------------------------------- + util.clear_test_template_context() test_app.post( '/auth/register/', {}) @@ -83,3 +88,62 @@ def test_register_views(): assert form.password.errors == [u'This field is required.'] assert form.confirm_password.errors == [u'This field is required.'] assert form.email.errors == [u'This field is required.'] + + # Try to register with fields that are known to be invalid + # -------------------------------------------------------- + + ## too short + util.clear_test_template_context() + test_app.post( + '/auth/register/', { + 'username': 'l', + 'password': 'o', + 'confirm_password': 'o', + 'email': 'l'}) + context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html'] + form = context['register_form'] + + assert form.username.errors == [ + u'Field must be between 3 and 30 characters long.'] + assert form.password.errors == [ + u'Field must be between 6 and 30 characters long.'] + + ## bad form + util.clear_test_template_context() + test_app.post( + '/auth/register/', { + 'username': '@_@', + 'email': 'lollerskates'}) + context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html'] + form = context['register_form'] + + assert form.username.errors == [ + u'Invalid input.'] + assert form.email.errors == [ + u'Invalid email address.'] + + ## mismatching passwords + util.clear_test_template_context() + test_app.post( + '/auth/register/', { + 'password': 'herpderp', + 'confirm_password': 'derpherp'}) + context = util.TEMPLATE_TEST_CONTEXT['mediagoblin/auth/register.html'] + form = context['register_form'] + + assert form.password.errors == [ + u'Passwords must match.'] + + ## At this point there should be no users in the database ;) + assert not mgoblin_globals.database.User.find().count() + + # Successful register + # ------------------- + ## Did we redirect to the proper page? Use the right template? + ## Make sure user is in place + ## Make sure we get email confirmation + ## Try logging in + + # We shouldn't be able to register with that user twice though... + + # Also check for double instances of an email address From b8fbd8179960f430b1ed3d6418946375c9efafd5 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 15:33:07 -0500 Subject: [PATCH 16/29] New password check error message --- mediagoblin/auth/forms.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mediagoblin/auth/forms.py b/mediagoblin/auth/forms.py index db8aaceb..7bc0aeb1 100644 --- a/mediagoblin/auth/forms.py +++ b/mediagoblin/auth/forms.py @@ -27,7 +27,9 @@ class RegistrationForm(wtforms.Form): 'Password', [wtforms.validators.Required(), wtforms.validators.Length(min=6, max=30), - wtforms.validators.EqualTo('confirm_password')]) + wtforms.validators.EqualTo( + 'confirm_password', + 'Passwords must match.')]) confirm_password = wtforms.PasswordField( 'Confirm password', [wtforms.validators.Required()]) From 7eac428526f92b308c9d041f3f427f8ad008b3d0 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 15:33:28 -0500 Subject: [PATCH 17/29] Instructions for running tests with the modern setup --- docs/hackinghowto.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/hackinghowto.rst b/docs/hackinghowto.rst index a56498bb..a9aadb62 100644 --- a/docs/hackinghowto.rst +++ b/docs/hackinghowto.rst @@ -152,7 +152,7 @@ Running the test suite Run:: - ./bin/nosetests + CELERY_CONFIG_MODULE=mediagoblin.celery_setup.from_tests ./bin/nosetests Running a shell From cb9bac0c83b7dd6d9153fc153b7282c26fe466f6 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 15:33:48 -0500 Subject: [PATCH 18/29] Just a bit of formatting for these unfinished tests ;) --- mediagoblin/tests/test_auth.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py index 6631c675..7a46a1ff 100644 --- a/mediagoblin/tests/test_auth.py +++ b/mediagoblin/tests/test_auth.py @@ -144,6 +144,8 @@ def test_register_views(): ## Make sure we get email confirmation ## Try logging in - # We shouldn't be able to register with that user twice though... + # Uniqueness checks + # ----------------- + ## We shouldn't be able to register with that user twice - # Also check for double instances of an email address + ## Also check for double instances of an email address From 9038c9f9acc4cfa257a52def2b292e6142e7d86a Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 15:41:08 -0500 Subject: [PATCH 19/29] I have a strong preference for aligning all parameters in a function call. --- mediagoblin/auth/views.py | 20 ++++++++++++-------- mediagoblin/edit/views.py | 3 ++- mediagoblin/submit/views.py | 7 ++++--- mediagoblin/user_pages/views.py | 6 ++++-- mediagoblin/views.py | 3 ++- 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py index 36d23e53..94a62225 100644 --- a/mediagoblin/auth/views.py +++ b/mediagoblin/auth/views.py @@ -56,13 +56,15 @@ def register(request): return redirect(request, "mediagoblin.auth.register_success") - return render_to_response(request, + return render_to_response( + request, 'mediagoblin/auth/register.html', {'register_form': register_form}) def register_success(request): - return render_to_response(request, + return render_to_response( + request, 'mediagoblin/auth/register_success.html', {}) @@ -96,7 +98,8 @@ def login(request): auth_lib.fake_login_attempt() login_failed = True - return render_to_response(request, + return render_to_response( + request, 'mediagoblin/auth/login.html', {'login_form': login_form, 'next': request.GET.get('next') or request.POST.get('next'), @@ -132,7 +135,8 @@ def verify_email(request): else: verification_successful = False - return render_to_response(request, + return render_to_response( + request, 'mediagoblin/auth/verify_email.html', {'user': user, 'verification_successful': verification_successful}) @@ -144,8 +148,8 @@ def verify_email_notice(request): When the user tries to do some action that requires their account to be verified beforehand, this view is called upon! """ - return render_to_response(request, - 'mediagoblin/auth/verification_needed.html', {}) + return render_to_response( + request, 'mediagoblin/auth/verification_needed.html', {}) def resend_activation(request): @@ -164,5 +168,5 @@ def resend_activation(request): def resend_activation_success(request): - return render_to_response(request, - 'mediagoblin/auth/resent_verification_email.html', {}) + return render_to_response( + request, 'mediagoblin/auth/resent_verification_email.html', {}) diff --git a/mediagoblin/edit/views.py b/mediagoblin/edit/views.py index 04b73567..c5f0f435 100644 --- a/mediagoblin/edit/views.py +++ b/mediagoblin/edit/views.py @@ -54,7 +54,8 @@ def edit_media(request, media): return redirect(request, "mediagoblin.user_pages.media_home", user=media.uploader()['username'], media=media['slug']) - return render_to_response(request, + return render_to_response( + request, 'mediagoblin/edit/edit.html', {'media': media, 'form': form}) diff --git a/mediagoblin/submit/views.py b/mediagoblin/submit/views.py index d4ecc75a..b409b64d 100644 --- a/mediagoblin/submit/views.py +++ b/mediagoblin/submit/views.py @@ -77,11 +77,12 @@ def submit_start(request): return redirect(request, "mediagoblin.submit.success") - return render_to_response(request, + return render_to_response( + request, 'mediagoblin/submit/start.html', {'submit_form': submit_form}) def submit_success(request): - return render_to_response(request, - 'mediagoblin/submit/success.html', {}) + return render_to_response( + request, 'mediagoblin/submit/success.html', {}) diff --git a/mediagoblin/user_pages/views.py b/mediagoblin/user_pages/views.py index e2fbcc80..323c3e54 100644 --- a/mediagoblin/user_pages/views.py +++ b/mediagoblin/user_pages/views.py @@ -42,7 +42,8 @@ def user_home(request, page): if media_entries == None: return exc.HTTPNotFound() - return render_to_response(request, + return render_to_response( + request, 'mediagoblin/user_pages/user.html', {'user': user, 'media_entries': media_entries, @@ -52,7 +53,8 @@ def user_home(request, page): @get_user_media_entry def media_home(request, media): """'Homepage' of a MediaEntry()""" - return render_to_response(request, + return render_to_response( + request, 'mediagoblin/user_pages/media.html', {'media': media}) diff --git a/mediagoblin/views.py b/mediagoblin/views.py index 22673d59..3bff3974 100644 --- a/mediagoblin/views.py +++ b/mediagoblin/views.py @@ -21,6 +21,7 @@ def root_view(request): media_entries = request.db.MediaEntry.find( {u'state': u'processed'}).sort('created', DESCENDING) - return render_to_response(request, + return render_to_response( + request, 'mediagoblin/root.html', {'media_entries': media_entries}) From 2262b2a9e1aad834bce8782216c3d8068a008618 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 15:58:35 -0500 Subject: [PATCH 20/29] Made a simple template rendering view and switched a bunch of code over to using it --- mediagoblin/auth/routing.py | 9 ++++++--- mediagoblin/auth/views.py | 22 ---------------------- mediagoblin/submit/routing.py | 4 ++-- mediagoblin/views.py | 13 +++++++++++-- 4 files changed, 19 insertions(+), 29 deletions(-) diff --git a/mediagoblin/auth/routing.py b/mediagoblin/auth/routing.py index a8909fbb..46c585d2 100644 --- a/mediagoblin/auth/routing.py +++ b/mediagoblin/auth/routing.py @@ -20,7 +20,8 @@ auth_routes = [ Route('mediagoblin.auth.register', '/register/', controller='mediagoblin.auth.views:register'), Route('mediagoblin.auth.register_success', '/register/success/', - controller='mediagoblin.auth.views:register_success'), + template='mediagoblin/auth/register_success.html', + controller='mediagoblin.views:simple_template_render'), Route('mediagoblin.auth.login', '/login/', controller='mediagoblin.auth.views:login'), Route('mediagoblin.auth.logout', '/logout/', @@ -28,9 +29,11 @@ auth_routes = [ Route('mediagoblin.auth.verify_email', '/verify_email/', controller='mediagoblin.auth.views:verify_email'), Route('mediagoblin.auth.verify_email_notice', '/verification_required/', - controller='mediagoblin.auth.views:verify_email_notice'), + template='mediagoblin/auth/verification_needed.html', + controller='mediagoblin.views:simple_template_render'), Route('mediagoblin.auth.resend_verification', '/resend_verification/', controller='mediagoblin.auth.views:resend_activation'), Route('mediagoblin.auth.resend_verification_success', '/resend_verification_success/', - controller='mediagoblin.auth.views:resend_activation_success')] + template='mediagoblin/auth/resent_verification_email.html', + controller='mediagoblin.views:simple_template_render')] diff --git a/mediagoblin/auth/views.py b/mediagoblin/auth/views.py index 94a62225..1d00f382 100644 --- a/mediagoblin/auth/views.py +++ b/mediagoblin/auth/views.py @@ -62,12 +62,6 @@ def register(request): {'register_form': register_form}) -def register_success(request): - return render_to_response( - request, - 'mediagoblin/auth/register_success.html', {}) - - def login(request): """ MediaGoblin login view. @@ -141,16 +135,6 @@ def verify_email(request): {'user': user, 'verification_successful': verification_successful}) -def verify_email_notice(request): - """ - Verify warning view. - - When the user tries to do some action that requires their account - to be verified beforehand, this view is called upon! - """ - return render_to_response( - request, 'mediagoblin/auth/verification_needed.html', {}) - def resend_activation(request): """ @@ -158,15 +142,9 @@ def resend_activation(request): Resend the activation email. """ - request.user['verification_key'] = unicode(uuid.uuid4()) request.user.save() send_verification_email(request.user, request) return redirect(request, 'mediagoblin.auth.resend_verification_success') - - -def resend_activation_success(request): - return render_to_response( - request, 'mediagoblin/auth/resent_verification_email.html', {}) diff --git a/mediagoblin/submit/routing.py b/mediagoblin/submit/routing.py index cff28acb..3edbab70 100644 --- a/mediagoblin/submit/routing.py +++ b/mediagoblin/submit/routing.py @@ -20,5 +20,5 @@ submit_routes = [ Route('mediagoblin.submit.start', '/', controller='mediagoblin.submit.views:submit_start'), Route('mediagoblin.submit.success', '/success/', - controller='mediagoblin.submit.views:submit_success'), - ] + template='mediagoblin/submit/success.html', + controller='mediagoblin.views:simple_template_render')] diff --git a/mediagoblin/views.py b/mediagoblin/views.py index 3bff3974..5b6d9773 100644 --- a/mediagoblin/views.py +++ b/mediagoblin/views.py @@ -22,6 +22,15 @@ def root_view(request): {u'state': u'processed'}).sort('created', DESCENDING) return render_to_response( - request, - 'mediagoblin/root.html', + request, 'mediagoblin/root.html', {'media_entries': media_entries}) + + +def simple_template_render(request): + """ + A view for absolutely simple template rendering. + Just make sure 'template' is in the matchdict! + """ + template_name = request.matchdict['template'] + return render_to_response( + request, template_name, {}) From 8045fd4074279426af7a7be71d10e5963c8fcf8d Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 16:43:25 -0500 Subject: [PATCH 21/29] Make sure that get_test_app() really does wipe the database --- mediagoblin/tests/test_tests.py | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 mediagoblin/tests/test_tests.py diff --git a/mediagoblin/tests/test_tests.py b/mediagoblin/tests/test_tests.py new file mode 100644 index 00000000..3ecbfac7 --- /dev/null +++ b/mediagoblin/tests/test_tests.py @@ -0,0 +1,38 @@ +# GNU MediaGoblin -- federated, autonomous media hosting +# Copyright (C) 2011 Free Software Foundation, Inc +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from mediagoblin.tests.tools import get_test_app + +from mediagoblin import globals as mgoblin_globals + + +def test_get_test_app_wipes_db(): + """ + Make sure we get a fresh database on every wipe :) + """ + get_test_app() + assert mgoblin_globals.database.User.find().count() == 0 + + new_user = mgoblin_globals.database.User() + new_user['username'] = u'lolcat' + new_user['email'] = u'lol@cats.example.org' + new_user['pw_hash'] = u'pretend_this_is_a_hash' + new_user.save() + assert mgoblin_globals.database.User.find().count() == 1 + + get_test_app() + + assert mgoblin_globals.database.User.find().count() == 0 From 2c3fd5c5bf6ac56638195863948e30a9d96394c9 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 17:31:18 -0500 Subject: [PATCH 22/29] Moved the verification url generation string template to a global variable --- mediagoblin/auth/lib.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/mediagoblin/auth/lib.py b/mediagoblin/auth/lib.py index dc5f9941..dc37c230 100644 --- a/mediagoblin/auth/lib.py +++ b/mediagoblin/auth/lib.py @@ -89,6 +89,10 @@ def fake_login_attempt(): randplus_stored_hash == randplus_hashed_pass +EMAIL_VERIFICATION_TEMPLATE = ( + u"http://{host}{uri}?" + u"userid={userid}&token={verification_key}") + def send_verification_email(user, request): """ Send the verification email to users to activate their accounts. @@ -113,8 +117,8 @@ def send_verification_email(user, request): 'GNU MediaGoblin - Verify your email!', email_template.render( username=user['username'], - verification_url='http://{host}{uri}?userid={userid}&token={verification_key}'.format( - host=request.host, - uri=request.urlgen('mediagoblin.auth.verify_email'), - userid=unicode(user['_id']), - verification_key=user['verification_key']))) + verification_url=EMAIL_VERIFICATION_TEMPLATE.format( + host=request.host, + uri=request.urlgen('mediagoblin.auth.verify_email'), + userid=unicode(user['_id']), + verification_key=user['verification_key']))) From 23cc15c99837913d4c87c7829745f71067902d3b Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 17:35:20 -0500 Subject: [PATCH 23/29] Use render_template utility so we can test whether or not this email sends --- mediagoblin/auth/lib.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/mediagoblin/auth/lib.py b/mediagoblin/auth/lib.py index dc37c230..f40e560f 100644 --- a/mediagoblin/auth/lib.py +++ b/mediagoblin/auth/lib.py @@ -19,7 +19,7 @@ import random import bcrypt -from mediagoblin.util import send_email +from mediagoblin.util import send_email, render_template from mediagoblin import globals as mgoblin_globals @@ -101,9 +101,14 @@ def send_verification_email(user, request): - user: a user object - request: the request """ - - email_template = request.template_env.get_template( - 'mediagoblin/auth/verification_email.txt') + rendered_email = render_template( + request, 'mediagoblin/auth/verification_email.txt', + {'username': user['username'], + 'verification_url': EMAIL_VERIFICATION_TEMPLATE.format( + host=request.host, + uri=request.urlgen('mediagoblin.auth.verify_email'), + userid=unicode(user['_id']), + verification_key=user['verification_key'])}) # TODO: There is no error handling in place send_email( @@ -115,10 +120,4 @@ def send_verification_email(user, request): # specific GNU MediaGoblin instance in the subject line. For # example "GNU MediaGoblin @ Wandborg - [...]". 'GNU MediaGoblin - Verify your email!', - email_template.render( - username=user['username'], - verification_url=EMAIL_VERIFICATION_TEMPLATE.format( - host=request.host, - uri=request.urlgen('mediagoblin.auth.verify_email'), - userid=unicode(user['_id']), - verification_key=user['verification_key']))) + rendered_email) From 1972a888b3619d23968a4efcd2cfab550ca588f1 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 17:39:47 -0500 Subject: [PATCH 24/29] Make sure we can register, and then that we get the verification email --- mediagoblin/tests/test_auth.py | 50 +++++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py index 7a46a1ff..43778a64 100644 --- a/mediagoblin/tests/test_auth.py +++ b/mediagoblin/tests/test_auth.py @@ -14,11 +14,12 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . +import urlparse + +from nose.tools import assert_equal from mediagoblin.auth import lib as auth_lib - from mediagoblin.tests.tools import get_test_app - from mediagoblin import globals as mgoblin_globals from mediagoblin import util @@ -139,10 +140,51 @@ def test_register_views(): # Successful register # ------------------- + util.clear_test_template_context() + response = test_app.post( + '/auth/register/', { + 'username': 'happygirl', + 'password': 'iamsohappy', + 'confirm_password': 'iamsohappy', + 'email': 'happygrrl@example.org'}) + response.follow() + ## Did we redirect to the proper page? Use the right template? + assert_equal( + urlparse.urlsplit(response.location)[2], + '/auth/register/success/') + assert util.TEMPLATE_TEST_CONTEXT.has_key( + 'mediagoblin/auth/register_success.html') + ## Make sure user is in place - ## Make sure we get email confirmation - ## Try logging in + new_user = mgoblin_globals.database.User.find_one( + {'username': 'happygirl'}) + assert new_user + assert new_user['status'] == u'needs_email_verification' + assert new_user['email_verified'] == False + + ## Make sure we get email confirmation, and try verifying + assert len(util.EMAIL_TEST_INBOX) == 1 + message = util.EMAIL_TEST_INBOX.pop() + assert message['To'] == 'happygrrl@example.org' + email_context = util.TEMPLATE_TEST_CONTEXT[ + 'mediagoblin/auth/verification_email.txt'] + assert email_context['verification_url'] in message.get_payload(decode=True) + + path = urlparse.urlsplit(email_context['verification_url'])[2] + get_params = urlparse.urlsplit(email_context['verification_url'])[3] + assert path == u'/auth/verify_email/' + parsed_get_params = urlparse.parse_qs(get_params) + + ### user should have these same parameters + assert parsed_get_params['userid'] == [ + unicode(new_user['_id'])] + assert parsed_get_params['token'] == [ + new_user['verification_key']] + + ## Verify the email + + ## TODO: Try logging in # Uniqueness checks # ----------------- From 7b1e17ed0d3224e997507e1ef14a111577b8b7b1 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 17:49:43 -0500 Subject: [PATCH 25/29] Email verification view test works --- mediagoblin/tests/test_auth.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py index 43778a64..4009c466 100644 --- a/mediagoblin/tests/test_auth.py +++ b/mediagoblin/tests/test_auth.py @@ -182,7 +182,31 @@ def test_register_views(): assert parsed_get_params['token'] == [ new_user['verification_key']] - ## Verify the email + ## Try verifying with bs verification key, shouldn't work + util.clear_test_template_context() + test_app.get( + "/auth/verify_email/?userid=%s&token=total_bs" % unicode( + new_user['_id'])) + context = util.TEMPLATE_TEST_CONTEXT[ + 'mediagoblin/auth/verify_email.html'] + assert context['verification_successful'] == False + new_user = mgoblin_globals.database.User.find_one( + {'username': 'happygirl'}) + assert new_user + assert new_user['status'] == u'needs_email_verification' + assert new_user['email_verified'] == False + + ## Verify the email activation works + util.clear_test_template_context() + test_app.get("%s?%s" % (path, get_params)) + context = util.TEMPLATE_TEST_CONTEXT[ + 'mediagoblin/auth/verify_email.html'] + assert context['verification_successful'] == True + new_user = mgoblin_globals.database.User.find_one( + {'username': 'happygirl'}) + assert new_user + assert new_user['status'] == u'active' + assert new_user['email_verified'] == True ## TODO: Try logging in From 8a869db8e46d120ff12854462e828220cd5ebf6a Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 18:14:48 -0500 Subject: [PATCH 26/29] Make sure that two users with the same username can't register. --- mediagoblin/tests/test_auth.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py index 4009c466..0f954ee0 100644 --- a/mediagoblin/tests/test_auth.py +++ b/mediagoblin/tests/test_auth.py @@ -213,5 +213,18 @@ def test_register_views(): # Uniqueness checks # ----------------- ## We shouldn't be able to register with that user twice + util.clear_test_template_context() + response = test_app.post( + '/auth/register/', { + 'username': 'happygirl', + 'password': 'iamsohappy2', + 'confirm_password': 'iamsohappy2', + 'email': 'happygrrl2@example.org'}) + + context = util.TEMPLATE_TEST_CONTEXT[ + 'mediagoblin/auth/register.html'] + form = context['register_form'] + assert form.username.errors == [ + u'Sorry, a user with that name already exists.'] - ## Also check for double instances of an email address + ## TODO: Also check for double instances of an email address? From 2fecc29d06f882541a2b476a618e22a0a90d2736 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Sun, 5 Jun 2011 18:16:31 -0500 Subject: [PATCH 27/29] Docstring for test_register_views() --- mediagoblin/tests/test_auth.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py index 0f954ee0..cf6d48f5 100644 --- a/mediagoblin/tests/test_auth.py +++ b/mediagoblin/tests/test_auth.py @@ -66,6 +66,9 @@ def test_bcrypt_gen_password_hash(): def test_register_views(): + """ + Massive test function that all our registration-related views all work. + """ util.clear_test_template_context() test_app = get_test_app() From 66471f0ee4740daeac2b8a42b88dadec630b8d67 Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 6 Jun 2011 07:44:12 -0500 Subject: [PATCH 28/29] A clear_test_buckets() method --- mediagoblin/util.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/mediagoblin/util.py b/mediagoblin/util.py index cbb937ee..64e21ca9 100644 --- a/mediagoblin/util.py +++ b/mediagoblin/util.py @@ -44,6 +44,26 @@ def _activate_testing(): TESTS_ENABLED = True +def clear_test_buckets(): + """ + We store some things for testing purposes that should be cleared + when we want a "clean slate" of information for our next round of + tests. Call this function to wipe all that stuff clean. + + Also wipes out some other things we might redefine during testing, + like the jinja envs. + """ + global SETUP_JINJA_ENVS + SETUP_JINJA_ENVS = {} + + global EMAIL_TEST_INBOX + global EMAIL_TEST_MBOX_INBOX + EMAIL_TEST_INBOX = [] + EMAIL_TEST_MBOX_INBOX = [] + + clear_test_template_context() + + def get_jinja_loader(user_template_path=None): """ Set up the Jinja template loaders, possibly allowing for user From 3aa4c668b9bfe53ed58d4ae21ed91210df7ad9ff Mon Sep 17 00:00:00 2001 From: Christopher Allan Webber Date: Mon, 6 Jun 2011 07:45:18 -0500 Subject: [PATCH 29/29] A setup_fresh_app decorator which should make writing tests a bit easier. Setting test_register_views() to use it also. --- mediagoblin/tests/test_auth.py | 8 +++----- mediagoblin/tests/tools.py | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/mediagoblin/tests/test_auth.py b/mediagoblin/tests/test_auth.py index cf6d48f5..cdfeccab 100644 --- a/mediagoblin/tests/test_auth.py +++ b/mediagoblin/tests/test_auth.py @@ -19,7 +19,7 @@ import urlparse from nose.tools import assert_equal from mediagoblin.auth import lib as auth_lib -from mediagoblin.tests.tools import get_test_app +from mediagoblin.tests.tools import setup_fresh_app from mediagoblin import globals as mgoblin_globals from mediagoblin import util @@ -65,13 +65,11 @@ def test_bcrypt_gen_password_hash(): 'notthepassword', hashed_pw, '3><7R45417') -def test_register_views(): +@setup_fresh_app +def test_register_views(test_app): """ Massive test function that all our registration-related views all work. """ - util.clear_test_template_context() - test_app = get_test_app() - # Test doing a simple GET on the page # ----------------------------------- diff --git a/mediagoblin/tests/tools.py b/mediagoblin/tests/tools.py index a51402e9..342b54b7 100644 --- a/mediagoblin/tests/tools.py +++ b/mediagoblin/tests/tools.py @@ -21,6 +21,8 @@ import os, shutil from paste.deploy import appconfig, loadapp from webtest import TestApp +from mediagoblin import util +from mediagoblin.decorators import _make_safe from mediagoblin.db.open import setup_connection_and_db_from_config @@ -91,3 +93,17 @@ def get_test_app(dump_old_app=True): 'config:' + TEST_APP_CONFIG) return TestApp(test_app) + + +def setup_fresh_app(func): + """ + Decorator to setup a fresh test application for this function. + + Cleans out test buckets and passes in a new, fresh test_app. + """ + def wrapper(*args, **kwargs): + test_app = get_test_app() + util.clear_test_buckets() + return func(test_app, *args, **kwargs) + + return _make_safe(wrapper, func)