Commits

Chris Miles  committed d0c8214

Added working model & functional tests to the generated project. Ref #1 & #2.

version = '0.2b1'

  • Participants
  • Parent commits 17f124f

Comments (0)

Files changed (10)

File BlastOff/README.txt

 
 http://bitbucket.org/chrismiles/blastoff/
 
-BlastOff is a Pylons template to generate a skeleton application
-pre-configured with SQLAlchemy, repoze.who authentication, mako
-templates and SchemaBot database schema version control.
+BlastOff is a Pylons application template providing a working site
+skeleton configured with SQLAlchemy, mako, repoze.who, ToscaWidgets,
+TurboMail, WebFlash and (optionally) SchemaBot. The generated app is
+pre-configured with authentication, login and registration forms, and
+(optionally) email confirmation.
 
 Create a BlastOff-generated package using paster::
 

File BlastOff/blastoff/template/+package+/config/middleware.py_tmpl

 from pylons.middleware import ErrorHandler, StatusCodeRedirect
 from pylons.wsgiapp import PylonsApp
 from routes.middleware import RoutesMiddleware
-from repoze.who.config import make_middleware_with_config as make_who_with_config
+from repoze.who.plugins.testutil import make_middleware_with_config as make_who_with_config
 import tw.api   # ToscaWidgets
 
 from {{package}}.config.environment import load_environment
     
     # Repoze.who middleware
     who_config = os.path.join(os.path.dirname(__file__), 'who.ini')
-    app = make_who_with_config(app, global_conf, who_config, app_conf['who.log_file'], app_conf['who.log_level'])
-
+    app = make_who_with_config(
+        app,
+        global_conf,
+        who_config,
+        app_conf['who.log_file'],
+        app_conf['who.log_level'],
+        skip_authentication=asbool(app_conf.get('skip_authentication', 'false'))
+    )
+    
     if asbool(full_stack):
         # Handle Python exceptions
         app = ErrorHandler(app, global_conf, **config['pylons.errorware'])

File BlastOff/blastoff/template/+package+/tests/functional/test_account.py_tmpl

 from {{package}}.tests import *
 
 class TestAccountController(TestController):
-
-    def test_index(self):
-        response = self.app.get(url(controller='account', action='index'))
+    def test_login(self):
+        response = self.app.get(url(controller='account', action='login'), status=200)
+        
         # Test response...
+        assert 'Username:' in response.body
+        assert 'Password:' in response.body
+    
+    def test_register(self):
+        response = self.app.get(url(controller='account', action='register'), status=200)
+        
+        assert '<form id="register_account"' in response.body
+    

File BlastOff/blastoff/template/+package+/tests/functional/test_home.py_tmpl

+from {{package}} import model
+from {{package}}.model.meta import Session
 from {{package}}.tests import *
+from {{package}}.tests.test_models import clean_db
 
 class TestHomeController(TestController):
-
-    def test_index(self):
-        response = self.app.get(url(controller='home', action='index'))
+    def tearDown(self):
+        # Empty out the tables
+        clean_db()
+    
+    def test_index_anonymous(self):
+        response = self.app.get(url(controller='home', action='index'), status=200)
+        
         # Test response...
+        assert 'Home' in response.body
+        assert 'Register' in response.body
+    
+    def test_index_authenticated(self):
+        # Create a user to authenticate as
+        user = model.User(
+            user_name = u'test1',
+            email_address = u'test1@example.com',
+            display_name = u'Test One',
+            password = u'myPassword9!',
+            activated = True,
+        )
+        Session.add(user)
+        Session.commit()
+        
+        environ = {'REMOTE_USER': 'test1'}
+        
+        response = self.app.get(url(controller='home', action='index'), extra_environ=environ, status=200)
+        
+        # Test response...
+        assert 'Test One' in response.body
+        assert 'Logout' in response.body
+    

File BlastOff/blastoff/template/+package+/tests/test_models.py

Empty file removed.

File BlastOff/blastoff/template/+package+/tests/test_models.py_tmpl

