Diez Roggisch avatar Diez Roggisch committed 500fa3f

moved tests to separate directory.

Comments (0)

Files changed (132)

Empty file added.

+# -*- coding: utf-8 -*-
+
+import os, shutil
+from unittest import TestCase
+from xmlrpclib import loads, dumps
+import warnings
+
+import webob
+import beaker
+import pylons
+from paste.registry import Registry
+from paste.registry import RegistryManager
+from webtest import TestApp
+from paste.wsgiwrappers import WSGIRequest, WSGIResponse
+from paste import httpexceptions
+
+
+import tg
+import pylons
+from tg import tmpl_context
+from tests.test_stack import app_from_config, TestConfig
+from pylons import url
+from routes import URLGenerator, Mapper
+from tg.util import Bunch
+from pylons.util import ContextObj, PylonsContext
+from pylons.controllers.util import Request, Response
+from tg.controllers import TGController
+
+from pylons.configuration import response_defaults
+response_defaults['headers']['Content-Type'] = None
+
+from pylons.testutil import ControllerWrap, SetupCacheGlobal
+
+from beaker.middleware import CacheMiddleware
+
+data_dir = os.path.dirname(os.path.abspath(__file__))
+session_dir = os.path.join(data_dir, 'session')
+
+def setup_session_dir():
+    if not os.path.exists(session_dir):
+        os.makedirs(session_dir)
+
+def teardown_session_dir():
+    shutil.rmtree(session_dir, ignore_errors=True)
+
+default_config = {
+        'debug': False,
+        'pylons.package': None,
+        'pylons.paths': {'root': None,
+                         'controllers': None,
+                         'templates': [],
+                         'static_files': None},
+        'pylons.db_engines': {},
+        'pylons.environ_config': dict(session='beaker.session',
+                                      cache='beaker.cache'),
+        'pylons.g': None,
+        'pylons.h': None,
+        'pylons.request_options': pylons.configuration.request_defaults.copy(),
+        'pylons.response_options': pylons.configuration.response_defaults.copy(),
+        'pylons.strict_c': False,
+        'pylons.stritmpl_contextt_tmpl_context':False,
+        'pylons.c_attach_args': True,
+        'pylons.tmpl_context_attach_args': True,
+        'buffet.template_engines': [],
+        'buffet.template_options': {},
+        'default_renderer':'genshi',
+        'renderers':['genshi','json'],
+        'render_functions':{'genshi':tg.render.render_genshi, 'json':tg.render.render_json},
+        'use_legacy_renderers':False,
+        'use_sqlalchemy': False
+}
+
+default_environ = {
+    'pylons.use_webob' : True,
+    'pylons.routes_dict': dict(action='index'),
+    'paste.config': dict(global_conf=dict(debug=True))
+}
+
+default_map = Mapper()
+
+# Setup a default route for the error controller:
+default_map.connect('error/:action/:id', controller='error')
+# Setup a default route for the root of object dispatch
+default_map.connect('*url', controller='root', action='routes_placeholder')
+
+def make_app(controller_klass=None, environ=None):
+    """Creates a `TestApp` instance."""
+    if environ is None:
+        environ = {}
+    environ['pylons.routes_dict'] = {}
+    environ['pylons.routes_dict']['action'] = "routes_placeholder"
+
+
+    if controller_klass is None:
+        controller_klass = TGController
+
+    app = ControllerWrap(controller_klass)
+    app = SetupCacheGlobal(app, environ, setup_cache=True, setup_session=True)
+    app = RegistryManager(app)
+    app = beaker.middleware.SessionMiddleware(app, {}, data_dir=session_dir)
+    app = CacheMiddleware(app, {}, data_dir=os.path.join(data_dir, 'cache'))
+    app = httpexceptions.make_middleware(app)
+    return TestApp(app)
+
+def create_request(path, environ=None):
+    """Helper used in test cases to quickly setup a request obj.
+
+    ``path``
+        The path will become PATH_INFO
+    ``environ``
+        Additional environment
+
+    Returns an instance of the `webob.Request` object.
+    """
+    # setup the environ
+    if environ is None:
+        environ = {}
+    environ.update(default_environ)
+    # create a "blank" WebOb Request object
+    # using Pylon's Request which is a webob Request plus
+    # some compatibility methods
+    req = Request.blank(path, environ)
+    # setup a Registry
+    reg = environ.setdefault('paste.registry', Registry())
+    reg.prepare()
+
+    # setup pylons.request to point to our Registry
+    reg.register(pylons.request, req)
+
+
+    # setup tmpl context
+    tmpl_context._push_object(ContextObj())
+    url._push_object(URLGenerator(default_map, environ))
+    return req
+
+class TestWSGIController(TestCase):
+    def setUp(self):
+        tmpl_options = {}
+        tmpl_options['genshi.search_path'] = ['tests']
+        self._ctx = ContextObj()
+        tmpl_context._push_object(self._ctx)
+
+        warnings.simplefilter("ignore")
+        pylons.config.push_process_config(default_config)
+        warnings.resetwarnings()
+        setup_session_dir()
+
+    def tearDown(self):
+        tmpl_context._pop_object(self._ctx)
+#        pylons.config.pop_thread_config()
+        pylons.config.pop_process_config()
+        teardown_session_dir()
+
+    def get_response(self, **kargs):
+        url = kargs.pop('_url', '/')
+        self.environ['pylons.routes_dict'].update(kargs)
+
+        return self.app.get(url, extra_environ=self.environ)
+
+    def post_response(self, **kargs):
+        url = kargs.pop('_url', '/')
+
+        return self.app.post(url, extra_environ=self.environ, params=kargs)
+

tests/fixtures/__init__.py

+"""TG developers may use this package to store fixtures for the test suite"""

tests/fixtures/model.py

+"""A fake application's model objects"""
+
+from datetime import datetime
+
+from zope.sqlalchemy import ZopeTransactionExtension
+from sqlalchemy import Table, ForeignKey, Column
+from sqlalchemy.orm import scoped_session, sessionmaker, relation, backref, \
+                           synonym
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.types import String, Unicode, UnicodeText, Integer, DateTime, \
+                             Boolean, Float
+
+
+# Global session manager.  DBSession() returns the session object
+# appropriate for the current web request.
+maker = sessionmaker(autoflush=True, autocommit=False,
+                     extension=ZopeTransactionExtension())
+DBSession = scoped_session(maker)
+
+# By default, the data model is defined with SQLAlchemy's declarative
+# extension, but if you need more control, you can switch to the traditional
+# method.
+DeclarativeBase = declarative_base()
+
+# Global metadata.
+# The default metadata is the one from the declarative base.
+metadata = DeclarativeBase.metadata
+
+def init_model(engine):
+    """Call me before using any of the tables or classes in the model."""
+    DBSession.configure(bind=engine)
+
+
+class Group(DeclarativeBase):
+    """An ultra-simple group definition.
+    """
+    __tablename__ = 'tg_group'
+    
+    group_id = Column(Integer, autoincrement=True, primary_key=True)
+    
+    group_name = Column(Unicode(16), unique=True)
+    
+    display_name = Column(Unicode(255))
+    
+    created = Column(DateTime, default=datetime.now)
+    
+    def __repr__(self):
+        return '<Group: name=%s>' % self.group_name
Add a comment to this file

tests/i18n/de/LC_MESSAGES/tests.mo

Binary file added.

tests/i18n/de/LC_MESSAGES/tests.po

+# German translations for tests.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: tests 0.0\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2009-07-07 07:07+0700\n"
+"PO-Revision-Date: 2009-07-07 07:17+0700\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: de <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n != 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.4\n"
+
+#: tests/controllers/root.py:13
+msgid "Your application is now running"
+msgstr "Ihre Anwendung läuft jetzt einwandfrei"
Add a comment to this file

tests/i18n/ru/LC_MESSAGES/tests.mo

Binary file added.

tests/i18n/ru/LC_MESSAGES/tests.po

+# Russian translations for tests.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: tests 0.0\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2009-07-07 07:07+0700\n"
+"PO-Revision-Date: 2009-07-07 07:17+0700\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: ru <LL@li.org>\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
+"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 0.9.4\n"
+
+#: tests/controllers/root.py:13
+msgid "Your application is now running"
+msgstr "Ваши приложение успешно запущено"

tests/non_overridden.html

+<html>
+<head>
+<title></title>
+</head>
+<body>
+Not overridden.
+</body>
+</html>

tests/overridden.html

+<html>
+<head>
+<title> overridden.html </title>
+</head>
+<body>
+This is overridden.
+</body>
+</html>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
+                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml"
+      xmlns:py="http://genshi.edgewall.org/"
+      xmlns:xi="http://www.w3.org/2001/XInclude">
+
+
+<head>
+  <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
+  <title>Welcome to TurboGears 2.0 - standing on the shoulders of giants 
+    since 2007</title>
+</head>
+
+<body>
+    <h1>All objects from locals():<h1>
+
+    <div py:for="item in sorted(locals()['data'].keys())">
+      ${item}: ${repr(locals()['data'][item])}</div>
+</body>
+</html>

tests/test_caching.py

+# -*- coding: utf-8 -*-
+
+""" Test cases for Pylons caching.  See:
+
+http://wiki.pylonshq.com/display/pylonsdocs/Caching+in+Templates+and+Controllers
+
+For more details.
+"""
+
+
+import tg
+from tg.controllers import TGController
+from tg.decorators import expose
+from pylons.decorators.cache import beaker_cache
+from pylons.controllers.util import etag_cache
+from pylons import cache
+from routes import Mapper
+import pylons
+from routes.middleware import RoutesMiddleware
+from webob.exc import HTTPNotModified
+from tests.base import TestWSGIController, make_app, setup_session_dir, teardown_session_dir
+
+def setup():
+    setup_session_dir()
+    
+def teardown():
+    teardown_session_dir()
+
+# a variable used to represent state held outside the controllers
+mockdb = {}
+
+class MockTime:
+    
+    """ A very simple class to mock the time module. This lets us slide time
+    around to fake expiry in beaker.container. """
+    
+    mock_time = 0
+    
+    def time(self):
+        return self.mock_time
+    
+    def set_time(self, v):
+        self.mock_time = v
+
+mocktime = MockTime()
+import beaker.container
+beaker.container.time = mocktime
+
+class SimpleCachingController(TGController):
+    
+    """ Pylons supports a mechanism for arbitrary caches that can be allocated
+    within controllers. Each cache value has a creation function associated
+    with it that is called to retrieve it's results. """
+    
+    @expose()
+    def simple(self, a):
+        c = cache.get_cache("BasicTGController.index")
+        x = c.get_value(key=a, 
+                        createfunc=lambda: "cached %s" % a,
+                        type="memory",
+                        expiretime=3600)
+        return x
+    
+    def createfunc(self):
+        return "cached %s" % mockdb['expiry']
+    
+    @expose()
+    def expiry(self, a):
+        mockdb['expiry'] = a # inject a value into the context
+        c = cache.get_cache("BasicTGController.index")
+        x = c.get_value(key='test', 
+                        createfunc=self.createfunc,
+                        type="memory",
+                        expiretime=100)
+        return x
+
+class TestSimpleCaching(TestWSGIController):
+    def __init__(self, *args, **kargs):
+        TestWSGIController.__init__(self, *args, **kargs)
+        self.baseenviron = {}
+        self.app = make_app(SimpleCachingController, self.baseenviron)
+
+    def test_simple_cache(self):
+        """ test that caches get different results for different cache keys. """
+        resp = self.app.get('/simple/', params={'a':'foo'})
+        assert resp.body == 'cached foo'
+        resp = self.app.get('/simple/', params={'a':'bar'})
+        assert resp.body == 'cached bar'
+        resp = self.app.get('/simple/', params={'a':'baz'})
+        assert resp.body == 'cached baz'
+
+    def test_expiry(self):
+        """ test that values expire from a single cache key. """
+        mocktime.set_time(0)
+        resp = self.app.get('/expiry/', params={'a':'foo1'})
+        assert resp.body == 'cached foo1'
+        mocktime.set_time(1)
+        resp = self.app.get('/expiry/', params={'a':'foo2'})
+        assert resp.body == 'cached foo1'
+        mocktime.set_time(200) # wind clock past expiry
+        resp = self.app.get('/expiry/', params={'a':'foo2'})
+        assert resp.body == 'cached foo2'
+
+class DecoratorController(TGController):
+    
+    @beaker_cache(expire=100, type='memory')
+    @expose()
+    def simple(self):
+        return "cached %s" % mockdb['DecoratorController.simple']
+    
+class TestDecoratorCaching(TestWSGIController):
+    
+    """ Test that the decorators function. """
+    
+    def __init__(self, *args, **kargs):
+        TestWSGIController.__init__(self, *args, **kargs)
+        self.baseenviron = {}
+        self.app = make_app(DecoratorController, self.baseenviron)
+    
+    def test_simple(self):
+        """ Test expiry of cached results for decorated functions. """
+        mocktime.set_time(0)
+        mockdb['DecoratorController.simple'] = 'foo1'
+        resp = self.app.get('/simple/')
+        assert resp.body == 'cached foo1'
+        mocktime.set_time(1)
+        mockdb['DecoratorController.simple'] = 'foo2'
+        resp = self.app.get('/simple/')
+        assert resp.body == 'cached foo1'
+        mocktime.set_time(200)
+        mockdb['DecoratorController.simple'] = 'foo2'
+        resp = self.app.get('/simple/')
+        assert resp.body == 'cached foo2'
+
+class EtagController(TGController):
+
+    @expose()
+    def etagged(self, etag):
+        # this is needed because the default content type is overridden within the config
+        # etag_cache should probably do this, in order that the content-type header is popped
+        # if it is None
+        pylons.response.headers.pop('Content-Type')
+        etag_cache(etag)
+        return "bar"
+    
+class TestEtagCaching(TestWSGIController):
+    
+    """ A simple mechanism is provided to set the etag header for returned results. """
+    
+    def __init__(self, *args, **kargs):
+        TestWSGIController.__init__(self, *args, **kargs)
+        self.app = make_app(EtagController)
+
+    def test_etags(self):
+        """ Test that the etag in the response headers is the one we expect. """
+        resp = self.app.get('/etagged/', params={'etag':'foo'})
+        assert resp.etag == 'foo', resp.etag
+        resp = self.app.get('/etagged/', params={'etag':'bar'})
+        assert resp.etag == 'bar', resp.etag    
+        
+    def test_304(self):
+        resp = self.app.get('/etagged/', params={'etag':'foo'}, headers={'if-none-match': 'foo'})
+        assert "304" in resp.status, resp
+

tests/test_configuration.py