+#!/usr/bin/env python
+# encoding: utf-8
+"""
+test_models.py
+"""
+from datetime import date, datetime
+import hashlib
+import unittest
+
+import sqlalchemy as sa
+
+from {{package}} import model
+from {{package}}.model.meta import Session
+
+MODEL_CLASSES = (
+    model.User,
+{{if email_confirmation}}
+    model.UserActivation,
+{{endif}}
+)
+
+
+# ---- Utility Functions ----
+
+def clean_db():
+    for model_class in MODEL_CLASSES:
+        for rec in Session.query(model_class):
+            Session.delete(rec)
+        Session.commit()
+
+
+# ---- Test Cases ----
+
+class UserTests(unittest.TestCase):
+    def setUp(self):
+        user = model.User(
+            user_name = u'test1',
+            email_address = u'test1@example.com',
+            display_name = u'Test One',
+            password = u'myPassword9!',
+            activated = True,
+        )
+        Session.add(user)
+        Session.commit()
+    
+    def tearDown(self):
+        # Empty out the tables
+        clean_db()
+    
+    def test_add_user(self):
+        u = Session.query(model.User).filter_by(user_name=u'test1').one()
+        self.failUnlessEqual(u.email_address, u'test1@example.com')
+        self.failUnlessEqual(u.display_name, u'Test One')
+        self.failUnlessEqual(u.password, hashlib.sha512(u'myPassword9!').hexdigest())
+        self.failUnlessEqual(u.activated, True),
+        self.failUnless(u.id > 0)
+        self.failUnless(u.created <= datetime.now())
+    
+    def test_duplicate_user_name(self):
+        user = model.User(
+            user_name = u'test1',
+            email_address = u'test1@example2.com',
+            display_name = u'Test One Again',
+            password = u'myPassword8!',
+            activated = True,
+        )
+        Session.add(user)
+        self.failUnlessRaises(sa.exc.IntegrityError, Session.commit)
+        Session.rollback()
+    
+    def test_validate_password(self):
+        u = Session.query(model.User).filter_by(user_name=u'test1').one()
+        self.failUnlessEqual(u.validate_password(u'myPassword9!'), True)
+    
+
+{{if email_confirmation}}
+class UserActivationTests(unittest.TestCase):
+    def setUp(self):
+        user = model.User(
+            user_name = u'test1',
+            email_address = u'test1@example.com',
+            display_name = u'Test One',
+            password = u'myPassword9!',
+            activated = True,
+        )
+        Session.add(user)
+        
+        act = model.UserActivation()
+        act.key = u'testkey'
+        Session.add(act)
+        
+        act.user = user
+        
+        Session.commit()
+    
+    def tearDown(self):
+        # Empty out the tables
+        clean_db()
+    
+    def test_user_activation(self):
+        u = Session.query(model.User).filter_by(user_name=u'test1').one()
+        self.failUnlessEqual(u.activation.key, u'testkey')
+    
+{{endif}}

File BlastOff/blastoff/template/README.txt_tmpl

     paster setup-app config.ini
 
 Then you are ready to go.
+
+Unit Tests
+==========
+
+Nose is used to run the project unit tests.  Install nose, if required,
+with::
+
+    easy_install nose
+
+then, in the root of the project (where setup.py is located) run the
+tests with::
+
+    nosetests

File BlastOff/blastoff/template/setup.py_tmpl

         "repoze.who",
         "repoze.who.plugins.sa",
         "repoze.who-friendlyform",
+        "repoze.who-testutil",
         "ToscaWidgets",
         "tw.forms",
         "TurboMail >= 3.0b2",

File BlastOff/blastoff/template/test.ini_tmpl

 
 [app:main]
 use = config:development.ini
+sqlalchemy.url = sqlite:///:memory:
+{{if use_schemabot}}
+schemabot.update_database = true
+{{endif}}
+skip_authentication = true
 
 # Add additional test specific configuration options as necessary.

File BlastOff/setup.py

 from setuptools import setup, find_packages
 import sys, os
 
-version = '0.2a3'
+version = '0.2b1'
 
 # OS X: prevent 'tar' from including resource forks ("._*" files)
 os.environ['COPYFILE_DISABLE'] = 'true'
 
 Point your browser at the URL http://127.0.0.1:5000/
 
+The generated project contains model and functional tests that can be
+run using the "nosetests" command (requires the Nose package to be
+installed).
+
 
 Documentation
 -------------