+"""
+Testing for TG2 Configuration
+"""
+from nose.tools import eq_, raises
+import atexit
+
+from tg.util import Bunch
+from tg.configuration import AppConfig, config
+from tests.base import TestWSGIController, make_app, setup_session_dir, teardown_session_dir, create_request
+
+
+
+def setup():
+    setup_session_dir()
+def teardown():
+    teardown_session_dir()
+
+class TestPylonsConfigWrapper:
+
+    def setup(self):
+        self.config = config
+
+    def test_create(self):
+        pass
+
+    def test_getitem(self):
+        expected_keys = ['global_conf', 'use_sqlalchemy', 'package', 'pylons.app_globals', 'call_on_shutdown']
+        for key in expected_keys:
+            self.config[key]
+
+    @raises(KeyError)
+    def test_getitem_bad(self):
+        self.config['no_such_key']
+
+    def test_setitem(self):
+        self.config['no_such_key'] = 'something'
+
+    def test_delattr(self):
+        del self.config.use_sqlalchemy
+        eq_(hasattr(self.config, 'use_sqlalchemy'), False)
+        self.config.use_sqlalchemy = True
+
+    @raises(AttributeError)
+    def test_delattr_bad(self):
+        del self.config.i_dont_exist
+
+class TestAppConfig:
+    def setup(self):
+        self.config = AppConfig()
+        # set up some required paths and config settings
+        # FIXME: these seem to be needed so that
+        # other tests don't suffer - but that's a nasty
+        # side-effect. setup for those tests actually needs
+        # fixing.
+        config['pylons.paths']['static_files'] = "test"
+        config["pylons.app_globals"] = Bunch()
+        config["use_sqlalchemy"] = False
+        config["global_conf"] = Bunch()
+        config["package"] = "test"
+        config["call_on_shutdown"] = "foo"
+        config["render_functions"] = Bunch()
+        config['beaker.session.secret'] = 'some_secret'
+
+    def test_create(self):
+        pass
+
+    def test_setup_startup_and_shutdown_startup_callable(self):
+        def func():
+            a = 7
+        self.config.call_on_startup = [func]
+        self.config.setup_startup_and_shutdown()
+
+    def test_setup_startup_and_shutdown_callable_startup_with_exception(self):
+        def func():
+            raise Exception
+        self.config.call_on_startup = [func]
+        self.config.setup_startup_and_shutdown()
+
+    def test_setup_startup_and_shutdown_startup_not_callable(self):
+        self.config.call_on_startup = ['not callable']
+        self.config.setup_startup_and_shutdown()
+
+    def test_setup_startup_and_shutdown_shutdown_not_callable(self):
+        self.config.call_on_shutdown = ['not callable']
+        self.config.setup_startup_and_shutdown()
+
+    def test_setup_startup_and_shutdown_shutdown_callable(self):
+        def func():
+            a = 7
+        self.config.call_on_shutdown = [func]
+        self.config.setup_startup_and_shutdown()
+        assert (func, (), {}) in atexit._exithandlers
+
+    #this tests fails
+    def _test_setup_helpers_and_globals(self):
+        self.config.setup_helpers_and_globals()
+
+    def test_setup_sa_auth_backend(self):
+        self.config.setup_sa_auth_backend()
+
+    def test_setup_chameleon_genshi_renderer(self):
+        self.config.paths.templates = 'template_path'
+        self.config.setup_chameleon_genshi_renderer()
+
+    def test_setup_genshi_renderer(self):
+        self.config.paths.templates = 'template_path'
+        self.config.setup_genshi_renderer()
+
+    def test_setup_jinja_renderer(self):
+        self.config.paths.templates = 'template_path'
+        self.config.setup_jinja_renderer()
+
+    def test_setup_mako_renderer(self):
+        self.config.paths.templates = ['template_path']
+        self.config.setup_mako_renderer(use_dotted_templatenames=True)
+    
+    def test_setup_sqlalchemy(self):
+        config['sqlalchemy.url'] = 'sqlite://'
+        class Package:
+            class model:
+                @classmethod
+                def init_model(package, engine):
+                    pass
+        self.config.package = Package()
+        self.config.setup_sqlalchemy()
+
+    def test_add_auth_middleware(self):
+        class Dummy:pass
+
+        self.config.sa_auth.dbsession = Dummy()
+        self.config.sa_auth.user_class = Dummy
+        self.config.sa_auth.group_class = Dummy
+        self.config.sa_auth.permission_class = Dummy
+        self.config.sa_auth.cookie_secret = 'dummy'
+        self.config.sa_auth.password_encryption_method = 'sha'
+
+        self.config.add_auth_middleware(None, None)
+
+    def test_add_static_file_middleware(self):
+        self.config.add_static_file_middleware(None)
+

tests/test_controllers.py

+# -*- coding: utf-8 -*-
+
+import pylons
+import tg
+from tg.controllers import *
+from tg.exceptions import HTTPFound
+from nose.tools import eq_
+from tests.base import TestWSGIController, make_app, setup_session_dir, teardown_session_dir, create_request
+from tg.util import no_warn
+
+def setup():
+    setup_session_dir()
+def teardown():
+    teardown_session_dir()
+
+def test_create_request():
+    environ = { 'SCRIPT_NAME' : '/xxx' }
+    request = create_request('/', environ)
+    eq_('http://localhost/xxx/hello', tg.request.relative_url('hello'))
+    eq_('http://localhost/xxx', tg.request.application_url)
+
+def test_approots():
+    create_request('/subthing/',{ 'SCRIPT_NAME' : '/subthing' })
+    eq_("foo", url("foo"))
+    eq_("/subthing/foo", url("/foo"))
+
+def test_lowerapproots():
+    create_request(
+                '/subthing/subsubthing/',
+                { 'SCRIPT_NAME' : '/subthing/subsubthing' }
+                )
+    eq_("/subthing/subsubthing/foo", url("/foo"))
+
+@no_warn
+def test_multi_values():
+    create_request('/')
+    r = url("/foo", bar=(u"asdf",u"qwer"))
+    assert r in \
+            ["/foo?bar=qwer&bar=asdf", "/foo?bar=asdf&bar=qwer"], r
+    r = url("/foo", bar=[1,2])
+    assert  r in \
+            ["/foo?bar=1&bar=2", "/foo?bar=2&bar=1"], r
+
+@no_warn
+def test_unicode():
+    """url() can handle unicode parameters"""
+    create_request("/")
+    unicodestring = (u'\N{LATIN SMALL LETTER A WITH GRAVE}'
+        u'\N{LATIN SMALL LETTER E WITH GRAVE}'
+        u'\N{LATIN SMALL LETTER I WITH GRAVE}'
+        u'\N{LATIN SMALL LETTER O WITH GRAVE}'
+        u'\N{LATIN SMALL LETTER U WITH GRAVE}')
+    eq_(url('/', x=unicodestring),
+        '/?x=%C3%A0%C3%A8%C3%AC%C3%B2%C3%B9'
+        )
+
+@no_warn
+def test_list():
+    """url() can handle list parameters, with unicode too"""
+    create_request("/")
+    value = url('/', foo=['bar', u'\N{LATIN SMALL LETTER A WITH GRAVE}']),
+    assert '/?foo=bar&foo=%C3%A0' in value, value
+
+@no_warn
+def test_url_kwargs_overwrite_tgparams():
+    params = {'spamm': 'eggs'}
+    result = url('/foo', params, spamm='ham')
+    assert 'spamm=ham' in result
+
+def test_url_with_params_key():
+    params = {'spamm': 'eggs'}
+    result = url('/foo', params=params)
+    assert 'spamm=eggs' in result
+
+@no_warn
+def test_url_strip_None():
+    params = {'spamm':'eggs', 'hamm':None }
+    result = url('/foo', params=params)
+    assert 'hamm' not in result, result
+
+@no_warn
+def test_url_doesnt_change_tgparams():
+    params = {'spamm': 'eggs'}
+    result = url('/foo', params, spamm='ham')
+    eq_(params['spamm'], 'eggs')
+
+#def test_approotsWithPath():
+#    create_request('/coolsite/root/subthing/', {'SCRIPT_NAME' : '/subthing'})
+#    pylons.config.update({"server.webpath":"/coolsite/root"})
+#    eq_("/coolsite/root/subthing/foo", pylons.url("/foo"))

tests/test_generic_json.py

+from tg.jsonify import jsonify, encode
+from simplejson import loads
+from datetime import date
+
+class Person(object):
+    def __init__(self, first_name, last_name):
+        self.first_name = first_name
+        self.last_name = last_name
+    
+    @property
+    def name(self):
+        return '%s %s' % (self.first_name, self.last_name)
+    
+    def __json__(self):
+        return dict(first_name=self.first_name, last_name=self.last_name)
+
+def test_simple_rule():    
+    # skip this test if simplegeneric is not installed
+    try:
+        import simplegeneric
+    except ImportError:
+        return
+    
+    # create a Person instance
+    p = Person('Jonathan', 'LaCour')
+    
+    # encode the object using the existing "default" rules
+    result = loads(encode(p))
+    assert result['first_name'] == 'Jonathan'
+    assert result['last_name'] == 'LaCour'
+    assert len(result) == 2
+    
+    # register a generic JSON rule
+    @jsonify.when_type(Person)
+    def jsonify_person(obj):
+        return dict(
+            name=obj.name
+        )
+    
+    # encode the object using our new rule
+    result = loads(encode(p))
+    assert result['name'] == 'Jonathan LaCour'
+    assert len(result) == 1
+
+def test_builtin_override():
+    # skip this test if simplegeneric is not installed
+    try:
+        import simplegeneric
+    except ImportError:
+        return
+    
+    # create a few date objects
+    d1 = date(1979, 10, 12)
+    d2 = date(2000, 1, 1)
+    d3 = date(2012, 1, 1)
+    
+    # jsonify using the built in rules
+    result1 = encode(dict(date=d1))
+    assert '"1979-10-12"' in result1
+    result2 = encode(dict(date=d2))
+    assert '"2000-01-01"' in result2
+    result3 = encode(dict(date=d3))
+    assert '"2012-01-01"' in result3
+    
+    # create a custom rule
+    @jsonify.when_type(date)
+    def jsonify_date(obj):
+        if obj.year == 1979 and obj.month == 10 and obj.day == 12:
+            return "Jon's Birthday!"
+        elif obj.year == 2000 and obj.month == 1 and obj.day == 1:
+            return "Its Y2K! Panic!"
+        return '%d/%d/%d' % (obj.month, obj.day, obj.year)
+    
+    # jsonify using the built in rules
+    result1 = encode(dict(date=d1))
+    assert '"Jon\'s Birthday!"' in result1
+    result2 = encode(dict(date=d2))
+    assert '"Its Y2K! Panic!"' in result2
+    result3 = encode(dict(date=d3))
+    assert  '"1/1/2012"' in result3

tests/test_jsonify.py

+from tg import jsonify
+from nose.tools import raises
+
+class Foo(object):
+    def __init__(self, bar):
+        self.bar = bar
+
+class Bar(object):
+    def __init__(self, bar):
+        self.bar = bar
+    def __json__(self):
+        return 'bar-%s' % self.bar
+
+class Baz(object):
+    pass
+
+def test_string():
+    d = "string"
+    encoded = jsonify.encode(d)
+    assert encoded == '"string"'
+
+@raises(jsonify.JsonEncodeError)
+def test_list():
+    d = ['a', 1, 'b', 2]
+    encoded = jsonify.encode(d)
+    assert encoded == '["a", 1, "b", 2]'
+
+@raises(jsonify.JsonEncodeError)
+def test_list_iter():
+    d = range(3)
+    encoded = jsonify.encode_iter(d)
+    assert ''.join(jsonify.encode_iter(d)) == jsonify.encode(d)
+
+def test_dictionary():
+    d = {'a': 1, 'b': 2}
+    encoded = jsonify.encode(d)
+    assert encoded == '{"a": 1, "b": 2}'
+
+@raises(jsonify.JsonEncodeError)
+def test_nospecificjson():
+    b = Baz()
+    try:
+        encoded = jsonify.encode(b)
+    except TypeError, e:
+        pass
+    assert  "is not JSON serializable" in e.message 
+
+def test_exlicitjson():
+    b = Bar("bq")
+    encoded = jsonify.encode(b)
+    assert encoded == '"bar-bq"'
+
+@raises(jsonify.JsonEncodeError)
+def test_exlicitjson_in_list():
+    b = Bar("bq")
+    d = [b]
+    encoded = jsonify.encode(d)
+    assert encoded == '["bar-bq"]'
+
+def test_exlicitjson_in_dict():
+    b = Bar("bq")
+    d = {"b": b}
+    encoded = jsonify.encode(d)
+    assert encoded == '{"b": "bar-bq"}'

tests/test_jsonify_sqlalchemy.py

+from nose.tools import raises
+from tg import jsonify
+
+try:
+    try:
+        import sqlite3
+    except:
+        import pysqlite2
+    from sqlalchemy import (MetaData, Table, Column, ForeignKey,
+        Integer, String)
+    from sqlalchemy.orm import create_session, mapper, relation
+
+    metadata = MetaData('sqlite:///:memory:')
+
+    test1 = Table('test1', metadata,
+        Column('id', Integer, primary_key=True),
+        Column('val', String(8)))
+
+    test2 = Table('test2', metadata,
+        Column('id', Integer, primary_key=True),
+        Column('test1id', Integer, ForeignKey('test1.id')),
+        Column('val', String(8)))
+
+    test3 = Table('test3', metadata,
+        Column('id', Integer, primary_key=True),
+        Column('val', String(8)))
+
+    test4 = Table('test4', metadata,
+        Column('id', Integer, primary_key=True),
+        Column('val', String(8)))
+
+    metadata.create_all()
+
+    class Test2(object):
+        pass
+    mapper(Test2, test2)
+
+    class Test1(object):
+        pass
+    mapper(Test1, test1, properties={'test2s': relation(Test2)})
+
+    class Test3(object):
+        def __json__(self):
+            return {'id': self.id, 'val': self.val, 'customized': True}
+
+    mapper(Test3, test3)
+
+    class Test4(object):
+        pass
+    mapper(Test4, test4)
+
+    test1.insert().execute({'id': 1, 'val': 'bob'})
+    test2.insert().execute({'id': 1, 'test1id': 1, 'val': 'fred'})
+    test2.insert().execute({'id': 2, 'test1id': 1, 'val': 'alice'})
+    test3.insert().execute({'id': 1, 'val': 'bob'})
+    test4.insert().execute({'id': 1, 'val': 'alberto'})
+
+except ImportError:
+    from warnings import warn
+    warn('SQLAlchemy or PySqlite not installed - cannot run these tests.')
+
+else:
+
+    def test_saobj():
+        s = create_session()
+        t = s.query(Test1).get(1)
+        encoded = jsonify.encode(t)
+        assert encoded == '{"id": 1, "val": "bob"}'
+
+    @raises(jsonify.JsonEncodeError)
+    def test_salist():
+        s = create_session()
+        t = s.query(Test1).get(1)
+        encoded = jsonify.encode(t.test2s)
+        assert encoded == '{rows: [{"test1id": 1, "id": 1, "val": "fred"},' \
+            ' {"test1id": 1, "id": 2, "val": "alice"}]', encoded
+        
+    @raises(jsonify.JsonEncodeError)
+    def test_select_row():
+        s = create_session()
+        t = test1.select().execute()
+        encoded = jsonify.encode(t)
+# this may be added back later on
+#        assert encoded == """{"count": -1, "rows": [{"count": 1, "rows": {"id": 1, "val": "bob"}}]}""", encoded
+
+    @raises(jsonify.JsonEncodeError)
+    def test_select_rows():
+        s = create_session()
+        t = test2.select().execute()
+        encoded = jsonify.encode(t)
+
+# this may be added back later
+#
+        assert encoded == '{"count": -1, "rows": [{"count": 1, "rows": {"test1id": 1, "id": 1, "val": "fred"}},\
+ {"count": 1, "rows": {"test1id": 1, "id": 2, "val": "alice"}}]}', encoded
+
+    def test_explicit_saobj():
+        s = create_session()
+        t = s.query(Test3).get(1)
+        encoded = jsonify.encode(t)
+        assert encoded == '{"id": 1, "val": "bob", "customized": true}'
+

tests/test_render.py

+"""
+Testing for TG2 Configuration
+"""
+from nose.tools import eq_, raises
+import atexit
+
+from tg.render import render, MissingRendererError
+from tests.base import TestWSGIController, make_app, setup_session_dir, teardown_session_dir, create_request
+
+def setup():
+    setup_session_dir()
+def teardown():
+    teardown_session_dir()
+
+@raises(MissingRendererError)
+def test_render_missing_renderer():
+    render({}, 'gensh')

tests/test_rest_controller_dispatch.py

+# -*- coding: utf-8 -*-
+
+import tg, pylons
+from tg.util import no_warn
+from tg.controllers import TGController, RestController
+from tg.controllers.dispatcher import DispatchState
+from tg.decorators import expose, validate, override_template
+from routes import Mapper
+from routes.middleware import RoutesMiddleware
+from formencode import validators
+from webob import Response, Request
+from nose.tools import raises
+
+from tests.base import TestWSGIController, make_app, setup_session_dir, \
+                          teardown_session_dir
+
+def setup():
+    setup_session_dir()
+def teardown():
+    teardown_session_dir()
+
+def wsgi_app(environ, start_response):
+    req = Request(environ)
+    if req.method == 'POST':
+        resp = Response(req.POST['data'])
+    else:
+        resp = Response("Hello from %s/%s"%(req.script_name, req.path_info))
+    return resp(environ, start_response)
+
+
+class LookupHelper:
+    def __init__(self, var):
+        self.var = var
+
+    @expose()
+    def index(self):
+        return self.var
+
+
+class LookupController(TGController):
+
+    @expose()
+    def _lookup(self, a, *args):
+        return LookupHelper(a), args
+
+class DeprecatedLookupController(TGController):
+    @expose()
+    def lookup(self, a, *args):
+        return LookupHelper(a), args
+
+class LookupAlwaysHelper:
+    """for testing _dispatch"""
+
+    def __init__(self, var):
+        self.var = var
+
+    def _setup_wsgiorg_routing_args(self, url_path, remainder, params):
+        pass
+
+    @expose()
+    def always(self, *args, **kwargs):
+        return 'always go here'
+
+    def _dispatch(self, state, remainder):
+        state.add_method(self.always, remainder)
+        return state
+
+class LookupAlwaysController(TGController):
+
+
+    @expose()
+    def _lookup(self, a, *args):
+        return LookupAlwaysHelper(a), args
+
+class SubController:
+
+    @expose()
+    def sub_method(self, arg):
+        return 'sub %s'%arg
+
+class CustomDispatchingSubController(TGController):
+
+    @expose()
+    def always(self, *args, **kwargs):
+        return 'always go here'
+
+    def _dispatch(self, state, remainder):
+        state.add_method(self.always, remainder)
+        return state
+
+class OptionalArgumentRestController(RestController):
+    @expose()
+    def get_one(self, optional=None):
+        return "SUBREST GET ONE"
+    @expose()
+    def put(self, optional=None):
+        return "SUBREST PUT"
+    @expose()
+    def post(self, optional=None):
+        return "SUBREST POST"
+    @expose()
+    def edit(self, optional=None):
+        return "SUBREST EDIT"
+    @expose()
+    def new(self, optional=None):
+        return "SUBREST NEW"
+    @expose()
+    def get_delete(self, optional=None):
+        return "SUBREST GET DELETE"
+    @expose()
+    def post_delete(self, optional=None):
+        return "SUBREST POST DELETE"
+
+class RequiredArgumentRestController(RestController):
+    @expose()
+    def get_one(self, something):
+        return "SUBREST GET ONE"
+    @expose()
+    def put(self, something):
+        return "SUBREST PUT"
+    @expose()
+    def post(self, something):
+        return "SUBREST POST"
+    @expose()
+    def edit(self, something):
+        return "SUBREST EDIT"
+    @expose()
+    def new(self):
+        return "SUBREST NEW"
+    @expose()
+    def get_delete(self, something):
+        return "SUBREST GET DELETE"
+    @expose()
+    def post_delete(self, something):
+        return "SUBREST POST DELETE"
+
+class VariableSubRestController(RestController):
+    @expose()
+    def get_one(self, *args):
+        return "SUBREST GET ONE"
+    @expose()
+    def put(self, *args):
+        return "SUBREST PUT"
+    @expose()
+    def edit(self, *args):
+        return "SUBREST EDIT"
+    @expose()
+    def new(self, *args):
+        return "SUBREST NEW"
+    @expose()
+    def get_delete(self, *args):
+        return "SUBREST GET DELETE"
+    @expose()
+    def post_delete(self, *args):
+        return "SUBREST POST DELETE"
+
+class SubRestController(RestController):
+    @expose()
+    def get_all(self):
+        return "SUBREST GET ALL"
+    @expose()
+    def get_one(self, id):
+        return "SUBREST GET ONE"
+    @expose()
+    def new(self):
+        return "SUBREST NEW"
+    @expose()
+    def edit(self, id):
+        return "SUBREST EDIT"
+    @expose()
+    def post(self):
+        return "SUBREST POST"
+    @expose()
+    def put(self, id):
+        return "SUBREST PUT"
+    @expose()
+    def fxn(self):
+        return "SUBREST FXN"
+    @expose()
+    def get_delete(self, id):
+        return "SUBREST GET DELETE"
+    @expose()
+    def post_delete(self, id):
+        return "SUBREST POST DELETE"
+
+class VariableRestController(RestController):
+    subrest = SubRestController()
+    vsubrest = VariableSubRestController()
+
+    @expose()
+    def get_all(self):
+        return "REST GET ALL"
+    @expose()
+    def get_one(self, *args):
+        return "REST GET ONE"
+    @expose()
+    def get_delete(self, *args):
+        return "REST GET DELETE"
+    @expose()
+    def post_delete(self, *args):
+        return "REST POST DELETE"
+
+class ExtraRestController(RestController):
+    @expose()
+    def get_all(self):
+        return "REST GET ALL"
+    @expose()
+    def get_one(self, id):
+        return "REST GET ONE"
+    @expose()
+    def get_delete(self, id):
+        return "REST GET DELETE"
+    @expose()
+    def post_delete(self, id):
+        return "REST POST DELETE"
+
+    class sub(TGController):
+        @expose()
+        def index(self):
+            return "REST SUB INDEX"
+
+    subrest = SubRestController()
+    optsubrest = OptionalArgumentRestController()
+    reqsubrest = RequiredArgumentRestController()
+
+    _custom_actions = ['archive']
+
+    @expose()
+    def post_archive(self):
+        return 'got to post archive'
+
+    @expose()
+    def get_archive(self):
+        return 'got to get archive'
+
+class BasicRestController(RestController):
+
+    @expose()
+    def get(self):
+        return "REST GET"
+    @expose()
+    def post(self):
+        return "REST POST"
+    @expose()
+    def put(self):
+        return "REST PUT"
+    @expose()
+    def delete(self):
+        return "REST DELETE"
+    @expose()
+    def new(self):
+        return "REST NEW"
+    @expose()
+    def edit(self, *args, **kw):
+        return "REST EDIT"
+    @expose()
+    def other(self):
+        return "REST OTHER"
+
+    _custom_actions = ['archive']
+
+    @expose()
+    def archive(self):
+        return 'got to archive'
+
+class EmptyRestController(RestController):
+    pass
+
+class BasicTGController(TGController):
+
+    sub = SubController()
+    custom_dispatch = CustomDispatchingSubController()
+    lookup = LookupController()
+    deprecated_lookup = LookupController()
+    lookup_dispatch = LookupAlwaysController()
+    rest  = BasicRestController()
+    rest2 = ExtraRestController()
+    rest3 = VariableRestController()
+    empty = EmptyRestController()
+
+    @expose()
+    def index(self, **kwargs):
+        return 'hello world'
+
+    @expose()
+    def default(self, *remainder):
+        return "Main Default Page called for url /%s"%list(remainder)
+
+    @expose()
+    def hello(self, name, silly=None):
+        return "Hello " + name
+
+class BasicTGControllerNoDefault(TGController):
+    @expose()
+    def index(self, **kwargs):
+        return 'hello world'
+
+class TestTGControllerRoot(TestWSGIController):
+    def __init__(self, *args, **kargs):
+        TestWSGIController.__init__(self, *args, **kargs)
+        self.app = make_app(BasicTGControllerNoDefault)
+
+    def test_root_default_dispatch(self):
+        resp = self.app.get('/i/am/not/a/sub/controller', status=404)
+
+class TestTGController(TestWSGIController):
+    def __init__(self, *args, **kargs):
+        TestWSGIController.__init__(self, *args, **kargs)
+        self.app = make_app(BasicTGController)
+
+    def test_lookup(self):
+        r = self.app.get('/lookup/EYE')
+        msg = 'EYE'
+        assert msg in r, r
+
+    def test_deprecated_lookup(self):
+        r = self.app.get('/deprecated_lookup/EYE')
+        msg = 'EYE'
+        assert msg in r, r
+
+    def test_lookup_with_dispatch(self):
+        r = self.app.get('/lookup_dispatch/EYE')
+        msg = 'always'
+        assert msg in r, r
+
+    def test_root_method_dispatch(self):
+        resp = self.app.get('/hello/Bob')
+        assert "Hello Bob" in resp, resp
+
+    def test_root_index_dispatch(self):
+        resp = self.app.get('/')
+        assert "hello world" in resp, resp
+
+    def test_no_sub_index_dispatch(self):
+        resp = self.app.get('/sub/')
+        assert "['sub']" in resp, resp
+
+    def test_root_default_dispatch(self):
+        resp = self.app.get('/i/am/not/a/sub/controller')
+        assert "['i', 'am', 'not', 'a', 'sub', 'controller']" in resp, resp
+
+    def test_default_dispatch_not_found_in_sub_controller(self):
+        resp = self.app.get('/sub/no/default/found')
+        assert "['sub', 'no', 'default', 'found']" in resp, resp
+
+    def test_root_method_dispatch_with_trailing_slash(self):
+        resp = self.app.get('/hello/Bob/')
+        assert "Hello Bob" in resp, resp
+
+    def test_sub_method_dispatch(self):
+        resp = self.app.get('/sub/sub_method/army of darkness')
+        assert "sub army" in resp, resp
+
+    def test_custom_dispatch(self):
+        resp = self.app.get('/custom_dispatch/army of darkness')
+        assert "always" in resp, resp
+
+class TestRestController(TestWSGIController):
+
+    def __init__(self, *args, **kargs):
+        TestWSGIController.__init__(self, *args, **kargs)
+        self.app = make_app(BasicTGController)
+
+    def test_post(self):
+        r = self.app.post('/rest/')
+        assert 'REST POST' in r, r
+
+    def _test_non_resty(self):
+        r = self.app.post('/rest/non_resty_thing')
+        assert 'non_resty' in r, r
+
+    def test_custom_action_simple_get(self):
+        r = self.app.get('/rest/archive')
+        assert 'got to archive' in r, r
+
+    def test_custom_action_simple_post(self):
+        r = self.app.post('/rest/archive')
+        assert 'got to archive' in r, r
+
+    def test_custom_action_simple_post_args(self):
+        r = self.app.post('/rest?_method=archive')
+        assert 'got to archive' in r, r
+
+    def test_custom_action_get(self):
+        r = self.app.get('/rest2/archive')
+        assert 'got to get archive' in r, r
+
+    def test_custom_action_post(self):
+        r = self.app.post('/rest2?_method=archive')
+        assert 'got to post archive' in r, r
+
+    def test_get(self):
+        r = self.app.get('/rest/')
+        assert 'REST GET' in r, r
+
+    def test_put(self):
+        r = self.app.put('/rest/')
+        assert 'REST PUT' in r, r
+
+    def test_put_post(self):
+        r = self.app.post('/rest?_method=PUT')
+        assert 'REST PUT' in r, r
+
+    def test_put_get(self):
+        r = self.app.get('/rest?_method=PUT', status=405)
+
+    def test_put_post(self):
+        r = self.app.post('/rest', params={'_method':'PUT'})
+        assert 'REST PUT' in r, r
+
+    def test_get_delete_bad(self):
+        r = self.app.get('/rest?_method=DELETE', status=405)
+
+    def test_delete(self):
+        r = self.app.delete('/rest/')
+        assert 'REST DELETE' in r, r
+
+    def test_post_delete(self):
+        r = self.app.post('/rest/', params={'_method':'DELETE'})
+        assert 'REST DELETE' in r, r
+
+    def test_get_all(self):
+        r = self.app.get('/rest2/')
+        assert 'REST GET ALL' in r, r
+
+    def test_get_one(self):
+        r = self.app.get('/rest2/1')
+        assert 'REST GET ONE' in r, r
+
+    def test_get_delete(self):
+        r = self.app.get('/rest2/1/delete')
+        assert 'REST GET DELETE' in r, r
+
+    def test_post_delete(self):
+        r = self.app.post('/rest2/1', params={'_method':'DELETE'})
+        assert 'REST POST DELETE' in r, r
+
+    def test_post_delete_var(self):
+        r = self.app.post('/rest3/a/b/c', params={'_method':'DELETE'})
+        assert 'REST POST DELETE' in r, r
+
+    def test_get_delete_var(self):
+        r = self.app.get('/rest3/a/b/c/delete')
+        assert 'REST GET DELETE' in r, r
+
+    def test_get_method(self):
+        r = self.app.get('/rest/other')
+        assert 'REST OTHER' in r, r
+
+    @no_warn
+    def test_get_sub_controller(self):
+        r = self.app.get('/rest2/sub')
+        assert 'REST SUB INDEX' in r, r
+
+    @no_warn
+    def test_put_sub_controller(self):
+        r = self.app.put('/rest2/sub')
+        assert 'REST SUB INDEX' in r, r
+
+    def test_post_sub_controller(self):
+        r = self.app.post('/rest2/sub')
+        assert 'REST SUB INDEX' in r, r
+
+    def test_post_miss(self):
+        r = self.app.post('/rest2/something')
+        assert "/['rest2', 'something']" in r, r
+
+    def test_get_empty(self):
+        r = self.app.get('/empty/')
+        assert "/['empty']" in r, r
+
+    def test_post_empty(self):
+        r = self.app.post('/empty/')
+        assert "/['empty']" in r, r
+
+    def test_put_empty(self):
+        r = self.app.put('/empty/')
+        assert "/['empty']" in r, r
+
+    @no_warn
+    def test_delete_empty(self):
+        r = self.app.delete('/empty/')
+        assert "/['empty']" in r, r
+
+    def test_put_miss(self):
+        r = self.app.put('/rest/something')
+        assert "/['rest', 'something']" in r, r
+
+    def test_delete_miss(self):
+        r = self.app.delete('/rest/something')
+        assert "/['rest', 'something']" in r, r
+
+    def test_get_miss(self):
+        r = self.app.get('/rest2/something/else')
+        assert "/['rest2', 'something', 'else']" in r, r
+
+    def test_post_method(self):
+        r = self.app.post('/rest/other')
+        assert 'REST OTHER' in r, r
+
+    def test_new_method(self):
+        r = self.app.post('/rest/new')
+        assert 'REST NEW' in r, r
+
+    def test_edit_method(self):
+        r = self.app.get('/rest/1/edit')
+        assert 'REST EDIT' in r, r
+
+    def test_delete_method(self):
+        r = self.app.delete('/rest/other', status=405)
+
+    def test_put_method(self):
+        r = self.app.put('/rest/other')
+        assert 'REST OTHER' in r, r
+
+    def test_sub_get_all_method(self):
+        r = self.app.get('/rest2/1/subrest')
+        assert 'SUBREST GET ALL' in r, r
+
+    def test_var_sub_get_all_method(self):
+        r = self.app.get('/rest3/1/3/3/subrest')
+        assert 'SUBREST GET ALL' in r, r
+        r = self.app.get('/rest3/1/3/subrest')
+        assert 'SUBREST GET ALL' in r, r
+        r = self.app.get('/rest3/subrest')
+        assert 'SUBREST GET ALL' in r, r
+
+    def test_var_sub_get_one_method(self):
+        r = self.app.get('/rest3/1/3/3/subrest/1')
+        assert 'SUBREST GET ONE' in r, r
+        r = self.app.get('/rest3/1/3/subrest/1')
+        assert 'SUBREST GET ONE' in r, r
+        r = self.app.get('/rest3/subrest/1')
+        assert 'SUBREST GET ONE' in r, r
+
+    def test_var_sub_edit_method(self):
+        r = self.app.get('/rest3/1/3/3/subrest/1/edit')
+        assert 'SUBREST EDIT' in r, r
+        r = self.app.get('/rest3/1/3/subrest/1/edit')
+        assert 'SUBREST EDIT' in r, r
+        r = self.app.get('/rest3/subrest/1/edit')
+        assert 'SUBREST EDIT' in r, r
+
+    def test_var_sub_edit_var_method(self):
+        r = self.app.get('/rest3/1/3/3/vsubrest/1/edit')
+        assert 'SUBREST EDIT' in r, r
+        r = self.app.get('/rest3/1/3/vsubrest/1/a/edit')
+        assert 'SUBREST EDIT' in r, r
+        r = self.app.get('/rest3/vsubrest/edit')
+        assert 'SUBREST EDIT' in r, r
+
+    def test_var_sub_edit_method(self):
+        r = self.app.get('/rest3/1/3/3/subrest/1/delete')
+        assert 'SUBREST GET DELETE' in r, r
+        r = self.app.get('/rest3/1/3/subrest/1/delete')
+        assert 'SUBREST GET DELETE' in r, r
+        r = self.app.get('/rest3/subrest/1/delete')
+        assert 'SUBREST GET DELETE' in r, r
+
+    def test_var_sub_edit_var_method(self):
+        r = self.app.get('/rest3/1/3/3/vsubrest/1/edit')
+        assert 'SUBREST EDIT' in r, r
+        r = self.app.get('/rest3/1/3/vsubrest/1/a/edit')
+        assert 'SUBREST EDIT' in r, r
+        r = self.app.get('/rest3/vsubrest/edit')
+        assert 'SUBREST EDIT' in r, r
+
+    def test_var_sub_new_method(self):
+        r = self.app.get('/rest3/1/3/3/subrest/new')
+        assert 'SUBREST NEW' in r, r
+        r = self.app.get('/rest3/1/3/subrest/new')
+        assert 'SUBREST NEW' in r, r
+        r = self.app.get('/rest3/subrest/new')
+        assert 'SUBREST NEW' in r, r
+
+    def test_var_sub_var_get_one_method(self):
+        r = self.app.get('/rest3/1/3/3/vsubrest/1')
+        assert 'SUBREST GET ONE' in r, r
+        r = self.app.get('/rest3/1/3/vsubrest/1/a')
+        assert 'SUBREST GET ONE' in r, r
+        r = self.app.get('/rest3/vsubrest/')
+        assert 'SUBREST GET ONE' in r, r
+
+    def test_var_sub_var_put_method(self):
+        r = self.app.put('/rest3/1/3/3/vsubrest/1')
+        assert 'SUBREST PUT' in r, r
+        r = self.app.put('/rest3/1/3/vsubrest/1/asdf')
+        assert 'SUBREST PUT' in r, r
+        r = self.app.put('/rest3/vsubrest/')
+        assert 'SUBREST PUT' in r, r
+
+    def test_var_sub_post_method(self):
+        r = self.app.post('/rest3/1/3/3/subrest/')
+        assert 'SUBREST POST' in r, r
+        r = self.app.post('/rest3/1/3/subrest/')
+        assert 'SUBREST POST' in r, r
+        r = self.app.post('/rest3/subrest/')
+        assert 'SUBREST POST' in r, r
+
+    def test_var_sub_put_method(self):
+        r = self.app.put('/rest3/1/3/3/subrest/1')
+        assert 'SUBREST PUT' in r, r
+        r = self.app.put('/rest3/1/3/subrest/1')
+        assert 'SUBREST PUT' in r, r
+        r = self.app.put('/rest3/subrest/1')
+        assert 'SUBREST PUT' in r, r
+
+    def test_var_sub_put_hack_method(self):
+        r = self.app.post('/rest3/1/3/3/subrest/1?_method=PUT')
+        assert 'SUBREST PUT' in r, r
+        r = self.app.post('/rest3/1/3/subrest/1?_method=PUT')
+        assert 'SUBREST PUT' in r, r
+        r = self.app.post('/rest3/subrest/1?_method=PUT')
+        assert 'SUBREST PUT' in r, r
+
+    def test_var_sub_var_delete_method(self):
+        r = self.app.delete('/rest3/1/3/3/vsubrest/1')
+        assert 'SUBREST POST DELETE' in r, r
+        r = self.app.delete('/rest3/1/3/vsubrest/1')
+        assert 'SUBREST POST DELETE' in r, r
+        r = self.app.delete('/rest3/vsubrest/')
+        assert 'SUBREST POST DELETE' in r, r
+
+    def test_var_sub_delete_var_hack_method(self):
+        r = self.app.post('/rest3/1/3/3/vsubrest/1?_method=DELETE')
+        assert 'SUBREST POST DELETE' in r, r
+        r = self.app.post('/rest3/1/3/vsubrest/1?_method=DELETE')
+        assert 'SUBREST POST DELETE' in r, r
+        r = self.app.post('/rest3/vsubrest?_method=DELETE')
+        assert 'SUBREST POST DELETE' in r, r
+
+    def test_var_sub_var_put_hack_method(self):
+        r = self.app.post('/rest3/1/3/3/vsubrest/1?_method=PUT')
+        assert 'SUBREST PUT' in r, r
+        r = self.app.post('/rest3/1/3/vsubrest/1/a?_method=PUT')
+        assert 'SUBREST PUT' in r, r
+        r = self.app.post('/rest3/vsubrest/?_method=PUT')
+        assert 'SUBREST PUT' in r, r
+
+    def test_var_sub_delete_hack_method(self):
+        r = self.app.post('/rest3/1/3/3/subrest/1?_method=DELETE')
+        assert 'SUBREST POST DELETE' in r, r
+        r = self.app.post('/rest3/1/3/subrest/1?_method=DELETE')
+        assert 'SUBREST POST DELETE' in r, r
+        r = self.app.post('/rest3/subrest/1?_method=DELETE')
+        assert 'SUBREST POST DELETE' in r, r
+
+    def test_var_sub_delete_method(self):
+        r = self.app.delete('/rest3/1/3/3/subrest/1')
+        assert 'SUBREST POST DELETE' in r, r
+        r = self.app.delete('/rest3/1/3/subrest/1')
+        assert 'SUBREST POST DELETE' in r, r
+
+    def test_sub_new(self):
+        r = self.app.get('/rest2/1/subrest/new')
+        assert 'SUBREST NEW' in r, r
+
+    def test_sub_edit(self):
+        r = self.app.get('/rest2/1/subrest/1/edit')
+        assert 'SUBREST EDIT' in r, r
+
+    def test_sub_post(self):
+        r = self.app.post('/rest2/1/subrest/')
+        assert 'SUBREST POST' in r, r
+
+    def test_sub_put(self):
+        r = self.app.put('/rest2/1/subrest/2')
+        assert 'SUBREST PUT' in r, r
+
+    def test_sub_post(self):
+        r = self.app.post('/rest2/1/subrest/')
+        assert 'SUBREST POST' in r, r
+
+    def test_sub_post_opt(self):
+        r = self.app.post('/rest2/1/optsubrest/1')
+        assert 'SUBREST POST' in r, r
+    def test_sub_put_opt(self):
+        r = self.app.put('/rest2/1/optsubrest/1')
+        assert 'SUBREST PUT' in r, r
+    def test_sub_put_opt_hack(self):
+        r = self.app.post('/rest2/1/optsubrest/1?_method=PUT')
+        assert 'SUBREST PUT' in r, r
+    def test_sub_delete_opt_hack(self):
+        r = self.app.post('/rest2/1/optsubrest/1?_method=DELETE')
+        assert 'SUBREST ' in r, r
+
+    def test_put_post_req(self):
+        r = self.app.post('/rest2/reqsubrest', params={'something':'required'})
+        assert 'SUBREST POST' in r, r
+
+    def test_sub_put_req(self):
+        r = self.app.post('/rest2/reqsubrest', params={'_method':'PUT', 'something':'required'})
+        assert 'SUBREST PUT' in r, r
+
+    def test_sub_post_req_bad(self):
+        r = self.app.post('/rest2/reqsubrest',)
+        assert "['rest2', 'reqsubrest']" in r, r
+
+    def test_sub_delete_hack(self):
+        r = self.app.post('/rest2/1/subrest/2?_method=DELETE')
+        assert 'SUBREST POST DELETE' in r, r
+
+    def test_sub_get_delete(self):
+        r = self.app.get('/rest2/1/subrest/2/delete')
+        assert 'SUBREST GET DELETE' in r, r
+
+    def test_sub_post_delete(self):
+        r = self.app.delete('/rest2/1/subrest/2')
+        assert 'SUBREST POST DELETE' in r, r
+
+    def test_sub_get_fxn(self):
+        r = self.app.get('/rest2/1/subrest/fxn')
+        assert 'SUBREST FXN' in r, r
+
+    def test_sub_post_fxn(self):
+        r = self.app.post('/rest2/1/subrest/fxn')
+        assert 'SUBREST FXN' in r, r

tests/test_stack/__init__.py

+import os
+from webtest import TestApp
+import tg
+import tests
+from tg.util import DottedFileNameFinder
+from tg.configuration import AppConfig
+
+class TestConfig(AppConfig):
+
+    def __init__(self, folder, values=None):
+        if values is None:
+            values = {}
+        AppConfig.__init__(self)
+        #First we setup some base values that we know will work
+        self.renderers = ['genshi', 'mako', 'chameleon_genshi', 'jinja','json']
+        self.render_functions = tg.util.Bunch()
+        self.package = tests.test_stack
+        self.default_renderer = 'genshi'
+        self.globals = self
+        self.helpers = {}
+        self.auth_backend = None
+        self.auto_reload_templates = False
+        self.use_legacy_renderer = False
+        self.use_dotted_templatenames = False
+        self.serve_static = False
+
+        root = os.path.dirname(os.path.dirname(tests.__file__))
+        test_base_path = os.path.join(root,'tests', 'test_stack',)
+        test_config_path = os.path.join(test_base_path, folder)
+        self.paths=tg.util.Bunch(
+                    root=test_base_path,
+                    controllers=os.path.join(test_config_path, 'controllers'),
+                    static_files=os.path.join(test_config_path, 'public'),
+                    templates=[os.path.join(test_config_path, 'templates')],
+                    i18n=os.path.join(test_config_path, 'i18n')
+                    )
+        #Then we overide those values with what was passed in
+        for key, value in values.items():
+            setattr(self, key, value)
+
+    def setup_helpers_and_globals(self):
+        tg.config['pylons.app_globals'] = self.globals
+        tg.config['pylons.h'] = self.helpers
+        g = tg.config['pylons.app_globals']
+        g.dotted_filename_finder = DottedFileNameFinder()
+
+def app_from_config(base_config, deployment_config=None):
+    if not deployment_config:
+        deployment_config = {'debug': 'true',
+                             'error_email_from': 'paste@localhost',
+                             'smtp_server': 'localhost'}
+
+    env_loader = base_config.make_load_environment()
+    app_maker = base_config.setup_tg_wsgi_app(env_loader)
+    app = TestApp(app_maker(deployment_config, full_stack=True))
+    return app
+
+
+
Add a comment to this file

tests/test_stack/config/__init__.py

Empty file added.

Add a comment to this file

tests/test_stack/config/controllers/__init__.py

Empty file added.

tests/test_stack/config/controllers/root.py

+"""Main Controller"""
+
+from tg import expose, redirect, config
+from tg.controllers import TGController
+
+class RootController(TGController):
+    @expose()
+    def index(self):
+        return "my foo"
+
+    @expose()
+    def config_test(self):
+        return str(config)
+    
+    @expose()
+    def config_attr_lookup(self):
+        return str(config.render_functions)
+    
+    @expose()
+    def config_dotted_values(self):
+        return str(config.pylons)
+    
+    @expose()
+    def config_attr_set(self, foo):
+        config.test_value = foo
+        return str(config.test_value)
+        
+    @expose()
+    def config_set_method(self):
+        return str(config.get('pylons'))
+
+    @expose()
+    def config_dict_set(self, foo):
+        config['test_value'] = foo
+        return str(config.test_value)
+        
+

tests/test_stack/config/test_config.py

+import os
+from tests.test_stack import TestConfig, app_from_config
+from webtest import TestApp
+
+def setup_noDB():
+    base_config = TestConfig(folder = 'config',
+                             values = {'use_sqlalchemy': False,
+                                       'pylons.tmpl_context_attach_args': False
+                                       }
+                             )
+    return app_from_config(base_config)
+
+def test_basic_stack():
+    app = setup_noDB()
+    resp = app.get('/')
+    assert resp.body == "my foo"
+
+def test_config_reading():
+    """Ensure that the config object can be read via dict and attr access"""
+    app = setup_noDB()
+    resp = app.get('/config_test')
+    assert "default_renderer" in resp.body
+    resp = app.get('/config_attr_lookup')
+    assert "genshi" in resp.body
+    resp = app.get('/config_dotted_values')
+    assert "environ_config" in resp.body
+
+def test_config_writing():
+    """Ensure that new values can be added to the config object"""
+    app = setup_noDB()
+    value = "gooberblue"
+    resp = app.get('/config_attr_set/'+value)
+    assert value in resp.body
+    resp = app.get('/config_dict_set/'+value)
+    assert value in resp.body
+
Add a comment to this file

tests/test_stack/dispatch/__init__.py

Empty file added.

Add a comment to this file

tests/test_stack/dispatch/controllers/__init__.py

Empty file added.

tests/test_stack/dispatch/controllers/root.py

+# -*- coding: utf-8 -*-
+
+import tg, pylons
+from tg.controllers import TGController, CUSTOM_CONTENT_TYPE
+from tg.decorators import expose, validate, https, variable_decode
+from formencode import validators
+
+from tg import expose, redirect, config
+from tg.controllers import TGController
+from nose.tools import eq_
+
+
+class SubController(object):
+    @expose()
+    def foo(self,):
+        return 'sub_foo'
+
+    @expose()
+    def index(self):
+        return 'sub index'
+
+    @expose()
+    def _default(self, *args):
+        return ("recieved the following args (from the url): %s" %list(args))
+
+    @expose()
+    def redirect_me(self, target, **kw):
+        tg.redirect(target, **kw)
+
+    @expose()
+    def redirect_sub(self):
+        tg.redirect('index')
+
+    @expose()
+    def redirect_list(self):
+        tg.redirect(["/sub2", "list"])
+
+    @expose()
+    def hello(self, name):
+        return "Why HELLO! " + name
+
+class LookupController(TGController):
+    @expose()
+    def findme(self, *args, **kw):
+        return 'got to lookup'
+
+class SubController2(object):
+    @expose()
+    def index(self):
+        tg.redirect('list')
+
+    @expose()
+    def list(self, **kw):
+        return "hello list"
+
+    @expose()
+    def lookup(self, *args):
+        lookup = LookupController()
+        return lookup, args
+
+class RootController(TGController):
+    @expose()
+    def index(self, **kwargs):
+        return 'hello world'
+
+    @expose()
+    def _default(self, remainder):
+        return "Main Default Page called for url /%s"%remainder
+
+    @expose()
+    def feed(self, feed=None):
+        return feed
+
+    sub = SubController()
+    sub2 = SubController2()
+
+    @expose()
+    def redirect_me(self, target, **kw):
+        tg.redirect(target, kw)
+
+    @expose()
+    def hello(self, name, silly=None):
+        return "Hello " + name
+
+    @expose()
+    def redirect_cookie(self, name):
+        pylons.response.set_cookie('name', name)
+        tg.redirect('/hello_cookie')
+
+    @expose()
+    def hello_cookie(self):
+        return "Hello " + pylons.request.cookies['name']
+
+    @expose()
+    def flash_redirect(self):
+        tg.flash("Wow, flash!")
+        tg.redirect("/flash_after_redirect")
+
+    @expose()
+    def bigflash_redirect(self):
+        tg.flash('x' * 5000)
+        tg.redirect('/flash_after_redirect')
+
+    @expose()
+    def flash_unicode(self):
+        tg.flash(u"Привет, мир!")
+        tg.redirect("/flash_after_redirect")
+
+    @expose()
+    def flash_after_redirect(self):
+        return tg.get_flash()
+
+    @expose()
+    def flash_status(self):
+        return tg.get_status()
+