diff options
author | matt <matt@xcolour.net> | 2013-01-28 11:32:18 -0500 |
---|---|---|
committer | matt <matt@xcolour.net> | 2013-01-28 11:32:18 -0500 |
commit | 1afcb52d73271bbbd78f885451aa1b0e78c09871 (patch) | |
tree | 9145840d6036fcbc0b6647c88f679a567fa8c54d /tests | |
download | paste-git-stringio.tar.gz |
Import StringIO so it can be used.stringio
Diffstat (limited to 'tests')
63 files changed, 3145 insertions, 0 deletions
diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..3e2e36a --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,7 @@ +import sys +import os + +sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) + +import pkg_resources +pkg_resources.require('Paste') diff --git a/tests/cgiapp_data/error.cgi b/tests/cgiapp_data/error.cgi new file mode 100755 index 0000000..5afc9c9 --- /dev/null +++ b/tests/cgiapp_data/error.cgi @@ -0,0 +1,3 @@ +#!/usr/bin/env python + +print 'hey you!' diff --git a/tests/cgiapp_data/form.cgi b/tests/cgiapp_data/form.cgi new file mode 100755 index 0000000..6d2e038 --- /dev/null +++ b/tests/cgiapp_data/form.cgi @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +import cgi + +print 'Content-type: text/plain' +print + +form = cgi.FieldStorage() + +print 'Filename:', form['up'].filename +print 'Name:', form['name'].value +print 'Content:', form['up'].file.read() diff --git a/tests/cgiapp_data/ok.cgi b/tests/cgiapp_data/ok.cgi new file mode 100755 index 0000000..8b8eb29 --- /dev/null +++ b/tests/cgiapp_data/ok.cgi @@ -0,0 +1,6 @@ +#!/usr/bin/env python + +print 'Content-type: text/html; charset=UTF-8' +print 'Status: 200 Okay' +print +print 'This is the body' diff --git a/tests/cgiapp_data/stderr.cgi b/tests/cgiapp_data/stderr.cgi new file mode 100755 index 0000000..89dae0a --- /dev/null +++ b/tests/cgiapp_data/stderr.cgi @@ -0,0 +1,8 @@ +#!/usr/bin/env python + +import sys +print 'Status: 500 Server Error' +print 'Content-type: text/html' +print +print 'There was an error' +print >> sys.stderr, 'some data on the error' diff --git a/tests/test_auth/__init__.py b/tests/test_auth/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/test_auth/__init__.py diff --git a/tests/test_auth/test_auth_cookie.py b/tests/test_auth/test_auth_cookie.py new file mode 100644 index 0000000..1cf50c2 --- /dev/null +++ b/tests/test_auth/test_auth_cookie.py @@ -0,0 +1,42 @@ +# (c) 2005 Clark C. Evans +# This module is part of the Python Paste Project and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from paste.auth import cookie +from paste.wsgilib import raw_interactive, dump_environ +from paste.response import header_value +from paste.httpexceptions import * +from Cookie import SimpleCookie +import urllib2, os + +def build(application,setenv, *args, **kwargs): + def setter(environ, start_response): + save = environ['paste.auth.cookie'].append + for (k,v) in setenv.items(): + save(k) + environ[k] = v + return application(environ, start_response) + return cookie.middleware(setter,*args,**kwargs) + +def test_noop(): + app = build(dump_environ,{}) + (status,headers,content,errors) = \ + raw_interactive(app) + assert not header_value(headers,'Set-Cookie') + +def test_basic(key='key', val='bingles'): + app = build(dump_environ,{key:val}) + (status,headers,content,errors) = \ + raw_interactive(app) + value = header_value(headers,'Set-Cookie') + assert "Path=/;" in value + assert "expires=" not in value + cookie = value.split(";")[0] + (status,headers,content,errors) = \ + raw_interactive(app,{'HTTP_COOKIE': cookie}) + assert ("%s: %s" % (key,val.replace("\n","\n "))) in content + +def test_roundtrip(): + roundtrip = str('').join(map(chr,xrange(256))) + test_basic(roundtrip,roundtrip) + diff --git a/tests/test_auth/test_auth_digest.py b/tests/test_auth/test_auth_digest.py new file mode 100644 index 0000000..4721db3 --- /dev/null +++ b/tests/test_auth/test_auth_digest.py @@ -0,0 +1,89 @@ +# (c) 2005 Clark C. Evans +# This module is part of the Python Paste Project and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php + +from paste.auth.digest import * +from paste.wsgilib import raw_interactive +from paste.response import header_value +from paste.httpexceptions import * +from paste.httpheaders import AUTHORIZATION, WWW_AUTHENTICATE, REMOTE_USER +import os + +def application(environ, start_response): + content = REMOTE_USER(environ) + start_response("200 OK",(('Content-Type', 'text/plain'), + ('Content-Length', len(content)))) + return content + +realm = "tag:clarkevans.com,2005:testing" + +def backwords(environ, realm, username): + """ dummy password hash, where user password is just reverse """ + password = list(username) + password.reverse() + password = "".join(password) + return digest_password(realm, username, password) + +application = AuthDigestHandler(application,realm,backwords) +application = HTTPExceptionHandler(application) + +def check(username, password, path="/"): + """ perform two-stage authentication to verify login """ + (status,headers,content,errors) = \ + raw_interactive(application,path, accept='text/html') + assert status.startswith("401") + challenge = WWW_AUTHENTICATE(headers) + response = AUTHORIZATION(username=username, password=password, + challenge=challenge, path=path) + assert "Digest" in response and username in response + (status,headers,content,errors) = \ + raw_interactive(application,path, + HTTP_AUTHORIZATION=response) + if status.startswith("200"): + return content + if status.startswith("401"): + return None + assert False, "Unexpected Status: %s" % status + +def test_digest(): + assert 'bing' == check("bing","gnib") + assert check("bing","bad") is None + +# +# The following code uses sockets to test the functionality, +# to enable use: +# +# $ TEST_SOCKET py.test +# + +if os.environ.get("TEST_SOCKET",""): + import urllib2 + from paste.debug.testserver import serve + server = serve(application) + + def authfetch(username,password,path="/",realm=realm): + server.accept(2) + import socket + socket.setdefaulttimeout(5) + uri = ("http://%s:%s" % server.server_address) + path + auth = urllib2.HTTPDigestAuthHandler() + auth.add_password(realm,uri,username,password) + opener = urllib2.build_opener(auth) + result = opener.open(uri) + return result.read() + + def test_success(): + assert "bing" == authfetch('bing','gnib') + + def test_failure(): + # urllib tries 5 more times before it gives up + server.accept(5) + try: + authfetch('bing','wrong') + assert False, "this should raise an exception" + except urllib2.HTTPError, e: + assert e.code == 401 + + def test_shutdown(): + server.stop() + diff --git a/tests/test_cgiapp.py b/tests/test_cgiapp.py new file mode 100644 index 0000000..2887271 --- /dev/null +++ b/tests/test_cgiapp.py @@ -0,0 +1,36 @@ +import os +import sys +from nose.tools import assert_raises +from paste.cgiapp import CGIApplication, CGIError +from paste.fixture import * + +data_dir = os.path.join(os.path.dirname(__file__), 'cgiapp_data') + +# these CGI scripts can't work on Windows or Jython +if sys.platform != 'win32' and not sys.platform.startswith('java'): + def test_ok(): + app = TestApp(CGIApplication({}, script='ok.cgi', path=[data_dir])) + res = app.get('') + assert res.header('content-type') == 'text/html; charset=UTF-8' + assert res.full_status == '200 Okay' + assert 'This is the body' in res + + def test_form(): + app = TestApp(CGIApplication({}, script='form.cgi', path=[data_dir])) + res = app.post('', params={'name': 'joe'}, + upload_files=[('up', 'file.txt', 'x'*10000)]) + assert 'file.txt' in res + assert 'joe' in res + assert 'x'*10000 in res + + def test_error(): + app = TestApp(CGIApplication({}, script='error.cgi', path=[data_dir])) + assert_raises(CGIError, app.get, '', status=500) + + def test_stderr(): + app = TestApp(CGIApplication({}, script='stderr.cgi', path=[data_dir])) + res = app.get('', expect_errors=True) + assert res.status == 500 + assert 'error' in res + assert 'some data' in res.errors + diff --git a/tests/test_cgitb_catcher.py b/tests/test_cgitb_catcher.py new file mode 100644 index 0000000..2b80d28 --- /dev/null +++ b/tests/test_cgitb_catcher.py @@ -0,0 +1,75 @@ +from paste.fixture import * +from paste.cgitb_catcher import CgitbMiddleware +from paste import lint +from test_exceptions.test_error_middleware import clear_middleware + +def do_request(app, expect_status=500): + app = lint.middleware(app) + app = CgitbMiddleware(app, {}, display=True) + app = clear_middleware(app) + testapp = TestApp(app) + res = testapp.get('', status=expect_status, + expect_errors=True) + return res + + +############################################################ +## Applications that raise exceptions +############################################################ + +def bad_app(): + "No argument list!" + return None + +def start_response_app(environ, start_response): + "raise error before start_response" + raise ValueError("hi") + +def after_start_response_app(environ, start_response): + start_response("200 OK", [('Content-type', 'text/plain')]) + raise ValueError('error2') + +def iter_app(environ, start_response): + start_response("200 OK", [('Content-type', 'text/plain')]) + return yielder(['this', ' is ', ' a', None]) + +def yielder(args): + for arg in args: + if arg is None: + raise ValueError("None raises error") + yield arg + +############################################################ +## Tests +############################################################ + +def test_makes_exception(): + res = do_request(bad_app) + print res + assert 'bad_app() takes no arguments (2 given' in res + assert 'iterator = application(environ, start_response_wrapper)' in res + assert 'lint.py' in res + assert 'cgitb_catcher.py' in res + +def test_start_res(): + res = do_request(start_response_app) + print res + assert 'ValueError: hi' in res + assert 'test_cgitb_catcher.py' in res + assert 'line 26, in start_response_app' in res + +def test_after_start(): + res = do_request(after_start_response_app, 200) + print res + assert 'ValueError: error2' in res + assert 'line 30' in res + +def test_iter_app(): + res = do_request(iter_app, 200) + print res + assert 'None raises error' in res + assert 'yielder' in res + + + + diff --git a/tests/test_config.py b/tests/test_config.py new file mode 100644 index 0000000..882f82c --- /dev/null +++ b/tests/test_config.py @@ -0,0 +1,63 @@ +# (c) 2007 Philip Jenvey; written for Paste (http://pythonpaste.org) +# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php +from nose.tools import assert_raises +from paste.config import CONFIG, ConfigMiddleware +from paste.fixture import TestApp + +test_key = 'test key' + +def app_with_config(environ, start_response): + start_response('200 OK', [('Content-type','text/plain')]) + return ['Variable is: %s\n' % CONFIG[test_key], + 'Variable is (in environ): %s' % environ['paste.config'][test_key]] + +class NestingAppWithConfig(object): + def __init__(self, app): + self.app = app + + def __call__(self, environ, start_response): + response = self.app(environ, start_response) + assert isinstance(response, list) + supplement = ['Nesting variable is: %s' % CONFIG[test_key], + 'Nesting variable is (in environ): %s' % \ + environ['paste.config'][test_key]] + response.extend(supplement) + return response + +def test_request_config(): + config = {test_key: 'test value'} + app = ConfigMiddleware(app_with_config, config) + res = TestApp(app).get('/') + assert 'Variable is: test value' in res + assert 'Variable is (in environ): test value' in res + +def test_request_config_multi(): + config = {test_key: 'test value'} + app = ConfigMiddleware(app_with_config, config) + config = {test_key: 'nesting value'} + app = ConfigMiddleware(NestingAppWithConfig(app), config) + res = TestApp(app).get('/') + assert 'Variable is: test value' in res + assert 'Variable is (in environ): test value' in res + assert 'Nesting variable is: nesting value' in res + print res + assert 'Nesting variable is (in environ): nesting value' in res + +def test_process_config(request_app=test_request_config): + process_config = {test_key: 'bar', 'process_var': 'foo'} + CONFIG.push_process_config(process_config) + + assert CONFIG[test_key] == 'bar' + assert CONFIG['process_var'] == 'foo' + + request_app() + + assert CONFIG[test_key] == 'bar' + assert CONFIG['process_var'] == 'foo' + CONFIG.pop_process_config() + + assert_raises(AttributeError, lambda: 'process_var' not in CONFIG) + assert_raises(IndexError, CONFIG.pop_process_config) + +def test_process_config_multi(): + test_process_config(test_request_config_multi) diff --git a/tests/test_doctests.py b/tests/test_doctests.py new file mode 100644 index 0000000..3db3857 --- /dev/null +++ b/tests/test_doctests.py @@ -0,0 +1,60 @@ +from paste.util import doctest24 as doctest +from paste.util.import_string import simple_import +import os + +filenames = [ + 'tests/test_template.txt', + ] + +modules = [ + 'paste.util.template', + 'paste.util.looper', + # This one opens up httpserver, which is bad: + #'paste.auth.cookie', + #'paste.auth.multi', + #'paste.auth.digest', + #'paste.auth.basic', + #'paste.auth.form', + #'paste.progress', + 'paste.exceptions.serial_number_generator', + 'paste.evalexception.evalcontext', + 'paste.util.dateinterval', + 'paste.util.quoting', + 'paste.wsgilib', + 'paste.url', + 'paste.request', + ] + +options = doctest.ELLIPSIS|doctest.REPORT_ONLY_FIRST_FAILURE + +def test_doctests(): + for filename in filenames: + filename = os.path.join( + os.path.dirname(os.path.dirname(__file__)), + filename) + yield do_doctest, filename + +def do_doctest(filename): + failure, total = doctest.testfile( + filename, module_relative=False, + optionflags=options) + assert not failure, "Failure in %r" % filename + +def test_doctest_mods(): + for module in modules: + yield do_doctest_mod, module + +def do_doctest_mod(module): + module = simple_import(module) + failure, total = doctest.testmod( + module, optionflags=options) + assert not failure, "Failure in %r" % module + +if __name__ == '__main__': + import sys + import doctest + args = sys.argv[1:] + if not args: + args = filenames + for filename in args: + doctest.testfile(filename, module_relative=False) diff --git a/tests/test_errordocument.py b/tests/test_errordocument.py new file mode 100644 index 0000000..24f3022 --- /dev/null +++ b/tests/test_errordocument.py @@ -0,0 +1,92 @@ +from paste.errordocument import forward +from paste.fixture import * +from paste.recursive import RecursiveMiddleware + +def simple_app(environ, start_response): + start_response("200 OK", [('Content-type', 'text/plain')]) + return ['requested page returned'] + +def not_found_app(environ, start_response): + start_response("404 Not found", [('Content-type', 'text/plain')]) + return ['requested page returned'] + +def test_ok(): + app = TestApp(simple_app) + res = app.get('') + assert res.header('content-type') == 'text/plain' + assert res.full_status == '200 OK' + assert 'requested page returned' in res + +def error_docs_app(environ, start_response): + if environ['PATH_INFO'] == '/not_found': + start_response("404 Not found", [('Content-type', 'text/plain')]) + return ['Not found'] + elif environ['PATH_INFO'] == '/error': + start_response("200 OK", [('Content-type', 'text/plain')]) + return ['Page not found'] + else: + return simple_app(environ, start_response) + +def test_error_docs_app(): + app = TestApp(error_docs_app) + res = app.get('') + assert res.header('content-type') == 'text/plain' + assert res.full_status == '200 OK' + assert 'requested page returned' in res + res = app.get('/error') + assert res.header('content-type') == 'text/plain' + assert res.full_status == '200 OK' + assert 'Page not found' in res + res = app.get('/not_found', status=404) + assert res.header('content-type') == 'text/plain' + assert res.full_status == '404 Not found' + assert 'Not found' in res + +def test_forward(): + app = forward(error_docs_app, codes={404:'/error'}) + app = TestApp(RecursiveMiddleware(app)) + res = app.get('') + assert res.header('content-type') == 'text/plain' + assert res.full_status == '200 OK' + assert 'requested page returned' in res + res = app.get('/error') + assert res.header('content-type') == 'text/plain' + assert res.full_status == '200 OK' + assert 'Page not found' in res + res = app.get('/not_found', status=404) + assert res.header('content-type') == 'text/plain' + assert res.full_status == '404 Not found' + # Note changed response + assert 'Page not found' in res + +def auth_required_app(environ, start_response): + start_response('401 Unauthorized', [('content-type', 'text/plain'), ('www-authenticate', 'Basic realm="Foo"')]) + return ['Sign in!'] + +def auth_docs_app(environ, start_response): + if environ['PATH_INFO'] == '/auth': + return auth_required_app(environ, start_response) + elif environ['PATH_INFO'] == '/auth_doc': + start_response("200 OK", [('Content-type', 'text/html')]) + return ['<html>Login!</html>'] + else: + return simple_app(environ, start_response) + +def test_auth_docs_app(): + wsgi_app = forward(auth_docs_app, codes={401: '/auth_doc'}) + app = TestApp(wsgi_app) + res = app.get('/auth_doc') + assert res.header('content-type') == 'text/html' + res = app.get('/auth', status=401) + assert res.header('content-type') == 'text/html' + assert res.header('www-authenticate') == 'Basic realm="Foo"' + assert res.body == '<html>Login!</html>' + +def test_bad_error(): + def app(environ, start_response): + start_response('404 Not Found', [('content-type', 'text/plain')]) + return ['not found'] + app = forward(app, {404: '/404.html'}) + app = TestApp(app) + resp = app.get('/test', expect_errors=True) + print resp diff --git a/tests/test_exceptions/__init__.py b/tests/test_exceptions/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/tests/test_exceptions/__init__.py @@ -0,0 +1 @@ +# diff --git a/tests/test_exceptions/test_error_middleware.py b/tests/test_exceptions/test_error_middleware.py new file mode 100644 index 0000000..a34de73 --- /dev/null +++ b/tests/test_exceptions/test_error_middleware.py @@ -0,0 +1,107 @@ +from paste.fixture import * +from paste.exceptions.errormiddleware import ErrorMiddleware +from paste import lint +from paste.util.quoting import strip_html +# +# For some strange reason, these 4 lines cannot be removed or the regression +# test breaks; is it counting the number of lines in the file somehow? +# + +def do_request(app, expect_status=500): + app = lint.middleware(app) + app = ErrorMiddleware(app, {}, debug=True) + app = clear_middleware(app) + testapp = TestApp(app) + res = testapp.get('', status=expect_status, + expect_errors=True) + return res + +def clear_middleware(app): + """ + The fixture sets paste.throw_errors, which suppresses exactly what + we want to test in this case. This wrapper also strips exc_info + on the *first* call to start_response (but not the second, or + subsequent calls. + """ + def clear_throw_errors(environ, start_response): + headers_sent = [] + def replacement(status, headers, exc_info=None): + if headers_sent: + return start_response(status, headers, exc_info) + headers_sent.append(True) + return start_response(status, headers) + if 'paste.throw_errors' in environ: + del environ['paste.throw_errors'] + return app(environ, replacement) + return clear_throw_errors + + +############################################################ +## Applications that raise exceptions +############################################################ + +def bad_app(): + "No argument list!" + return None + +def unicode_bad_app(environ, start_response): + raise ValueError(u"\u1000") + +def start_response_app(environ, start_response): + "raise error before start_response" + raise ValueError("hi") + +def after_start_response_app(environ, start_response): + start_response("200 OK", [('Content-type', 'text/plain')]) + raise ValueError('error2') + +def iter_app(environ, start_response): + start_response("200 OK", [('Content-type', 'text/plain')]) + return yielder(['this', ' is ', ' a', None]) + +def yielder(args): + for arg in args: + if arg is None: + raise ValueError("None raises error") + yield arg + +############################################################ +## Tests +############################################################ + +def test_makes_exception(): + res = do_request(bad_app) + assert '<html' in res + res = strip_html(str(res)) + #print res + assert 'bad_app() takes no arguments (2 given' in res + assert 'iterator = application(environ, start_response_wrapper)' in res + assert 'paste.lint' in res + assert 'paste.exceptions.errormiddleware' in res + +def test_unicode_exception(): + res = do_request(unicode_bad_app) + + +def test_start_res(): + res = do_request(start_response_app) + res = strip_html(str(res)) + assert 'ValueError: hi' in res + assert 'test_error_middleware' in res + assert ':52 in start_response_app' in res + +def test_after_start(): + res = do_request(after_start_response_app, 200) + res = strip_html(str(res)) + #print res + assert 'ValueError: error2' in res + +def test_iter_app(): + res = do_request(lint.middleware(iter_app), 200) + #print res + assert 'None raises error' in res + assert 'yielder' in res + + + + diff --git a/tests/test_exceptions/test_formatter.py b/tests/test_exceptions/test_formatter.py new file mode 100644 index 0000000..2fe4528 --- /dev/null +++ b/tests/test_exceptions/test_formatter.py @@ -0,0 +1,186 @@ +from paste.exceptions import formatter +from paste.exceptions import collector +from paste.util.quoting import strip_html +import sys +import os +import difflib +import re + +class Mock(object): + def __init__(self, **kw): + for name, value in kw.items(): + setattr(self, name, value) + +class Supplement(Mock): + + object = 'test_object' + source_url = 'http://whatever.com' + info = 'This is some supplemental information' + args = () + def getInfo(self): + return self.info + + def __call__(self, *args): + self.args = args + return self + +class BadSupplement(Supplement): + + def getInfo(self): + raise ValueError("This supplemental info is buggy") + +def call_error(sup): + 1 + 2 + __traceback_supplement__ = (sup, ()) + assert 0, "I am an error" + +def raise_error(sup='default'): + if sup == 'default': + sup = Supplement() + for i in range(10): + __traceback_info__ = i + if i == 5: + call_error(sup=sup) + +def hide(t, inner, *args, **kw): + __traceback_hide__ = t + return inner(*args, **kw) + +def pass_through(info, inner, *args, **kw): + """ + To add another frame to the call; detectable because + __tracback_info__ is set to `info` + """ + __traceback_info__ = info + return inner(*args, **kw) + +def format(type='html', **ops): + data = collector.collect_exception(*sys.exc_info()) + report = getattr(formatter, 'format_' + type)(data, **ops) + return report + +formats = ('text', 'html') + +def test_excersize(): + for f in formats: + try: + raise_error() + except: + format(f) + +def test_content(): + for f in formats: + try: + raise_error() + except: + result = format(f) + print result + assert 'test_object' in result + assert 'http://whatever.com' in result + assert 'This is some supplemental information' in result + assert 'raise_error' in result + assert 'call_error' in result + assert '5' in result + assert 'test_content' in result + else: + assert 0 + +def test_trim(): + current = os.path.abspath(os.getcwd()) + for f in formats: + try: + raise_error() + except: + result = format(f, trim_source_paths=[(current, '.')]) + assert current not in result + assert ('%stest_formatter.py' % os.sep) in result, ValueError(repr(result)) + else: + assert 0 + +def test_hide(): + for f in formats: + try: + hide(True, raise_error) + except: + result = format(f) + print result + assert 'in hide_inner' not in result + assert 'inner(*args, **kw)' not in result + else: + assert 0 + +def print_diff(s1, s2): + differ = difflib.Differ() + result = list(differ.compare(s1.splitlines(), s2.splitlines())) + print '\n'.join(result) + +def test_hide_supppressed(): + """ + When an error occurs and __traceback_stop__ is true for the + erroneous frame, then that setting should be ignored. + """ + for f in ['html']: #formats: + results = [] + for hide_value in (False, 'after'): + try: + pass_through( + 'a', + hide, + hide_value, + pass_through, + 'b', + raise_error) + except: + results.append(format(f)) + else: + assert 0 + if results[0] != results[1]: + print_diff(results[0], results[1]) + assert 0 + +def test_hide_after(): + for f in formats: + try: + pass_through( + 'AABB', + hide, 'after', + pass_through, 'CCDD', + # A little whitespace to keep this line out of the + # content part of the report + + + hide, 'reset', + raise_error) + except: + result = format(f) + print strip_html(result).encode('ascii', 'replace') + assert 'AABB' in result + assert 'CCDD' not in result + assert 'raise_error' in result + else: + assert 0 + +def test_hide_before(): + for f in formats: + try: + pass_through( + 'AABB', + hide, 'before', + raise_error) + except: + result = format(f) + print result + assert 'AABB' not in result + assert 'raise_error' in result + else: + assert 0 + +def test_make_wrappable(): + assert '<wbr>' in formatter.make_wrappable('x'*1000) + # I'm just going to test that this doesn't excede the stack limit: + formatter.make_wrappable(';'*2000) + assert (formatter.make_wrappable('this that the other') + == 'this that the other') + assert (formatter.make_wrappable('this that ' + ('x'*50) + ';' + ('y'*50) + ' and the other') + == 'this that '+('x'*50) + ';<wbr>' + ('y'*50) + ' and the other') + diff --git a/tests/test_exceptions/test_httpexceptions.py b/tests/test_exceptions/test_httpexceptions.py new file mode 100644 index 0000000..c833eae --- /dev/null +++ b/tests/test_exceptions/test_httpexceptions.py @@ -0,0 +1,98 @@ +# (c) 2005 Ian Bicking, Clark C. Evans and contributors +# This module is part of the Python Paste Project and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +""" +WSGI Exception Middleware + +Regression Test Suite +""" +from nose.tools import assert_raises +from paste.httpexceptions import * +from paste.wsgilib import raw_interactive +from paste.response import header_value + + +def test_HTTPMove(): + """ make sure that location is a mandatory attribute of Redirects """ + assert_raises(AssertionError, HTTPFound) + assert_raises(AssertionError, HTTPTemporaryRedirect, + headers=[('l0cation','/bing')]) + assert isinstance(HTTPMovedPermanently("This is a message", + headers=[('Location','/bing')]) + ,HTTPRedirection) + assert isinstance(HTTPUseProxy(headers=[('LOCATION','/bing')]) + ,HTTPRedirection) + assert isinstance(HTTPFound('/foobar'),HTTPRedirection) + +def test_badapp(): + """ verify that the middleware handles previously-started responses """ + def badapp(environ, start_response): + start_response("200 OK",[]) + raise HTTPBadRequest("Do not do this at home.") + newapp = HTTPExceptionHandler(badapp) + assert 'Bad Request' in ''.join(newapp({'HTTP_ACCEPT': 'text/html'}, + (lambda a, b, c=None: None))) + +def test_unicode(): + """ verify unicode output """ + tstr = u"\0xCAFE" + def badapp(environ, start_response): + start_response("200 OK",[]) + raise HTTPBadRequest(tstr) + newapp = HTTPExceptionHandler(badapp) + assert tstr.encode("utf-8") in ''.join(newapp({'HTTP_ACCEPT': + 'text/html'}, + (lambda a, b, c=None: None))) + assert tstr.encode("utf-8") in ''.join(newapp({'HTTP_ACCEPT': + 'text/plain'}, + (lambda a, b, c=None: None))) + +def test_template(): + """ verify that html() and plain() output methods work """ + e = HTTPInternalServerError() + e.template = 'A %(ping)s and <b>%(pong)s</b> message.' + assert str(e).startswith("500 Internal Server Error") + assert e.plain({'ping': 'fun', 'pong': 'happy'}) == ( + '500 Internal Server Error\r\n' + 'A fun and happy message.\r\n') + assert '<p>A fun and <b>happy</b> message.</p>' in \ + e.html({'ping': 'fun', 'pong': 'happy'}) + +def test_redapp(): + """ check that redirect returns the correct, expected results """ + saved = [] + def saveit(status, headers, exc_info = None): + saved.append((status,headers)) + def redapp(environ, start_response): + raise HTTPFound("/bing/foo") + app = HTTPExceptionHandler(redapp) + result = list(app({'HTTP_ACCEPT': 'text/html'},saveit)) + assert '<a href="/bing/foo">' in result[0] + assert "302 Found" == saved[0][0] + assert "text/html" == header_value(saved[0][1], 'content-type') + assert "/bing/foo" == header_value(saved[0][1],'location') + result = list(app({'HTTP_ACCEPT': 'text/plain'},saveit)) + print result[0] == ( + '302 Found\n' + 'This resource was found at /bing/foo;\n' + 'you should be redirected automatically.\n') + assert "text/plain; charset=utf8" == header_value(saved[1][1],'content-type') + assert "/bing/foo" == header_value(saved[1][1],'location') + +def test_misc(): + assert get_exception(301) == HTTPMovedPermanently + redirect = HTTPFound("/some/path") + assert isinstance(redirect,HTTPException) + assert isinstance(redirect,HTTPRedirection) + assert not isinstance(redirect,HTTPError) + notfound = HTTPNotFound() + assert isinstance(notfound,HTTPException) + assert isinstance(notfound,HTTPError) + assert isinstance(notfound,HTTPClientError) + assert not isinstance(notfound,HTTPServerError) + notimpl = HTTPNotImplemented() + assert isinstance(notimpl,HTTPException) + assert isinstance(notimpl,HTTPError) + assert isinstance(notimpl,HTTPServerError) + assert not isinstance(notimpl,HTTPClientError) + diff --git a/tests/test_exceptions/test_reporter.py b/tests/test_exceptions/test_reporter.py new file mode 100644 index 0000000..8f4dc7a --- /dev/null +++ b/tests/test_exceptions/test_reporter.py @@ -0,0 +1,50 @@ +import sys +import os +from paste.exceptions.reporter import * +from paste.exceptions import collector + +def setup_file(fn, content=None): + dir = os.path.join(os.path.dirname(__file__), 'reporter_output') + fn = os.path.join(dir, fn) + if os.path.exists(dir): + if os.path.exists(fn): + os.unlink(fn) + else: + os.mkdir(dir) + if content is not None: + f = open(fn, 'wb') + f.write(content) + f.close() + return fn + +def test_logger(): + fn = setup_file('test_logger.log') + rep = LogReporter( + filename=fn, + show_hidden_frames=False) + try: + int('a') + except: + exc_data = collector.collect_exception(*sys.exc_info()) + else: + assert 0 + rep.report(exc_data) + content = open(fn).read() + assert len(content.splitlines()) == 4, len(content.splitlines()) + assert 'ValueError' in content + assert 'int' in content + assert 'test_reporter.py' in content + assert 'test_logger' in content + + try: + 1 / 0 + except: + exc_data = collector.collect_exception(*sys.exc_info()) + else: + assert 0 + rep.report(exc_data) + content = open(fn).read() + print content + assert len(content.splitlines()) == 8 + assert 'ZeroDivisionError' in content + diff --git a/tests/test_fileapp.py b/tests/test_fileapp.py new file mode 100644 index 0000000..edb4e4c --- /dev/null +++ b/tests/test_fileapp.py @@ -0,0 +1,234 @@ +# (c) 2005 Ian Bicking, Clark C. Evans and contributors +# This module is part of the Python Paste Project and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +from paste.fileapp import * +from paste.fixture import * +from rfc822 import parsedate_tz, mktime_tz +import time, string + +def test_data(): + harness = TestApp(DataApp('mycontent')) + res = harness.get("/") + assert 'application/octet-stream' == res.header('content-type') + assert '9' == res.header('content-length') + assert "<Response 200 OK 'mycontent'>" == repr(res) + harness.app.set_content("bingles") + assert "<Response 200 OK 'bingles'>" == repr(harness.get("/")) + +def test_cache(): + def build(*args,**kwargs): + app = DataApp("SomeContent") + app.cache_control(*args,**kwargs) + return TestApp(app).get("/") + res = build() + assert 'public' == res.header('cache-control') + assert not res.header('expires',None) + res = build(private=True) + assert 'private' == res.header('cache-control') + assert mktime_tz(parsedate_tz(res.header('expires'))) < time.time() + res = build(no_cache=True) + assert 'no-cache' == res.header('cache-control') + assert mktime_tz(parsedate_tz(res.header('expires'))) < time.time() + res = build(max_age=60,s_maxage=30) + assert 'public, max-age=60, s-maxage=30' == res.header('cache-control') + expires = mktime_tz(parsedate_tz(res.header('expires'))) + assert expires > time.time()+58 and expires < time.time()+61 + res = build(private=True, max_age=60, no_transform=True, no_store=True) + assert 'private, no-store, no-transform, max-age=60' == \ + res.header('cache-control') + expires = mktime_tz(parsedate_tz(res.header('expires'))) + assert mktime_tz(parsedate_tz(res.header('expires'))) < time.time() + +def test_disposition(): + def build(*args,**kwargs): + app = DataApp("SomeContent") + app.content_disposition(*args,**kwargs) + return TestApp(app).get("/") + res = build() + assert 'attachment' == res.header('content-disposition') + assert 'application/octet-stream' == res.header('content-type') + res = build(filename="bing.txt") + assert 'attachment; filename="bing.txt"' == \ + res.header('content-disposition') + assert 'text/plain' == res.header('content-type') + res = build(inline=True) + assert 'inline' == res.header('content-disposition') + assert 'application/octet-stream' == res.header('content-type') + res = build(inline=True, filename="/some/path/bing.txt") + assert 'inline; filename="bing.txt"' == \ + res.header('content-disposition') + assert 'text/plain' == res.header('content-type') + try: + res = build(inline=True,attachment=True) + except AssertionError: + pass + else: + assert False, "should be an exception" + +def test_modified(): + harness = TestApp(DataApp('mycontent')) + res = harness.get("/") + assert "<Response 200 OK 'mycontent'>" == repr(res) + last_modified = res.header('last-modified') + res = harness.get("/",headers={'if-modified-since': last_modified}) + assert "<Response 304 Not Modified ''>" == repr(res) + res = harness.get("/",headers={'if-modified-since': last_modified + \ + '; length=1506'}) + assert "<Response 304 Not Modified ''>" == repr(res) + res = harness.get("/",status=400, + headers={'if-modified-since': 'garbage'}) + assert 400 == res.status and "ill-formed timestamp" in res.body + res = harness.get("/",status=400, + headers={'if-modified-since': + 'Thu, 22 Dec 2030 01:01:01 GMT'}) + assert 400 == res.status and "check your system clock" in res.body + +def test_file(): + import random, string, os + tempfile = "test_fileapp.%s.txt" % (random.random()) + content = string.letters * 20 + file = open(tempfile,"w") + file.write(content) + file.close() + try: + from paste import fileapp + app = fileapp.FileApp(tempfile) + res = TestApp(app).get("/") + assert len(content) == int(res.header('content-length')) + assert 'text/plain' == res.header('content-type') + assert content == res.body + assert content == app.content # this is cashed + lastmod = res.header('last-modified') + print "updating", tempfile + file = open(tempfile,"a+") + file.write("0123456789") + file.close() + res = TestApp(app).get("/",headers={'Cache-Control': 'max-age=0'}) + assert len(content)+10 == int(res.header('content-length')) + assert 'text/plain' == res.header('content-type') + assert content + "0123456789" == res.body + assert app.content # we are still cached + file = open(tempfile,"a+") + file.write("X" * fileapp.CACHE_SIZE) # exceed the cashe size + file.write("YZ") + file.close() + res = TestApp(app).get("/",headers={'Cache-Control': 'max-age=0'}) + newsize = fileapp.CACHE_SIZE + len(content)+12 + assert newsize == int(res.header('content-length')) + assert newsize == len(res.body) + assert res.body.startswith(content) and res.body.endswith('XYZ') + assert not app.content # we are no longer cached + finally: + import os + os.unlink(tempfile) + +def test_dir(): + import os + import tempfile + tmpdir = tempfile.mkdtemp() + try: + tmpfile = os.path.join(tmpdir, 'file') + tmpsubdir = os.path.join(tmpdir, 'dir') + fp = open(tmpfile, 'w') + fp.write('abcd') + fp.close() + os.mkdir(tmpsubdir) + try: + from paste import fileapp + app = fileapp.DirectoryApp(tmpdir) + for path in ['/', '', '//', '/..', '/.', '/../..']: + assert TestApp(app).get(path, status=403).status == 403, ValueError(path) + for path in ['/~', '/foo', '/dir', '/dir/']: + assert TestApp(app).get(path, status=404).status == 404, ValueError(path) + assert TestApp(app).get('/file').body == 'abcd' + finally: + os.remove(tmpfile) + os.rmdir(tmpsubdir) + finally: + os.rmdir(tmpdir) + +def _excercize_range(build,content): + # full content request, but using ranges' + res = build("bytes=0-%d" % (len(content)-1)) + assert res.header('accept-ranges') == 'bytes' + assert res.body == content + assert res.header('content-length') == str(len(content)) + res = build("bytes=-%d" % (len(content)-1)) + assert res.body == content + assert res.header('content-length') == str(len(content)) + res = build("bytes=0-") + assert res.body == content + assert res.header('content-length') == str(len(content)) + # partial content requests + res = build("bytes=0-9", status=206) + assert res.body == content[:10] + assert res.header('content-length') == '10' + res = build("bytes=%d-" % (len(content)-1), status=206) + assert res.body == 'Z' + assert res.header('content-length') == '1' + res = build("bytes=%d-%d" % (3,17), status=206) + assert res.body == content[3:18] + assert res.header('content-length') == '15' + +def test_range(): + content = string.letters * 5 + def build(range, status=200): + app = DataApp(content) + return TestApp(app).get("/",headers={'Range': range}, status=status) + _excercize_range(build,content) + build('bytes=0-%d' % (len(content)+1), 416) + +def test_file_range(): + from paste import fileapp + import random, string, os + tempfile = "test_fileapp.%s.txt" % (random.random()) + content = string.letters * (1+(fileapp.CACHE_SIZE / len(string.letters))) + assert len(content) > fileapp.CACHE_SIZE + file = open(tempfile,"w") + file.write(content) + file.close() + try: + def build(range, status=200): + app = fileapp.FileApp(tempfile) + return TestApp(app).get("/",headers={'Range': range}, + status=status) + _excercize_range(build,content) + for size in (13,len(string.letters),len(string.letters)-1): + fileapp.BLOCK_SIZE = size + _excercize_range(build,content) + finally: + import os + os.unlink(tempfile) + +def test_file_cache(): + from paste import fileapp + filename = os.path.join(os.path.dirname(__file__), + 'urlparser_data', 'secured.txt') + app = TestApp(fileapp.FileApp(filename)) + res = app.get('/') + etag = res.header('ETag') + last_mod = res.header('Last-Modified') + res = app.get('/', headers={'If-Modified-Since': last_mod}, + status=304) + res = app.get('/', headers={'If-None-Match': etag}, + status=304) + res = app.get('/', headers={'If-None-Match': 'asdf'}, + status=200) + res = app.get('/', headers={'If-Modified-Since': 'Sat, 1 Jan 2005 12:00:00 GMT'}, + status=200) + res = app.get('/', headers={'If-Modified-Since': last_mod + '; length=100'}, + status=304) + res = app.get('/', headers={'If-Modified-Since': 'invalid date'}, + status=400) + +def test_methods(): + from paste import fileapp + filename = os.path.join(os.path.dirname(__file__), + 'urlparser_data', 'secured.txt') + app = TestApp(fileapp.FileApp(filename)) + get_res = app.get('') + res = app.get('', extra_environ={'REQUEST_METHOD': 'HEAD'}) + assert res.headers == get_res.headers + assert not res.body + app.post('', status=405) # Method Not Allowed + diff --git a/tests/test_fixture.py b/tests/test_fixture.py new file mode 100644 index 0000000..ba56488 --- /dev/null +++ b/tests/test_fixture.py @@ -0,0 +1,28 @@ +from paste.debug.debugapp import SimpleApplication +from paste.fixture import TestApp + +def test_fixture(): + app = TestApp(SimpleApplication()) + res = app.get('/', params={'a': ['1', '2']}) + assert (res.request.environ['QUERY_STRING'] == + 'a=1&a=2') + res = app.put('/') + assert (res.request.environ['REQUEST_METHOD'] == + 'PUT') + res = app.delete('/') + assert (res.request.environ['REQUEST_METHOD'] == + 'DELETE') + class FakeDict(object): + def items(self): + return [('a', '10'), ('a', '20')] + res = app.post('/params', params=FakeDict()) + + # test multiple cookies in one request + app.cookies['one'] = 'first'; + app.cookies['two'] = 'second'; + app.cookies['three'] = ''; + res = app.get('/') + hc = res.request.environ['HTTP_COOKIE'].split('; '); + assert ('one=first' in hc) + assert ('two=second' in hc) + assert ('three=' in hc) diff --git a/tests/test_grantip.py b/tests/test_grantip.py new file mode 100644 index 0000000..8d74280 --- /dev/null +++ b/tests/test_grantip.py @@ -0,0 +1,34 @@ +from paste.auth import grantip +from paste.fixture import * + +def test_make_app(): + def application(environ, start_response): + start_response('200 OK', [('content-type', 'text/plain')]) + return [ + str(environ.get('REMOTE_USER')), + ':', + str(environ.get('REMOTE_USER_TOKENS')), + ] + ip_map = { + '127.0.0.1': (None, 'system'), + '192.168.0.0/16': (None, 'worker'), + '192.168.0.5<->192.168.0.8': ('bob', 'editor'), + '192.168.0.8': ('__remove__', '-worker'), + } + app = grantip.GrantIPMiddleware(application, ip_map) + app = TestApp(app) + return app + +def test_req(): + app = test_make_app() + def doit(remote_addr): + res = app.get('/', extra_environ={'REMOTE_ADDR': remote_addr}) + return res.body + assert doit('127.0.0.1') == 'None:system' + assert doit('192.168.15.12') == 'None:worker' + assert doit('192.168.0.4') == 'None:worker' + result = doit('192.168.0.5') + assert result.startswith('bob:') + assert 'editor' in result and 'worker' in result + assert result.count(',') == 1 + assert doit('192.168.0.8') == 'None:editor' diff --git a/tests/test_gzipper.py b/tests/test_gzipper.py new file mode 100644 index 0000000..0832700 --- /dev/null +++ b/tests/test_gzipper.py @@ -0,0 +1,18 @@ +from paste.fixture import TestApp +from paste.gzipper import middleware +import gzip, cStringIO + +def simple_app(environ, start_response): + start_response('200 OK', [('content-type', 'text/plain')]) + return 'this is a test' + +wsgi_app = middleware(simple_app) +app = TestApp(wsgi_app) + +def test_gzip(): + res = app.get( + '/', extra_environ=dict(HTTP_ACCEPT_ENCODING='gzip')) + assert int(res.header('content-length')) == len(res.body) + assert res.body != 'this is a test' + actual = gzip.GzipFile(fileobj=cStringIO.StringIO(res.body)).read() + assert actual == 'this is a test' diff --git a/tests/test_httpheaders.py b/tests/test_httpheaders.py new file mode 100644 index 0000000..8c560d2 --- /dev/null +++ b/tests/test_httpheaders.py @@ -0,0 +1,159 @@ +from paste.httpheaders import * +import time + +def _test_generic(collection): + assert 'bing' == VIA(collection) + REFERER.update(collection,'internal:/some/path') + assert 'internal:/some/path' == REFERER(collection) + CACHE_CONTROL.update(collection,max_age=1234) + CONTENT_DISPOSITION.update(collection,filename="bingles.txt") + PRAGMA.update(collection,"test","multi",'valued="items"') + assert 'public, max-age=1234' == CACHE_CONTROL(collection) + assert 'attachment; filename="bingles.txt"' == \ + CONTENT_DISPOSITION(collection) + assert 'test, multi, valued="items"' == PRAGMA(collection) + VIA.delete(collection) + + +def test_environ(): + collection = {'HTTP_VIA':'bing', 'wsgi.version': '1.0' } + _test_generic(collection) + assert collection == {'wsgi.version': '1.0', + 'HTTP_PRAGMA': 'test, multi, valued="items"', + 'HTTP_REFERER': 'internal:/some/path', + 'HTTP_CONTENT_DISPOSITION': 'attachment; filename="bingles.txt"', + 'HTTP_CACHE_CONTROL': 'public, max-age=1234' + } + +def test_environ_cgi(): + environ = {'CONTENT_TYPE': 'text/plain', 'wsgi.version': '1.0', + 'HTTP_CONTENT_TYPE': 'ignored/invalid', + 'CONTENT_LENGTH': '200'} + assert 'text/plain' == CONTENT_TYPE(environ) + assert '200' == CONTENT_LENGTH(environ) + CONTENT_TYPE.update(environ,'new/type') + assert 'new/type' == CONTENT_TYPE(environ) + CONTENT_TYPE.delete(environ) + assert '' == CONTENT_TYPE(environ) + assert 'ignored/invalid' == environ['HTTP_CONTENT_TYPE'] + +def test_response_headers(): + collection = [('via', 'bing')] + _test_generic(collection) + normalize_headers(collection) + assert collection == [ + ('Cache-Control', 'public, max-age=1234'), + ('Pragma', 'test, multi, valued="items"'), + ('Referer', 'internal:/some/path'), + ('Content-Disposition', 'attachment; filename="bingles.txt"') + ] + +def test_cache_control(): + assert 'public' == CACHE_CONTROL() + assert 'public' == CACHE_CONTROL(public=True) + assert 'private' == CACHE_CONTROL(private=True) + assert 'no-cache' == CACHE_CONTROL(no_cache=True) + assert 'private, no-store' == CACHE_CONTROL(private=True, no_store=True) + assert 'public, max-age=60' == CACHE_CONTROL(max_age=60) + assert 'public, max-age=86400' == \ + CACHE_CONTROL(max_age=CACHE_CONTROL.ONE_DAY) + CACHE_CONTROL.extensions['community'] = str + assert 'public, community="bingles"' == \ + CACHE_CONTROL(community="bingles") + headers = [] + CACHE_CONTROL.apply(headers,max_age=60) + assert 'public, max-age=60' == CACHE_CONTROL(headers) + assert EXPIRES.parse(headers) > time.time() + assert EXPIRES.parse(headers) < time.time() + 60 + +def test_content_disposition(): + assert 'attachment' == CONTENT_DISPOSITION() + assert 'attachment' == CONTENT_DISPOSITION(attachment=True) + assert 'inline' == CONTENT_DISPOSITION(inline=True) + assert 'inline; filename="test.txt"' == \ + CONTENT_DISPOSITION(inline=True, filename="test.txt") + assert 'attachment; filename="test.txt"' == \ + CONTENT_DISPOSITION(filename="/some/path/test.txt") + headers = [] + CONTENT_DISPOSITION.apply(headers,filename="test.txt") + assert 'text/plain' == CONTENT_TYPE(headers) + CONTENT_DISPOSITION.apply(headers,filename="test") + assert 'text/plain' == CONTENT_TYPE(headers) + CONTENT_DISPOSITION.apply(headers,filename="test.html") + assert 'text/plain' == CONTENT_TYPE(headers) + headers = [('Content-Type', 'application/octet-stream')] + CONTENT_DISPOSITION.apply(headers,filename="test.txt") + assert 'text/plain' == CONTENT_TYPE(headers) + assert headers == [ + ('Content-Type', 'text/plain'), + ('Content-Disposition', 'attachment; filename="test.txt"') + ] + +def test_range(): + assert ('bytes',[(0,300)]) == RANGE.parse("bytes=0-300") + assert ('bytes',[(0,300)]) == RANGE.parse("bytes = -300") + assert ('bytes',[(0,None)]) == RANGE.parse("bytes= -") + assert ('bytes',[(0,None)]) == RANGE.parse("bytes=0 - ") + assert ('bytes',[(300,None)]) == RANGE.parse(" BYTES=300-") + assert ('bytes',[(4,5),(6,7)]) == RANGE.parse(" Bytes = 4 - 5,6 - 07 ") + assert ('bytes',[(0,5),(7,None)]) == RANGE.parse(" bytes=-5,7-") + assert ('bytes',[(0,5),(7,None)]) == RANGE.parse(" bytes=-5,7-") + assert ('bytes',[(0,5),(7,None)]) == RANGE.parse(" bytes=-5,7-") + assert None == RANGE.parse("") + assert None == RANGE.parse("bytes=0,300") + assert None == RANGE.parse("bytes=-7,5-") + +def test_copy(): + environ = {'HTTP_VIA':'bing', 'wsgi.version': '1.0' } + response_headers = [] + VIA.update(response_headers,environ) + assert response_headers == [('Via', 'bing')] + +def test_sorting(): + # verify the HTTP_HEADERS are set with their canonical form + sample = [WWW_AUTHENTICATE, VIA, ACCEPT, DATE, + ACCEPT_CHARSET, AGE, ALLOW, CACHE_CONTROL, + CONTENT_ENCODING, ETAG, CONTENT_TYPE, FROM, + EXPIRES, RANGE, UPGRADE, VARY, ALLOW] + sample.sort() + sample = [str(x) for x in sample] + assert sample == [ + # general headers first + 'Cache-Control', 'Date', 'Upgrade', 'Via', + # request headers next + 'Accept', 'Accept-Charset', 'From', 'Range', + # response headers following + 'Age', 'ETag', 'Vary', 'WWW-Authenticate', + # entity headers (/w expected duplicate) + 'Allow', 'Allow', 'Content-Encoding', 'Content-Type', 'Expires' + ] + +def test_normalize(): + response_headers = [ + ('www-authenticate','Response AuthMessage'), + ('unknown-header','Unknown Sorted Last'), + ('Via','General Bingles'), + ('aLLoW','Entity Allow Something'), + ('ETAG','Response 34234'), + ('expires','Entity An-Expiration-Date'), + ('date','General A-Date')] + normalize_headers(response_headers, strict=False) + assert response_headers == [ + ('Date', 'General A-Date'), + ('Via', 'General Bingles'), + ('ETag', 'Response 34234'), + ('WWW-Authenticate', 'Response AuthMessage'), + ('Allow', 'Entity Allow Something'), + ('Expires', 'Entity An-Expiration-Date'), + ('Unknown-Header', 'Unknown Sorted Last')] + +def test_if_modified_since(): + from paste.httpexceptions import HTTPBadRequest + date = 'Thu, 34 Jul 3119 29:34:18 GMT' + try: + x = IF_MODIFIED_SINCE.parse({'HTTP_IF_MODIFIED_SINCE': date, + 'wsgi.version': (1, 0)}) + except HTTPBadRequest: + pass + else: + assert 0 diff --git a/tests/test_import_string.py b/tests/test_import_string.py new file mode 100644 index 0000000..96526ac --- /dev/null +++ b/tests/test_import_string.py @@ -0,0 +1,16 @@ +from paste.util.import_string import * +import sys +import os + +def test_simple(): + for func in eval_import, simple_import: + assert func('sys') is sys + assert func('sys.version') is sys.version + assert func('os.path.join') is os.path.join + +def test_complex(): + assert eval_import('sys:version') is sys.version + assert eval_import('os:getcwd()') == os.getcwd() + assert (eval_import('sys:version.split()[0]') == + sys.version.split()[0]) + diff --git a/tests/test_multidict.py b/tests/test_multidict.py new file mode 100644 index 0000000..c99b10f --- /dev/null +++ b/tests/test_multidict.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +# (c) 2007 Ian Bicking and Philip Jenvey; written for Paste (http://pythonpaste.org) +# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php +import cgi +from nose.tools import assert_raises +from StringIO import StringIO +from paste.fixture import TestApp +from paste.wsgiwrappers import WSGIRequest +from paste.util.multidict import MultiDict, UnicodeMultiDict + +def test_dict(): + d = MultiDict({'a': 1}) + assert d.items() == [('a', 1)] + + d['b'] = 2 + d['c'] = 3 + assert d.items() == [('a', 1), ('b', 2), ('c', 3)] + + d['b'] = 4 + assert d.items() == [('a', 1), ('c', 3), ('b', 4)] + + d.add('b', 5) + assert_raises(KeyError, d.getone, "b") + assert d.getall('b') == [4, 5] + assert d.items() == [('a', 1), ('c', 3), ('b', 4), ('b', 5)] + + del d['b'] + assert d.items() == [('a', 1), ('c', 3)] + assert d.pop('xxx', 5) == 5 + assert d.getone('a') == 1 + assert d.popitem() == ('c', 3) + assert d.items() == [('a', 1)] + + item = [] + assert d.setdefault('z', item) is item + assert d.items() == [('a', 1), ('z', item)] + + assert d.setdefault('y', 6) == 6 + + assert d.mixed() == {'a': 1, 'y': 6, 'z': item} + assert d.dict_of_lists() == {'a': [1], 'y': [6], 'z': [item]} + + assert 'a' in d + dcopy = d.copy() + assert dcopy is not d + assert dcopy == d + d['x'] = 'x test' + assert dcopy != d + + d[(1, None)] = (None, 1) + assert d.items() == [('a', 1), ('z', []), ('y', 6), ('x', 'x test'), + ((1, None), (None, 1))] + +def test_unicode_dict(): + _test_unicode_dict() + _test_unicode_dict(decode_param_names=True) + +def _test_unicode_dict(decode_param_names=False): + d = UnicodeMultiDict(MultiDict({'a': 'a test'})) + d.encoding = 'utf-8' + d.errors = 'ignore' + + if decode_param_names: + key_str = unicode + d.decode_keys = True + else: + key_str = str + + def assert_unicode(obj): + assert isinstance(obj, unicode) + + def assert_key_str(obj): + assert isinstance(obj, key_str) + + def assert_unicode_item(obj): + key, value = obj + assert isinstance(key, key_str) + assert isinstance(value, unicode) + + assert d.items() == [('a', u'a test')] + map(assert_key_str, d.keys()) + map(assert_unicode, d.values()) + + d['b'] = '2 test' + d['c'] = '3 test' + assert d.items() == [('a', u'a test'), ('b', u'2 test'), ('c', u'3 test')] + map(assert_unicode_item, d.items()) + + d['b'] = '4 test' + assert d.items() == [('a', u'a test'), ('c', u'3 test'), ('b', u'4 test')] + map(assert_unicode_item, d.items()) + + d.add('b', '5 test') + assert_raises(KeyError, d.getone, "b") + assert d.getall('b') == [u'4 test', u'5 test'] + map(assert_unicode, d.getall('b')) + assert d.items() == [('a', u'a test'), ('c', u'3 test'), ('b', u'4 test'), + ('b', u'5 test')] + map(assert_unicode_item, d.items()) + + del d['b'] + assert d.items() == [('a', u'a test'), ('c', u'3 test')] + map(assert_unicode_item, d.items()) + assert d.pop('xxx', u'5 test') == u'5 test' + assert isinstance(d.pop('xxx', u'5 test'), unicode) + assert d.getone('a') == u'a test' + assert isinstance(d.getone('a'), unicode) + assert d.popitem() == ('c', u'3 test') + d['c'] = '3 test' + assert_unicode_item(d.popitem()) + assert d.items() == [('a', u'a test')] + map(assert_unicode_item, d.items()) + + item = [] + assert d.setdefault('z', item) is item + items = d.items() + assert items == [('a', u'a test'), ('z', item)] + assert isinstance(items[1][0], key_str) + assert isinstance(items[1][1], list) + + assert isinstance(d.setdefault('y', 'y test'), unicode) + assert isinstance(d['y'], unicode) + + assert d.mixed() == {u'a': u'a test', u'y': u'y test', u'z': item} + assert d.dict_of_lists() == {u'a': [u'a test'], u'y': [u'y test'], + u'z': [item]} + del d['z'] + map(assert_unicode_item, d.mixed().iteritems()) + map(assert_unicode_item, [(k, v[0]) for \ + k, v in d.dict_of_lists().iteritems()]) + + assert u'a' in d + dcopy = d.copy() + assert dcopy is not d + assert dcopy == d + d['x'] = 'x test' + assert dcopy != d + + d[(1, None)] = (None, 1) + assert d.items() == [('a', u'a test'), ('y', u'y test'), ('x', u'x test'), + ((1, None), (None, 1))] + item = d.items()[-1] + assert isinstance(item[0], tuple) + assert isinstance(item[1], tuple) + + fs = cgi.FieldStorage() + fs.name = 'thefile' + fs.filename = 'hello.txt' + fs.file = StringIO('hello') + d['f'] = fs + ufs = d['f'] + assert isinstance(ufs, cgi.FieldStorage) + assert ufs is not fs + assert ufs.name == fs.name + assert isinstance(ufs.name, key_str) + assert ufs.filename == fs.filename + assert isinstance(ufs.filename, unicode) + assert isinstance(ufs.value, str) + assert ufs.value == 'hello' diff --git a/tests/test_profilemiddleware.py b/tests/test_profilemiddleware.py new file mode 100644 index 0000000..4c189f8 --- /dev/null +++ b/tests/test_profilemiddleware.py @@ -0,0 +1,29 @@ +from paste.fixture import * +try: + from paste.debug.profile import * + disable = False +except ImportError: + disable = True + +if not disable: + def simple_app(environ, start_response): + start_response('200 OK', [('content-type', 'text/html')]) + return ['all ok'] + + def long_func(): + for i in range(1000): + pass + return 'test' + + def test_profile(): + app = TestApp(ProfileMiddleware(simple_app, {})) + res = app.get('/') + # The original app: + res.mustcontain('all ok') + # The profile information: + res.mustcontain('<pre') + + def test_decorator(): + value = profile_decorator()(long_func)() + assert value == 'test' + diff --git a/tests/test_proxy.py b/tests/test_proxy.py new file mode 100644 index 0000000..36d16b5 --- /dev/null +++ b/tests/test_proxy.py @@ -0,0 +1,12 @@ +from paste import proxy +from paste.fixture import TestApp + +def test_paste_website(): + # Not the most robust test... + # need to test things like POSTing to pages, and getting from pages + # that don't set content-length. + app = proxy.Proxy('http://pythonpaste.org') + app = TestApp(app) + res = app.get('/') + assert 'documentation' in res + diff --git a/tests/test_recursive.py b/tests/test_recursive.py new file mode 100644 index 0000000..e79d586 --- /dev/null +++ b/tests/test_recursive.py @@ -0,0 +1,105 @@ +from test_errordocument import error_docs_app, test_error_docs_app, simple_app +from paste.fixture import * +from paste.recursive import RecursiveMiddleware, ForwardRequestException + +def error_docs_app(environ, start_response): + if environ['PATH_INFO'] == '/not_found': + start_response("404 Not found", [('Content-type', 'text/plain')]) + return ['Not found'] + elif environ['PATH_INFO'] == '/error': + start_response("200 OK", [('Content-type', 'text/plain')]) + return ['Page not found'] + elif environ['PATH_INFO'] == '/recurse': + raise ForwardRequestException('/recurse') + else: + return simple_app(environ, start_response) + +class Middleware(object): + def __init__(self, app, url='/error'): + self.app = app + self.url = url + def __call__(self, environ, start_response): + raise ForwardRequestException(self.url) + +def forward(app): + app = TestApp(RecursiveMiddleware(app)) + res = app.get('') + assert res.header('content-type') == 'text/plain' + assert res.full_status == '200 OK' + assert 'requested page returned' in res + res = app.get('/error') + assert res.header('content-type') == 'text/plain' + assert res.full_status == '200 OK' + assert 'Page not found' in res + res = app.get('/not_found') + assert res.header('content-type') == 'text/plain' + assert res.full_status == '200 OK' + assert 'Page not found' in res + try: + res = app.get('/recurse') + except AssertionError, e: + if str(e).startswith('Forwarding loop detected'): + pass + else: + raise AssertionError('Failed to detect forwarding loop') + +def test_ForwardRequest_url(): + class TestForwardRequestMiddleware(Middleware): + def __call__(self, environ, start_response): + if environ['PATH_INFO'] != '/not_found': + return self.app(environ, start_response) + raise ForwardRequestException(self.url) + forward(TestForwardRequestMiddleware(error_docs_app)) + +def test_ForwardRequest_environ(): + class TestForwardRequestMiddleware(Middleware): + def __call__(self, environ, start_response): + if environ['PATH_INFO'] != '/not_found': + return self.app(environ, start_response) + environ['PATH_INFO'] = self.url + raise ForwardRequestException(environ=environ) + forward(TestForwardRequestMiddleware(error_docs_app)) + +def test_ForwardRequest_factory(): + + from paste.errordocument import StatusKeeper + + class TestForwardRequestMiddleware(Middleware): + def __call__(self, environ, start_response): + if environ['PATH_INFO'] != '/not_found': + return self.app(environ, start_response) + environ['PATH_INFO'] = self.url + def factory(app): + return StatusKeeper(app, status='404 Not Found', url='/error', headers=[]) + raise ForwardRequestException(factory=factory) + + app = TestForwardRequestMiddleware(error_docs_app) + app = TestApp(RecursiveMiddleware(app)) + res = app.get('') + assert res.header('content-type') == 'text/plain' + assert res.full_status == '200 OK' + assert 'requested page returned' in res + res = app.get('/error') + assert res.header('content-type') == 'text/plain' + assert res.full_status == '200 OK' + assert 'Page not found' in res + res = app.get('/not_found', status=404) + assert res.header('content-type') == 'text/plain' + assert res.full_status == '404 Not Found' # Different status + assert 'Page not found' in res + try: + res = app.get('/recurse') + except AssertionError, e: + if str(e).startswith('Forwarding loop detected'): + pass + else: + raise AssertionError('Failed to detect forwarding loop') + +# Test Deprecated Code +def test_ForwardRequestException(): + class TestForwardRequestExceptionMiddleware(Middleware): + def __call__(self, environ, start_response): + if environ['PATH_INFO'] != '/not_found': + return self.app(environ, start_response) + raise ForwardRequestException(path_info=self.url) + forward(TestForwardRequestExceptionMiddleware(error_docs_app)) diff --git a/tests/test_registry.py b/tests/test_registry.py new file mode 100644 index 0000000..d0dc8ac --- /dev/null +++ b/tests/test_registry.py @@ -0,0 +1,296 @@ +# (c) 2005 Ben Bangert +# This module is part of the Python Paste Project and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +from nose.tools import assert_raises + +from paste.fixture import * +from paste.registry import * +from paste.registry import Registry +from paste.evalexception.middleware import EvalException + +regobj = StackedObjectProxy() +secondobj = StackedObjectProxy(default=dict(hi='people')) + +def simpleapp(environ, start_response): + status = '200 OK' + response_headers = [('Content-type','text/plain')] + start_response(status, response_headers) + return ['Hello world!\n'] + +def simpleapp_withregistry(environ, start_response): + status = '200 OK' + response_headers = [('Content-type','text/plain')] + start_response(status, response_headers) + return ['Hello world!Value is %s\n' % regobj.keys()] + +def simpleapp_withregistry_default(environ, start_response): + status = '200 OK' + response_headers = [('Content-type','text/plain')] + start_response(status, response_headers) + return ['Hello world!Value is %s\n' % secondobj] + + +class RegistryUsingApp(object): + def __init__(self, var, value, raise_exc=False): + self.var = var + self.value = value + self.raise_exc = raise_exc + + def __call__(self, environ, start_response): + if environ.has_key('paste.registry'): + environ['paste.registry'].register(self.var, self.value) + if self.raise_exc: + raise self.raise_exc + status = '200 OK' + response_headers = [('Content-type','text/plain')] + start_response(status, response_headers) + return ['Hello world!\nThe variable is %s' % str(regobj)] + +class RegistryUsingIteratorApp(object): + def __init__(self, var, value): + self.var = var + self.value = value + + def __call__(self, environ, start_response): + if environ.has_key('paste.registry'): + environ['paste.registry'].register(self.var, self.value) + status = '200 OK' + response_headers = [('Content-type','text/plain')] + start_response(status, response_headers) + return iter(['Hello world!\nThe variable is %s' % str(regobj)]) + +class RegistryMiddleMan(object): + def __init__(self, app, var, value, depth): + self.app = app + self.var = var + self.value = value + self.depth = depth + + def __call__(self, environ, start_response): + if environ.has_key('paste.registry'): + environ['paste.registry'].register(self.var, self.value) + app_response = ['\nInserted by middleware!\nInsertValue at depth \ + %s is %s' % (self.depth, str(regobj))] + app_iter = None + app_iter = self.app(environ, start_response) + if type(app_iter) in (list, tuple): + app_response.extend(app_iter) + else: + response = [] + for line in app_iter: + response.append(line) + if hasattr(app_iter, 'close'): + app_iter.close() + app_response.extend(response) + app_response.extend(['\nAppended by middleware!\nAppendValue at \ + depth %s is %s' % (self.depth, str(regobj))]) + return app_response + + +def test_simple(): + app = TestApp(simpleapp) + response = app.get('/') + assert 'Hello world' in response + +def test_solo_registry(): + obj = {'hi':'people'} + wsgiapp = RegistryUsingApp(regobj, obj) + wsgiapp = RegistryManager(wsgiapp) + app = TestApp(wsgiapp) + res = app.get('/') + assert 'Hello world' in res + assert 'The variable is' in res + assert "{'hi': 'people'}" in res + +def test_registry_no_object_error(): + app = TestApp(simpleapp_withregistry) + assert_raises(TypeError, app.get, '/') + +def test_with_default_object(): + app = TestApp(simpleapp_withregistry_default) + res = app.get('/') + print res + assert 'Hello world' in res + assert "Value is {'hi': 'people'}" in res + +def test_double_registry(): + obj = {'hi':'people'} + secondobj = {'bye':'friends'} + wsgiapp = RegistryUsingApp(regobj, obj) + wsgiapp = RegistryManager(wsgiapp) + wsgiapp = RegistryMiddleMan(wsgiapp, regobj, secondobj, 0) + wsgiapp = RegistryManager(wsgiapp) + app = TestApp(wsgiapp) + res = app.get('/') + assert 'Hello world' in res + assert 'The variable is' in res + assert "{'hi': 'people'}" in res + assert "InsertValue at depth 0 is {'bye': 'friends'}" in res + assert "AppendValue at depth 0 is {'bye': 'friends'}" in res + +def test_really_deep_registry(): + keylist = ['fred', 'wilma', 'barney', 'homer', 'marge', 'bart', 'lisa', + 'maggie'] + valuelist = range(0, len(keylist)) + obj = {'hi':'people'} + wsgiapp = RegistryUsingApp(regobj, obj) + wsgiapp = RegistryManager(wsgiapp) + for depth in valuelist: + newobj = {keylist[depth]: depth} + wsgiapp = RegistryMiddleMan(wsgiapp, regobj, newobj, depth) + wsgiapp = RegistryManager(wsgiapp) + app = TestApp(wsgiapp) + res = app.get('/') + assert 'Hello world' in res + assert 'The variable is' in res + assert "{'hi': 'people'}" in res + for depth in valuelist: + assert "InsertValue at depth %s is {'%s': %s}" % \ + (depth, keylist[depth], depth) in res + for depth in valuelist: + assert "AppendValue at depth %s is {'%s': %s}" % \ + (depth, keylist[depth], depth) in res + +def test_iterating_response(): + obj = {'hi':'people'} + secondobj = {'bye':'friends'} + wsgiapp = RegistryUsingIteratorApp(regobj, obj) + wsgiapp = RegistryManager(wsgiapp) + wsgiapp = RegistryMiddleMan(wsgiapp, regobj, secondobj, 0) + wsgiapp = RegistryManager(wsgiapp) + app = TestApp(wsgiapp) + res = app.get('/') + assert 'Hello world' in res + assert 'The variable is' in res + assert "{'hi': 'people'}" in res + assert "InsertValue at depth 0 is {'bye': 'friends'}" in res + assert "AppendValue at depth 0 is {'bye': 'friends'}" in res + +def _test_restorer(stack, data): + # We need to test the request's specific Registry. Initialize it here so we + # can use it later (RegistryManager will re-use one preexisting in the + # environ) + registry = Registry() + extra_environ={'paste.throw_errors': False, + 'paste.registry': registry} + request_id = restorer.get_request_id(extra_environ) + app = TestApp(stack) + res = app.get('/', extra_environ=extra_environ, expect_errors=True) + + # Ensure all the StackedObjectProxies are empty after the RegistryUsingApp + # raises an Exception + for stacked, proxied_obj, test_cleanup in data: + only_key = proxied_obj.keys()[0] + try: + assert only_key not in stacked + assert False + except TypeError: + # Definitely empty + pass + + # Ensure the StackedObjectProxies & Registry 'work' in the simulated + # EvalException context + replace = {'replace': 'dict'} + new = {'new': 'object'} + restorer.restoration_begin(request_id) + try: + for stacked, proxied_obj, test_cleanup in data: + # Ensure our original data magically re-appears in this context + only_key, only_val = proxied_obj.items()[0] + assert only_key in stacked and stacked[only_key] == only_val + + # Ensure the Registry still works + registry.prepare() + registry.register(stacked, new) + assert 'new' in stacked and stacked['new'] == 'object' + registry.cleanup() + + # Back to the original (pre-prepare()) + assert only_key in stacked and stacked[only_key] == only_val + + registry.replace(stacked, replace) + assert 'replace' in stacked and stacked['replace'] == 'dict' + + if test_cleanup: + registry.cleanup() + try: + stacked._current_obj() + assert False + except TypeError: + # Definitely empty + pass + finally: + restorer.restoration_end() + +def _restorer_data(): + S = StackedObjectProxy + d = [[S(name='first'), dict(top='of the registry stack'), False], + [S(name='second'), dict(middle='of the stack'), False], + [S(name='third'), dict(bottom='of the STACK.'), False]] + return d + +def _set_cleanup_test(data): + """Instruct _test_restorer to check registry cleanup at this level of the stack + """ + data[2] = True + +def test_restorer_basic(): + data = _restorer_data()[0] + wsgiapp = RegistryUsingApp(data[0], data[1], raise_exc=Exception()) + wsgiapp = RegistryManager(wsgiapp) + _set_cleanup_test(data) + wsgiapp = EvalException(wsgiapp) + _test_restorer(wsgiapp, [data]) + +def test_restorer_basic_manager_outside(): + data = _restorer_data()[0] + wsgiapp = RegistryUsingApp(data[0], data[1], raise_exc=Exception()) + wsgiapp = EvalException(wsgiapp) + wsgiapp = RegistryManager(wsgiapp) + _set_cleanup_test(data) + _test_restorer(wsgiapp, [data]) + +def test_restorer_middleman_nested_evalexception(): + data = _restorer_data()[:2] + wsgiapp = RegistryUsingApp(data[0][0], data[0][1], raise_exc=Exception()) + wsgiapp = EvalException(wsgiapp) + wsgiapp = RegistryMiddleMan(wsgiapp, data[1][0], data[1][1], 0) + wsgiapp = RegistryManager(wsgiapp) + _set_cleanup_test(data[1]) + _test_restorer(wsgiapp, data) + +def test_restorer_nested_middleman(): + data = _restorer_data()[:2] + wsgiapp = RegistryUsingApp(data[0][0], data[0][1], raise_exc=Exception()) + wsgiapp = RegistryManager(wsgiapp) + _set_cleanup_test(data[0]) + wsgiapp = RegistryMiddleMan(wsgiapp, data[1][0], data[1][1], 0) + wsgiapp = EvalException(wsgiapp) + wsgiapp = RegistryManager(wsgiapp) + _set_cleanup_test(data[1]) + _test_restorer(wsgiapp, data) + +def test_restorer_middlemen_nested_evalexception(): + data = _restorer_data() + wsgiapp = RegistryUsingApp(data[0][0], data[0][1], raise_exc=Exception()) + wsgiapp = RegistryManager(wsgiapp) + _set_cleanup_test(data[0]) + wsgiapp = EvalException(wsgiapp) + wsgiapp = RegistryMiddleMan(wsgiapp, data[1][0], data[1][1], 0) + wsgiapp = RegistryManager(wsgiapp) + _set_cleanup_test(data[1]) + wsgiapp = RegistryMiddleMan(wsgiapp, data[2][0], data[2][1], 1) + wsgiapp = RegistryManager(wsgiapp) + _set_cleanup_test(data[2]) + _test_restorer(wsgiapp, data) + +def test_restorer_disabled(): + # Ensure restoration_begin/end work safely when there's no Registry + wsgiapp = TestApp(simpleapp) + wsgiapp.get('/') + try: + restorer.restoration_begin(1) + finally: + restorer.restoration_end() + # A second call should do nothing + restorer.restoration_end() diff --git a/tests/test_request.py b/tests/test_request.py new file mode 100644 index 0000000..3d882ed --- /dev/null +++ b/tests/test_request.py @@ -0,0 +1,62 @@ +# (c) 2005 Ben Bangert +# This module is part of the Python Paste Project and is released under +# the MIT License: http://www.opensource.org/licenses/mit-license.php +from paste.fixture import * +from paste.request import * +from paste.wsgiwrappers import WSGIRequest + +def simpleapp(environ, start_response): + status = '200 OK' + response_headers = [('Content-type','text/plain')] + start_response(status, response_headers) + request = WSGIRequest(environ) + return [ + 'Hello world!\n', 'The get is %s' % str(request.GET), + ' and Val is %s\n' % request.GET.get('name'), + 'The languages are: %s\n' % request.languages, + 'The accepttypes is: %s\n' % request.match_accept(['text/html', 'application/xml'])] + +def test_gets(): + app = TestApp(simpleapp) + res = app.get('/') + assert 'Hello' in res + assert "get is MultiDict([])" in res + + res = app.get('/?name=george') + res.mustcontain("get is MultiDict([('name', 'george')])") + res.mustcontain("Val is george") + +def test_language_parsing(): + app = TestApp(simpleapp) + res = app.get('/') + assert "The languages are: ['en-us']" in res + + res = app.get('/', headers={'Accept-Language':'da, en-gb;q=0.8, en;q=0.7'}) + assert "languages are: ['da', 'en-gb', 'en', 'en-us']" in res + + res = app.get('/', headers={'Accept-Language':'en-gb;q=0.8, da, en;q=0.7'}) + assert "languages are: ['da', 'en-gb', 'en', 'en-us']" in res + +def test_mime_parsing(): + app = TestApp(simpleapp) + res = app.get('/', headers={'Accept':'text/html'}) + assert "accepttypes is: ['text/html']" in res + + res = app.get('/', headers={'Accept':'application/xml'}) + assert "accepttypes is: ['application/xml']" in res + + res = app.get('/', headers={'Accept':'application/xml,*/*'}) + assert "accepttypes is: ['text/html', 'application/xml']" in res + +def test_bad_cookie(): + env = {} + env['HTTP_COOKIE'] = '070-it-:><?0' + assert get_cookie_dict(env) == {} + env['HTTP_COOKIE'] = 'foo=bar' + assert get_cookie_dict(env) == {'foo': 'bar'} + env['HTTP_COOKIE'] = '...' + assert get_cookie_dict(env) == {} + env['HTTP_COOKIE'] = '=foo' + assert get_cookie_dict(env) == {} + env['HTTP_COOKIE'] = '?=' + assert get_cookie_dict(env) == {} diff --git a/tests/test_request_form.py b/tests/test_request_form.py new file mode 100644 index 0000000..321b3b0 --- /dev/null +++ b/tests/test_request_form.py @@ -0,0 +1,36 @@ +import cgi +from cStringIO import StringIO +from paste.request import * +from paste.util.multidict import MultiDict + +def test_parse_querystring(): + e = {'QUERY_STRING': 'a=1&b=2&c=3&b=4'} + d = parse_querystring(e) + assert d == [('a', '1'), ('b', '2'), ('c', '3'), ('b', '4')] + assert e['paste.parsed_querystring'] == ( + (d, e['QUERY_STRING'])) + e = {'QUERY_STRING': 'a&b&c=&d=1'} + d = parse_querystring(e) + assert d == [('a', ''), ('b', ''), ('c', ''), ('d', '1')] + +def make_post(body): + e = { + 'CONTENT_TYPE': 'application/x-www-form-urlencoded', + 'CONTENT_LENGTH': str(len(body)), + 'REQUEST_METHOD': 'POST', + 'wsgi.input': StringIO(body), + } + return e + +def test_parsevars(): + e = make_post('a=1&b=2&c=3&b=4') + cur_input = e['wsgi.input'] + d = parse_formvars(e) + assert isinstance(d, MultiDict) + assert d == MultiDict([('a', '1'), ('b', '2'), ('c', '3'), ('b', '4')]) + assert e['paste.parsed_formvars'] == ( + (d, e['wsgi.input'])) + # XXX: http://trac.pythonpaste.org/pythonpaste/ticket/125 + #assert e['wsgi.input'] is not cur_input + #cur_input.seek(0) + #assert e['wsgi.input'].read() == cur_input.read() diff --git a/tests/test_response.py b/tests/test_response.py new file mode 100644 index 0000000..d4d4b65 --- /dev/null +++ b/tests/test_response.py @@ -0,0 +1,11 @@ +from paste.response import * + +def test_replace_header(): + h = [('content-type', 'text/plain'), + ('x-blah', 'foobar')] + replace_header(h, 'content-length', '10') + assert h[-1] == ('content-length', '10') + replace_header(h, 'Content-Type', 'text/html') + assert ('content-type', 'text/html') in h + assert ('content-type', 'text/plain') not in h + diff --git a/tests/test_session.py b/tests/test_session.py new file mode 100644 index 0000000..621d284 --- /dev/null +++ b/tests/test_session.py @@ -0,0 +1,52 @@ +from paste.session import SessionMiddleware +from paste.fixture import TestApp + +info = [] + +def wsgi_app(environ, start_response): + pi = environ.get('PATH_INFO', '') + if pi in ('/get1', '/get2'): + if pi == '/get1': + sess = environ['paste.session.factory']() + start_response('200 OK', [('content-type', 'text/plain')]) + if pi == '/get2': + sess = environ['paste.session.factory']() + if 'info' in sess: + return [str(sess['info'])] + else: + return ['no-info'] + if pi in ('/put1', '/put2'): + if pi == '/put1': + sess = environ['paste.session.factory']() + sess['info'] = info[0] + start_response('200 OK', [('content-type', 'text/plain')]) + if pi == '/put2': + sess = environ['paste.session.factory']() + sess['info'] = info[0] + return ['foo'] + +wsgi_app = SessionMiddleware(wsgi_app) + +def test_app1(): + app = TestApp(wsgi_app) + res = app.get('/get1') + assert res.body == 'no-info' + res = app.get('/get2') + assert res.body == 'no-info' + info[:] = ['test'] + res = app.get('/put1') + res = app.get('/get1') + assert res.body == 'test' + res = app.get('/get2') + assert res.body == 'test' + +def test_app2(): + app = TestApp(wsgi_app) + info[:] = ['fluff'] + res = app.get('/put2') + res = app.get('/get1') + assert res.body == 'fluff' + res = app.get('/get2') + assert res.body == 'fluff' + + diff --git a/tests/test_template.txt b/tests/test_template.txt new file mode 100644 index 0000000..45a85e2 --- /dev/null +++ b/tests/test_template.txt @@ -0,0 +1,136 @@ +The templating language is fairly simple, just {{stuff}}. For +example:: + + >>> from paste.util.template import Template, sub + >>> sub('Hi {{name}}', name='Ian') + 'Hi Ian' + >>> Template('Hi {{repr(name)}}').substitute(name='Ian') + "Hi 'Ian'" + >>> Template('Hi {{name+1}}').substitute(name='Ian') + Traceback (most recent call last): + ... + TypeError: cannot concatenate 'str' and 'int' objects at line 1 column 6 + +It also has Django-style piping:: + + >>> sub('Hi {{name|repr}}', name='Ian') + "Hi 'Ian'" + +Note that None shows up as an empty string:: + + >>> sub('Hi {{name}}', name=None) + 'Hi ' + +And if/elif/else:: + + >>> t = Template('{{if x}}{{y}}{{else}}{{z}}{{endif}}') + >>> t.substitute(x=1, y=2, z=3) + '2' + >>> t.substitute(x=0, y=2, z=3) + '3' + >>> t = Template('{{if x > 0}}positive{{elif x < 0}}negative{{else}}zero{{endif}}') + >>> t.substitute(x=1), t.substitute(x=-10), t.substitute(x=0) + ('positive', 'negative', 'zero') + +Plus a for loop:: + + >>> t = Template('{{for i in x}}i={{i}}\n{{endfor}}') + >>> t.substitute(x=range(3)) + 'i=0\ni=1\ni=2\n' + >>> t = Template('{{for a, b in sorted(z.items()):}}{{a}}={{b}},{{endfor}}') + >>> t.substitute(z={1: 2, 3: 4}) + '1=2,3=4,' + >>> t = Template('{{for i in x}}{{if not i}}{{break}}' + ... '{{endif}}{{i}} {{endfor}}') + >>> t.substitute(x=[1, 2, 0, 3, 4]) + '1 2 ' + >>> t = Template('{{for i in x}}{{if not i}}{{continue}}' + ... '{{endif}}{{i}} {{endfor}}') + >>> t.substitute(x=[1, 2, 0, 3, 0, 4]) + '1 2 3 4 ' + +Also Python blocks:: + + >>> sub('{{py:\nx=1\n}}{{x}}') + '1' + +And some syntax errors:: + + >>> t = Template('{{if x}}', name='foo.html') + Traceback (most recent call last): + ... + TemplateError: No {{endif}} at line 1 column 3 in foo.html + >>> t = Template('{{for x}}', name='foo2.html') + Traceback (most recent call last): + ... + TemplateError: Bad for (no "in") in 'x' at line 1 column 3 in foo2.html + +There's also an HTMLTemplate that uses HTMLisms:: + + >>> from paste.util.template import HTMLTemplate, sub_html, html + >>> sub_html('hi {{name}}', name='<foo>') + 'hi <foo>' + +But if you don't want quoting to happen you can do:: + + >>> sub_html('hi {{name}}', name=html('<foo>')) + 'hi <foo>' + >>> sub_html('hi {{name|html}}', name='<foo>') + 'hi <foo>' + +Also a couple handy functions;: + + >>> t = HTMLTemplate('<a href="article?id={{id|url}}" {{attr(class_=class_)}}>') + >>> t.substitute(id=1, class_='foo') + '<a href="article?id=1" class="foo">' + >>> t.substitute(id='with space', class_=None) + '<a href="article?id=with%20space" >' + +There's a handyish looper thing you can also use in your templates (or +in Python, but it's more useful in templates generally):: + + >>> from paste.util.looper import looper + >>> seq = ['apple', 'asparagus', 'Banana', 'orange'] + >>> for loop, item in looper(seq): + ... if item == 'apple': + ... assert loop.first + ... elif item == 'orange': + ... assert loop.last + ... if loop.first_group(lambda i: i[0].upper()): + ... print '%s:' % item[0].upper() + ... print loop.number, item + A: + 1 apple + 2 asparagus + B: + 3 Banana + O: + 4 orange + +It will also strip out empty lines, when there is a line that only +contains a directive/statement (if/for, etc):: + + >>> sub('{{if 1}}\n{{x}}\n{{endif}}\n', x=0) + '0\n' + >>> sub('{{if 1}}x={{x}}\n{{endif}}\n', x=1) + 'x=1\n' + >>> sub('{{if 1}}\nx={{x}}\n{{endif}}\n', x=1) + 'x=1\n' + +Lastly, there is a special directive that will create a default value +for a variable, if no value is given:: + + >>> sub('{{default x=1}}{{x}}', x=2) + '2' + >>> sub('{{default x=1}}{{x}}') + '1' + >>> # The normal case: + >>> sub('{{x}}') + Traceback (most recent call last): + ... + NameError: name 'x' is not defined at line 1 column 3 + +And comments work:: + + >>> sub('Test=x{{#whatever}}') + 'Test=x' diff --git a/tests/test_urlmap.py b/tests/test_urlmap.py new file mode 100644 index 0000000..9f77ca2 --- /dev/null +++ b/tests/test_urlmap.py @@ -0,0 +1,49 @@ +from paste.urlmap import * +from paste.fixture import * + +def make_app(response_text): + def app(environ, start_response): + headers = [('Content-type', 'text/html')] + start_response('200 OK', headers) + return [response_text % environ] + return app + +def test_map(): + mapper = URLMap({}) + app = TestApp(mapper) + text = '%s script_name="%%(SCRIPT_NAME)s" path_info="%%(PATH_INFO)s"' + mapper[''] = make_app(text % 'root') + mapper['/foo'] = make_app(text % 'foo-only') + mapper['/foo/bar'] = make_app(text % 'foo:bar') + mapper['/f'] = make_app(text % 'f-only') + res = app.get('/') + res.mustcontain('root') + res.mustcontain('script_name=""') + res.mustcontain('path_info="/"') + res = app.get('/blah') + res.mustcontain('root') + res.mustcontain('script_name=""') + res.mustcontain('path_info="/blah"') + res = app.get('/foo/and/more') + res.mustcontain('script_name="/foo"') + res.mustcontain('path_info="/and/more"') + res.mustcontain('foo-only') + res = app.get('/foo/bar/baz') + res.mustcontain('foo:bar') + res.mustcontain('script_name="/foo/bar"') + res.mustcontain('path_info="/baz"') + res = app.get('/fffzzz') + res.mustcontain('root') + res.mustcontain('path_info="/fffzzz"') + res = app.get('/f/z/y') + res.mustcontain('script_name="/f"') + res.mustcontain('path_info="/z/y"') + res.mustcontain('f-only') + +def test_404(): + mapper = URLMap({}) + app = TestApp(mapper, extra_environ={'HTTP_ACCEPT': 'text/html'}) + res = app.get("/-->%0D<script>alert('xss')</script>", status=404) + assert '--><script' not in res.body + res = app.get("/--%01><script>", status=404) + assert '--\x01><script>' not in res.body diff --git a/tests/test_urlparser.py b/tests/test_urlparser.py new file mode 100644 index 0000000..f8c8b9f --- /dev/null +++ b/tests/test_urlparser.py @@ -0,0 +1,178 @@ +import os +from paste.urlparser import * +from paste.fixture import * +from pkg_resources import get_distribution + +def relative_path(name): + here = os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'urlparser_data') + f = os.path.join('urlparser_data', '..', 'urlparser_data', name) + return os.path.join(here, f) + +def path(name): + return os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'urlparser_data', name) + +def make_app(name): + app = URLParser({}, path(name), name, index_names=['index', 'Main']) + testapp = TestApp(app) + return testapp + +def test_find_file(): + app = make_app('find_file') + res = app.get('/') + assert 'index1' in res + assert res.header('content-type') == 'text/plain' + res = app.get('/index') + assert 'index1' in res + assert res.header('content-type') == 'text/plain' + res = app.get('/index.txt') + assert 'index1' in res + assert res.header('content-type') == 'text/plain' + res = app.get('/test2.html') + assert 'test2' in res + assert res.header('content-type') == 'text/html' + res = app.get('/test 3.html') + assert 'test 3' in res + assert res.header('content-type') == 'text/html' + res = app.get('/test%203.html') + assert 'test 3' in res + assert res.header('content-type') == 'text/html' + res = app.get('/dir with spaces/test 4.html') + assert 'test 4' in res + assert res.header('content-type') == 'text/html' + res = app.get('/dir%20with%20spaces/test%204.html') + assert 'test 4' in res + assert res.header('content-type') == 'text/html' + # Ensure only data under the app's root directory is accessible + res = app.get('/../secured.txt', status=404) + res = app.get('/dir with spaces/../../secured.txt', status=404) + res = app.get('/%2e%2e/secured.txt', status=404) + res = app.get('/%2e%2e%3fsecured.txt', status=404) + res = app.get('/..%3fsecured.txt', status=404) + res = app.get('/dir%20with%20spaces/%2e%2e/%2e%2e/secured.txt', status=404) + +def test_deep(): + app = make_app('deep') + res = app.get('/') + assert 'index2' in res + res = app.get('/sub') + assert res.status == 301 + print res + assert res.header('location') == 'http://localhost/sub/' + assert 'http://localhost/sub/' in res + res = app.get('/sub/') + assert 'index3' in res + +def test_python(): + app = make_app('python') + res = app.get('/simpleapp') + assert 'test1' in res + assert res.header('test-header') == 'TEST!' + assert res.header('content-type') == 'text/html' + res = app.get('/stream') + assert 'test2' in res + res = app.get('/sub/simpleapp') + assert 'subsimple' in res + +def test_hook(): + app = make_app('hook') + res = app.get('/bob/app') + assert 'user: bob' in res + res = app.get('/tim/') + assert 'index: tim' in res + +def test_not_found_hook(): + app = make_app('not_found') + res = app.get('/simple/notfound') + assert res.status == 200 + assert 'not found' in res + res = app.get('/simple/found') + assert 'is found' in res + res = app.get('/recur/__notfound', status=404) + # @@: It's unfortunate that the original path doesn't actually show up + assert '/recur/notfound' in res + res = app.get('/recur/__isfound') + assert res.status == 200 + assert 'is found' in res + res = app.get('/user/list') + assert 'user: None' in res + res = app.get('/user/bob/list') + assert res.status == 200 + assert 'user: bob' in res + +def test_relative_path_in_static_parser(): + x = relative_path('find_file') + app = StaticURLParser(relative_path('find_file')) + assert '..' not in app.root_directory + +def test_xss(): + app = TestApp(StaticURLParser(relative_path('find_file')), + extra_environ={'HTTP_ACCEPT': 'text/html'}) + res = app.get("/-->%0D<script>alert('xss')</script>", status=404) + assert '--><script>' not in res.body + +def test_static_parser(): + app = StaticURLParser(path('find_file')) + testapp = TestApp(app) + res = testapp.get('', status=301) + res = testapp.get('/', status=404) + res = testapp.get('/index.txt') + assert res.body.strip() == 'index1' + res = testapp.get('/index.txt/foo', status=404) + res = testapp.get('/test 3.html') + assert res.body.strip() == 'test 3' + res = testapp.get('/test%203.html') + assert res.body.strip() == 'test 3' + res = testapp.get('/dir with spaces/test 4.html') + assert res.body.strip() == 'test 4' + res = testapp.get('/dir%20with%20spaces/test%204.html') + assert res.body.strip() == 'test 4' + # Ensure only data under the app's root directory is accessible + res = testapp.get('/../secured.txt', status=404) + res = testapp.get('/dir with spaces/../../secured.txt', status=404) + res = testapp.get('/%2e%2e/secured.txt', status=404) + res = testapp.get('/dir%20with%20spaces/%2e%2e/%2e%2e/secured.txt', status=404) + res = testapp.get('/dir%20with%20spaces/', status=404) + +def test_egg_parser(): + app = PkgResourcesParser('Paste', 'paste') + testapp = TestApp(app) + res = testapp.get('', status=301) + res = testapp.get('/', status=404) + res = testapp.get('/flup_session', status=404) + res = testapp.get('/util/classinit.py') + assert 'ClassInitMeta' in res + res = testapp.get('/util/classinit', status=404) + res = testapp.get('/util', status=301) + res = testapp.get('/util/classinit.py/foo', status=404) + + # Find a readable file in the Paste pkg's root directory (or upwards the + # directory tree). Ensure it's not accessible via the URLParser + unreachable_test_file = None + search_path = pkg_root_path = get_distribution('Paste').location + level = 0 + # We might not find any readable files in the pkg's root directory (this + # is likely when Paste is installed as a .egg in site-packages). We + # (hopefully) can prevent this by traversing up the directory tree until + # a usable file is found + while unreachable_test_file is None and \ + os.path.normpath(search_path) != os.path.sep: + for file in os.listdir(search_path): + full_path = os.path.join(search_path, file) + if os.path.isfile(full_path) and os.access(full_path, os.R_OK): + unreachable_test_file = file + break + + search_path = os.path.dirname(search_path) + level += 1 + assert unreachable_test_file is not None, \ + 'test_egg_parser requires a readable file in a parent dir of the\n' \ + 'Paste pkg\'s root dir:\n%s' % pkg_root_path + + unreachable_path = '/' + '../'*level + unreachable_test_file + unreachable_path_quoted = '/' + '%2e%2e/'*level + unreachable_test_file + res = testapp.get(unreachable_path, status=404) + res = testapp.get('/util/..' + unreachable_path, status=404) + res = testapp.get(unreachable_path_quoted, status=404) + res = testapp.get('/util/%2e%2e' + unreachable_path_quoted, status=404) diff --git a/tests/test_util/__init__.py b/tests/test_util/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tests/test_util/__init__.py diff --git a/tests/test_util/test_datetimeutil.py b/tests/test_util/test_datetimeutil.py new file mode 100644 index 0000000..3cd91ba --- /dev/null +++ b/tests/test_util/test_datetimeutil.py @@ -0,0 +1,135 @@ +# (c) 2005 Clark C. Evans and contributors
+# This module is part of the Python Paste Project and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+# Some of this code was funded by: http://prometheusresearch.com
+from time import localtime
+from datetime import date
+from paste.util.datetimeutil import *
+
+def test_timedelta():
+ assert('' == normalize_timedelta(""))
+ assert('0.10' == normalize_timedelta("6m"))
+ assert('0.50' == normalize_timedelta("30m"))
+ assert('0.75' == normalize_timedelta("45m"))
+ assert('1.00' == normalize_timedelta("60 min"))
+ assert('1.50' == normalize_timedelta("90min"))
+ assert('1.50' == normalize_timedelta("1.50"))
+ assert('4.50' == normalize_timedelta("4 : 30"))
+ assert('1.50' == normalize_timedelta("1h 30m"))
+ assert('1.00' == normalize_timedelta("1"))
+ assert('1.00' == normalize_timedelta("1 hour"))
+ assert('8.00' == normalize_timedelta("480 mins"))
+ assert('8.00' == normalize_timedelta("8h"))
+ assert('0.50' == normalize_timedelta("0.5"))
+ assert('0.10' == normalize_timedelta(".1"))
+ assert('0.50' == normalize_timedelta(".50"))
+ assert('0.75' == normalize_timedelta("0.75"))
+
+def test_time():
+ assert('03:00 PM' == normalize_time("3p", ampm=True))
+ assert('03:00 AM' == normalize_time("300", ampm=True))
+ assert('03:22 AM' == normalize_time("322", ampm=True))
+ assert('01:22 PM' == normalize_time("1322", ampm=True))
+ assert('01:00 PM' == normalize_time("13", ampm=True))
+ assert('12:00 PM' == normalize_time("noon", ampm=True))
+ assert("06:00 PM" == normalize_time("6", ampm=True))
+ assert("01:00 PM" == normalize_time("1", ampm=True))
+ assert("07:00 AM" == normalize_time("7", ampm=True))
+ assert("01:00 PM" == normalize_time("1 pm", ampm=True))
+ assert("03:30 PM" == normalize_time("3:30 pm", ampm=True))
+ assert("03:30 PM" == normalize_time("3 30 pm", ampm=True))
+ assert("03:30 PM" == normalize_time("3 30 P.M.", ampm=True))
+ assert("12:00 PM" == normalize_time("0", ampm=True))
+ assert("12:00 AM" == normalize_time("1200 AM", ampm=True))
+
+def test_date():
+ tm = localtime()
+ yr = tm[0]
+ mo = tm[1]
+ assert(date(yr,4,11) == parse_date("411"))
+ assert(date(yr,4,11) == parse_date("APR11"))
+ assert(date(yr,4,11) == parse_date("11APR"))
+ assert(date(yr,4,11) == parse_date("4 11"))
+ assert(date(yr,4,11) == parse_date("11 APR"))
+ assert(date(yr,4,11) == parse_date("APR 11"))
+ assert(date(yr,mo,11) == parse_date("11"))
+ assert(date(yr,4,1) == parse_date("APR"))
+ assert(date(yr,4,11) == parse_date("4/11"))
+ assert(date.today() == parse_date("today"))
+ assert(date.today() == parse_date("now"))
+ assert(None == parse_date(""))
+ assert('' == normalize_date(None))
+
+ assert('2001-02-03' == normalize_date("20010203"))
+ assert('1999-04-11' == normalize_date("1999 4 11"))
+ assert('1999-04-11' == normalize_date("1999 APR 11"))
+ assert('1999-04-11' == normalize_date("APR 11 1999"))
+ assert('1999-04-11' == normalize_date("11 APR 1999"))
+ assert('1999-04-11' == normalize_date("4 11 1999"))
+ assert('1999-04-01' == normalize_date("1999 APR"))
+ assert('1999-04-01' == normalize_date("1999 4"))
+ assert('1999-04-01' == normalize_date("4 1999"))
+ assert('1999-04-01' == normalize_date("APR 1999"))
+ assert('1999-01-01' == normalize_date("1999"))
+
+ assert('1999-04-01' == normalize_date("1APR1999"))
+ assert('2001-04-01' == normalize_date("1APR2001"))
+
+ assert('1999-04-18' == normalize_date("1999-04-11+7"))
+ assert('1999-04-18' == normalize_date("1999-04-11 7"))
+ assert('1999-04-01' == normalize_date("1 apr 1999"))
+ assert('1999-04-11' == normalize_date("11 apr 1999"))
+ assert('1999-04-11' == normalize_date("11 Apr 1999"))
+ assert('1999-04-11' == normalize_date("11-apr-1999"))
+ assert('1999-04-11' == normalize_date("11 April 1999"))
+ assert('1999-04-11' == normalize_date("11 APRIL 1999"))
+ assert('1999-04-11' == normalize_date("11 april 1999"))
+ assert('1999-04-11' == normalize_date("11 aprick 1999"))
+ assert('1999-04-11' == normalize_date("APR 11, 1999"))
+ assert('1999-04-11' == normalize_date("4/11/1999"))
+ assert('1999-04-11' == normalize_date("4-11-1999"))
+ assert('1999-04-11' == normalize_date("1999-4-11"))
+ assert('1999-04-11' == normalize_date("19990411"))
+
+ assert('1999-01-01' == normalize_date("1 Jan 1999"))
+ assert('1999-02-01' == normalize_date("1 Feb 1999"))
+ assert('1999-03-01' == normalize_date("1 Mar 1999"))
+ assert('1999-04-01' == normalize_date("1 Apr 1999"))
+ assert('1999-05-01' == normalize_date("1 May 1999"))
+ assert('1999-06-01' == normalize_date("1 Jun 1999"))
+ assert('1999-07-01' == normalize_date("1 Jul 1999"))
+ assert('1999-08-01' == normalize_date("1 Aug 1999"))
+ assert('1999-09-01' == normalize_date("1 Sep 1999"))
+ assert('1999-10-01' == normalize_date("1 Oct 1999"))
+ assert('1999-11-01' == normalize_date("1 Nov 1999"))
+ assert('1999-12-01' == normalize_date("1 Dec 1999"))
+
+ assert('1999-04-30' == normalize_date("1999-4-30"))
+ assert('2000-02-29' == normalize_date("29 FEB 2000"))
+ assert('2001-02-28' == normalize_date("28 FEB 2001"))
+ assert('2004-02-29' == normalize_date("29 FEB 2004"))
+ assert('2100-02-28' == normalize_date("28 FEB 2100"))
+ assert('1900-02-28' == normalize_date("28 FEB 1900"))
+
+ def assertError(val):
+ try:
+ normalize_date(val)
+ except (TypeError,ValueError):
+ return
+ raise "type error expected", val
+
+ assertError("2000-13-11")
+ assertError("APR 99")
+ assertError("29 FEB 1900")
+ assertError("29 FEB 2100")
+ assertError("29 FEB 2001")
+ assertError("1999-4-31")
+ assertError("APR 99")
+ assertError("20301")
+ assertError("020301")
+ assertError("1APR99")
+ assertError("1APR01")
+ assertError("1 APR 99")
+ assertError("1 APR 01")
+ assertError("11/5/01")
+
diff --git a/tests/test_util/test_mimeparse.py b/tests/test_util/test_mimeparse.py new file mode 100644 index 0000000..c2d6cdf --- /dev/null +++ b/tests/test_util/test_mimeparse.py @@ -0,0 +1,237 @@ +# (c) 2010 Ch. Zwerschke and contributors
+# This module is part of the Python Paste Project and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+from time import localtime
+from datetime import date
+from paste.util.mimeparse import *
+
+def test_parse_mime_type():
+ parse = parse_mime_type
+ assert parse('*/*') == ('*', '*', {})
+ assert parse('text/html') == ('text', 'html', {})
+ assert parse('audio/*; q=0.2') == ('audio', '*', {'q': '0.2'})
+ assert parse('text/x-dvi;level=1') == ('text', 'x-dvi', {'level': '1'})
+ assert parse('image/gif; level=2; q=0.4') == (
+ 'image', 'gif', {'level': '2', 'q': '0.4'})
+ assert parse('application/xhtml;level=3;q=0.5') == (
+ 'application', 'xhtml', {'level': '3', 'q': '0.5'})
+ assert parse('application/xml') == ('application', 'xml', {})
+ assert parse('application/xml;q=1') == ('application', 'xml', {'q': '1'})
+ assert parse('application/xml ; q=1;b=other') == (
+ 'application', 'xml', {'q': '1', 'b': 'other'})
+ assert parse('application/xml ; q=2;b=other') == (
+ 'application', 'xml', {'q': '2', 'b': 'other'})
+ assert parse('application/xhtml;q=0.5') == (
+ 'application', 'xhtml', {'q': '0.5'})
+ assert parse('application/xhtml;q=0.5;ver=1.2') == (
+ 'application', 'xhtml', {'q': '0.5', 'ver': '1.2'})
+
+def test_parse_illformed_mime_type():
+ parse = parse_mime_type
+ assert parse('*') == ('*', '*', {})
+ assert parse('text') == ('text', '*', {})
+ assert parse('text/') == ('text', '*', {})
+ assert parse('/plain') == ('*', 'plain', {})
+ assert parse('/') == ('*', '*', {})
+ assert parse('text/plain;') == ('text', 'plain', {})
+ assert parse(';q=0.5') == ('*', '*', {'q': '0.5'})
+ assert parse('*; q=.2') == ('*', '*', {'q': '.2'})
+ assert parse('image; q=.7; level=3') == (
+ 'image', '*', {'q': '.7', 'level': '3'})
+ assert parse('*;q=1') == ('*', '*', {'q': '1'})
+ assert parse('*;q=') == ('*', '*', {})
+ assert parse('*;=0.5') == ('*', '*', {})
+ assert parse('*;q=foobar') == ('*', '*', {'q': 'foobar'})
+ assert parse('image/gif; level=2; q=2') == (
+ 'image', 'gif', {'level': '2', 'q': '2'})
+ assert parse('application/xml;q=') == ('application', 'xml', {})
+ assert parse('application/xml ;q=') == ('application', 'xml', {})
+ assert parse(' *; q =;') == ('*', '*', {})
+ assert parse(' *; q=.2') == ('*', '*', {'q': '.2'})
+
+def test_parse_media_range():
+ parse = parse_media_range
+ assert parse('application/*;q=0.5') == ('application', '*', {'q': '0.5'})
+ assert parse('text/plain') == ('text', 'plain', {'q': '1'})
+ assert parse('*') == ('*', '*', {'q': '1'})
+ assert parse(';q=0.5') == ('*', '*', {'q': '0.5'})
+ assert parse('*;q=0.5') == ('*', '*', {'q': '0.5'})
+ assert parse('*;q=1') == ('*', '*', {'q': '1'})
+ assert parse('*;q=') == ('*', '*', {'q': '1'})
+ assert parse('*;q=-1') == ('*', '*', {'q': '1'})
+ assert parse('*;q=foobar') == ('*', '*', {'q': '1'})
+ assert parse('*;q=0.0001') == ('*', '*', {'q': '0.0001'})
+ assert parse('*;q=1000.0') == ('*', '*', {'q': '1'})
+ assert parse('*;q=0') == ('*', '*', {'q': '0'})
+ assert parse('*;q=0.0000') == ('*', '*', {'q': '0.0000'})
+ assert parse('*;q=1.0001') == ('*', '*', {'q': '1'})
+ assert parse('*;q=2') == ('*', '*', {'q': '1'})
+ assert parse('*;q=1e3') == ('*', '*', {'q': '1'})
+ assert parse('image/gif; level=2') == (
+ 'image', 'gif', {'level': '2', 'q': '1'})
+ assert parse('image/gif; level=2; q=0.5') == (
+ 'image', 'gif', {'level': '2', 'q': '0.5'})
+ assert parse('image/gif; level=2; q=2') == (
+ 'image', 'gif', {'level': '2', 'q': '1'})
+ assert parse('application/xml') == ('application', 'xml', {'q': '1'})
+ assert parse('application/xml;q=1') == ('application', 'xml', {'q': '1'})
+ assert parse('application/xml;q=') == ('application', 'xml', {'q': '1'})
+ assert parse('application/xml ;q=') == ('application', 'xml', {'q': '1'})
+ assert parse('application/xml ; q=1;b=other') == (
+ 'application', 'xml', {'q': '1', 'b': 'other'})
+ assert parse('application/xml ; q=2;b=other') == (
+ 'application', 'xml', {'q': '1', 'b': 'other'})
+ assert parse(' *; q =;') == ('*', '*', {'q': '1'})
+ assert parse(' *; q=.2') == ('*', '*', {'q': '.2'})
+
+def test_fitness_and_quality_parsed():
+ faq = fitness_and_quality_parsed
+ assert faq('*/*;q=0.7', [
+ ('foo', 'bar', {'q': '0.5'})]) == (0, 0.5)
+ assert faq('foo/*;q=0.7', [
+ ('foo', 'bar', {'q': '0.5'})]) == (100, 0.5)
+ assert faq('*/bar;q=0.7', [
+ ('foo', 'bar', {'q': '0.5'})]) == (10, 0.5)
+ assert faq('foo/bar;q=0.7', [
+ ('foo', 'bar', {'q': '0.5'})]) == (110, 0.5)
+ assert faq('text/html;q=0.7', [
+ ('foo', 'bar', {'q': '0.5'})]) == (-1, 0)
+ assert faq('text/html;q=0.7', [
+ ('text', 'bar', {'q': '0.5'})]) == (-1, 0)
+ assert faq('text/html;q=0.7', [
+ ('foo', 'html', {'q': '0.5'})]) == (-1, 0)
+ assert faq('text/html;q=0.7', [
+ ('text', '*', {'q': '0.5'})]) == (100, 0.5)
+ assert faq('text/html;q=0.7', [
+ ('*', 'html', {'q': '0.5'})]) == (10, 0.5)
+ assert faq('text/html;q=0.7', [
+ ('*', '*', {'q': '0'}), ('text', 'html', {'q': '0.5'})]) == (110, 0.5)
+ assert faq('text/html;q=0.7', [
+ ('*', '*', {'q': '0.5'}), ('audio', '*', {'q': '0'})]) == (0, 0.5)
+ assert faq('audio/mp3;q=0.7', [
+ ('*', '*', {'q': '0'}), ('audio', '*', {'q': '0.5'})]) == (100, 0.5)
+ assert faq('*/mp3;q=0.7', [
+ ('foo', 'mp3', {'q': '0.5'}), ('audio', '*', {'q': '0'})]) == (10, 0.5)
+ assert faq('audio/mp3;q=0.7', [
+ ('audio', 'ogg', {'q': '0'}), ('*', 'mp3', {'q': '0.5'})]) == (10, 0.5)
+ assert faq('audio/mp3;q=0.7', [
+ ('*', 'ogg', {'q': '0'}), ('*', 'mp3', {'q': '0.5'})]) == (10, 0.5)
+ assert faq('text/html;q=0.7', [
+ ('text', 'plain', {'q': '0'}),
+ ('plain', 'html', {'q': '0'}),
+ ('text', 'html', {'q': '0.5'}),
+ ('html', 'text', {'q': '0'})]) == (110, 0.5)
+ assert faq('text/html;q=0.7;level=2', [
+ ('plain', 'html', {'q': '0', 'level': '2'}),
+ ('text', '*', {'q': '0.5', 'level': '3'}),
+ ('*', 'html', {'q': '0.5', 'level': '2'}),
+ ('image', 'gif', {'q': '0.5', 'level': '2'})]) == (100, 0.5)
+ assert faq('text/html;q=0.7;level=2', [
+ ('text', 'plain', {'q': '0'}), ('text', 'html', {'q': '0'}),
+ ('text', 'plain', {'q': '0', 'level': '2'}),
+ ('text', 'html', {'q': '0.5', 'level': '2'}),
+ ('*', '*', {'q': '0', 'level': '2'}),
+ ('text', 'html', {'q': '0', 'level': '3'})]) == (111, 0.5)
+ assert faq('text/html;q=0.7;level=2;opt=3', [
+ ('text', 'html', {'q': '0'}),
+ ('text', 'html', {'q': '0', 'level': '2'}),
+ ('text', 'html', {'q': '0', 'opt': '3'}),
+ ('*', '*', {'q': '0', 'level': '2', 'opt': '3'}),
+ ('text', 'html', {'q': '0', 'level': '3', 'opt': '3'}),
+ ('text', 'html', {'q': '0.5', 'level': '2', 'opt': '3'}),
+ ('*', '*', {'q': '0', 'level': '3', 'opt': '3'})]) == (112, 0.5)
+
+def test_quality_parsed():
+ qp = quality_parsed
+ assert qp('image/gif;q=0.7', [('image', 'jpg', {'q': '0.5'})]) == 0
+ assert qp('image/gif;q=0.7', [('image', '*', {'q': '0.5'})]) == 0.5
+ assert qp('audio/mp3;q=0.7;quality=100', [
+ ('*', '*', {'q': '0', 'quality': '100'}),
+ ('audio', '*', {'q': '0', 'quality': '100'}),
+ ('*', 'mp3', {'q': '0', 'quality': '100'}),
+ ('audio', 'mp3', {'q': '0', 'quality': '50'}),
+ ('audio', 'mp3', {'q': '0.5', 'quality': '100'}),
+ ('audio', 'mp3', {'q': '0.5'})]) == 0.5
+
+def test_quality():
+ assert quality('text/html',
+ 'text/*;q=0.3, text/html;q=0.75, text/html;level=1,'
+ ' text/html;level=2;q=0.4, */*;q=0.5') == 0.75
+ assert quality('text/html;level=2',
+ 'text/*;q=0.3, text/html;q=0.7, text/html;level=1,'
+ ' text/html;level=2;q=0.4, */*;q=0.5') == 0.4
+ assert quality('text/plain',
+ 'text/*;q=0.25, text/html;q=0.7, text/html;level=1,'
+ ' text/html;level=2;q=0.4, */*;q=0.5') == 0.25
+ assert quality('plain/text',
+ 'text/*;q=0.3, text/html;q=0.7, text/html;level=1,'
+ ' text/html;level=2;q=0.4, */*;q=0.5') == 0.5
+ assert quality('text/html;level=1',
+ 'text/*;q=0.3, text/html;q=0.7, text/html;level=1,'
+ ' text/html;level=2;q=0.4, */*;q=0.5') == 1
+ assert quality('image/jpeg',
+ 'text/*;q=0.3, text/html;q=0.7, text/html;level=1,'
+ ' text/html;level=2;q=0.4, */*;q=0.5') == 0.5
+ assert quality('text/html;level=2',
+ 'text/*;q=0.3, text/html;q=0.7, text/html;level=1,'
+ ' text/html;level=2;q=0.375, */*;q=0.5') == 0.375
+ assert quality('text/html;level=3',
+ 'text/*;q=0.3, text/html;q=0.75, text/html;level=1,'
+ ' text/html;level=2;q=0.4, */*;q=0.5') == 0.75
+
+def test_best_match():
+ bm = best_match
+ assert bm([], '*/*') == ''
+ assert bm(['application/xbel+xml', 'text/xml'],
+ 'text/*;q=0.5,*/*; q=0.1') == 'text/xml'
+ assert bm(['application/xbel+xml', 'audio/mp3'],
+ 'text/*;q=0.5,*/*; q=0.1') == 'application/xbel+xml'
+ assert bm(['application/xbel+xml', 'audio/mp3'],
+ 'text/*;q=0.5,*/mp3; q=0.1') == 'audio/mp3'
+ assert bm(['application/xbel+xml', 'text/plain', 'text/html'],
+ 'text/*;q=0.5,*/plain; q=0.1') == 'text/plain'
+ assert bm(['application/xbel+xml', 'text/html', 'text/xhtml'],
+ 'text/*;q=0.1,*/xhtml; q=0.5') == 'text/html'
+ assert bm(['application/xbel+xml', 'text/html', 'text/xhtml'],
+ '*/html;q=0.1,*/xhtml; q=0.5') == 'text/xhtml'
+ assert bm(['application/xbel+xml', 'application/xml'],
+ 'application/xbel+xml') == 'application/xbel+xml'
+ assert bm(['application/xbel+xml', 'application/xml'],
+ 'application/xbel+xml; q=1') == 'application/xbel+xml'
+ assert bm(['application/xbel+xml', 'application/xml'],
+ 'application/xml; q=1') == 'application/xml'
+ assert bm(['application/xbel+xml', 'application/xml'],
+ 'application/*; q=1') == 'application/xbel+xml'
+ assert bm(['application/xbel+xml', 'application/xml'],
+ '*/*, application/xml') == 'application/xml'
+ assert bm(['application/xbel+xml', 'text/xml'],
+ 'text/*;q=0.5,*/*; q=0.1') == 'text/xml'
+ assert bm(['application/xbel+xml', 'text/xml'],
+ 'text/html,application/atom+xml; q=0.9') == ''
+ assert bm(['application/json', 'text/html'],
+ 'application/json, text/javascript, */*') == 'application/json'
+ assert bm(['application/json', 'text/html'],
+ 'application/json, text/html;q=0.9') == 'application/json'
+ assert bm(['image/*', 'application/xml'], 'image/png') == 'image/*'
+ assert bm(['image/*', 'application/xml'], 'image/*') == 'image/*'
+
+def test_illformed_best_match():
+ bm = best_match
+ assert bm(['image/png', 'image/jpeg', 'image/gif', 'text/html'],
+ 'text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2') == 'image/jpeg'
+ assert bm(['image/png', 'image/jpg', 'image/tif', 'text/html'],
+ 'text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2') == 'text/html'
+ assert bm(['image/png', 'image/jpg', 'image/tif', 'audio/mp3'],
+ 'text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2') == 'image/png'
+
+def test_sorted_match():
+ dm = desired_matches
+ assert dm(['text/html', 'application/xml'],
+ 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,'
+ 'text/plain;q=0.8,image/png') == ['text/html', 'application/xml']
+ assert dm(['text/html', 'application/xml'],
+ 'application/xml,application/json') == ['application/xml']
+ assert dm(['text/xhtml', 'text/plain', 'application/xhtml'],
+ 'text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,'
+ 'text/plain;q=0.8,image/png') == ['text/plain']
diff --git a/tests/test_wsgiwrappers.py b/tests/test_wsgiwrappers.py new file mode 100644 index 0000000..b552f86 --- /dev/null +++ b/tests/test_wsgiwrappers.py @@ -0,0 +1,146 @@ +# -*- coding: utf-8 -*- +# (c) 2007 Philip Jenvey; written for Paste (http://pythonpaste.org) +# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php +import cgi +from paste.fixture import TestApp +from paste.wsgiwrappers import WSGIRequest, WSGIResponse + +class AssertApp(object): + def __init__(self, assertfunc): + self.assertfunc = assertfunc + + def __call__(self, environ, start_response): + start_response('200 OK', [('Content-type','text/plain')]) + self.assertfunc(environ) + return ['Passed'] + +no_encoding = object() +def valid_name(name, encoding=no_encoding, post=False): + def assert_valid_name(environ): + if encoding is not no_encoding: + WSGIRequest.defaults._push_object(dict(content_type='text/html', + charset=encoding)) + try: + request = WSGIRequest(environ) + if post: + params = request.POST + else: + params = request.GET + assert params['name'] == name + assert request.params['name'] == name + finally: + if encoding is not no_encoding: + WSGIRequest.defaults._pop_object() + return assert_valid_name + +def test_wsgirequest_charset(): + # Jose, 'José' + app = TestApp(AssertApp(assertfunc=valid_name(u'José', + encoding='iso-8859-1'))) + res = app.get('/?name=Jos%E9') + + # Tanaka, '田中' + app = TestApp(AssertApp(assertfunc=valid_name(u'田中', encoding='UTF-8'))) + res = app.get('/?name=%E7%94%B0%E4%B8%AD') + + # Nippon (Japan), '日本' + app = TestApp(AssertApp(assertfunc=valid_name(u'日本', encoding='UTF-8', + post=True))) + res = app.post('/', params=dict(name='日本')) + + # WSGIRequest will determine the charset from the Content-Type header when + # unicode is expected. + # No encoding specified: not expecting unicode + app = TestApp(AssertApp(assertfunc=valid_name('日本', post=True))) + content_type = 'application/x-www-form-urlencoded; charset=%s' + res = app.post('/', params=dict(name='日本'), + headers={'content-type': content_type % 'UTF-8'}) + + # Encoding specified: expect unicode. Shiftjis is the default encoding, but + # params become UTF-8 because the browser specified so + app = TestApp(AssertApp(assertfunc=valid_name(u'日本', post=True, + encoding='shiftjis'))) + res = app.post('/', params=dict(name='日本'), + headers={'content-type': content_type % 'UTF-8'}) + + # Browser did not specify: parse params as the fallback shiftjis + app = TestApp(AssertApp(assertfunc=valid_name(u'日本', post=True, + encoding='shiftjis'))) + res = app.post('/', params=dict(name=u'日本'.encode('shiftjis'))) + +def test_wsgirequest_charset_fileupload(): + def handle_fileupload(environ, start_response): + start_response('200 OK', [('Content-type','text/plain')]) + request = WSGIRequest(environ) + + assert len(request.POST) == 1 + assert isinstance(request.POST.keys()[0], str) + fs = request.POST['thefile'] + assert isinstance(fs, cgi.FieldStorage) + assert isinstance(fs.filename, str) + assert fs.filename == '寿司.txt' + assert fs.value == 'Sushi' + + request.charset = 'UTF-8' + assert len(request.POST) == 1 + assert isinstance(request.POST.keys()[0], str) + fs = request.POST['thefile'] + assert isinstance(fs, cgi.FieldStorage) + assert isinstance(fs.filename, unicode) + assert fs.filename == u'寿司.txt' + assert fs.value == 'Sushi' + + request.charset = None + assert fs.value == 'Sushi' + return [] + + app = TestApp(handle_fileupload) + res = app.post('/', upload_files=[('thefile', '寿司.txt', 'Sushi')]) + +def test_wsgiresponse_charset(): + response = WSGIResponse(mimetype='text/html; charset=UTF-8') + assert response.content_type == 'text/html' + assert response.charset == 'UTF-8' + response.write(u'test') + response.write(u'test2') + response.write('test3') + status, headers, content = response.wsgi_response() + for data in content: + assert isinstance(data, str) + + WSGIResponse.defaults._push_object(dict(content_type='text/html', + charset='iso-8859-1')) + try: + response = WSGIResponse() + response.write(u'test') + response.write(u'test2') + response.write('test3') + status, headers, content = response.wsgi_response() + for data in content: + assert isinstance(data, str) + finally: + WSGIResponse.defaults._pop_object() + + # WSGIResponse will allow unicode to pass through when no charset is + # set + WSGIResponse.defaults._push_object(dict(content_type='text/html', + charset=None)) + try: + response = WSGIResponse(u'test') + response.write(u'test1') + status, headers, content = response.wsgi_response() + for data in content: + assert isinstance(data, unicode) + finally: + WSGIResponse.defaults._pop_object() + + WSGIResponse.defaults._push_object(dict(content_type='text/html', + charset='')) + try: + response = WSGIResponse(u'test') + response.write(u'test1') + status, headers, content = response.wsgi_response() + for data in content: + assert isinstance(data, unicode) + finally: + WSGIResponse.defaults._pop_object() diff --git a/tests/urlparser_data/__init__.py b/tests/urlparser_data/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/tests/urlparser_data/__init__.py @@ -0,0 +1 @@ +# diff --git a/tests/urlparser_data/deep/index.html b/tests/urlparser_data/deep/index.html new file mode 100644 index 0000000..8913442 --- /dev/null +++ b/tests/urlparser_data/deep/index.html @@ -0,0 +1 @@ +index2 diff --git a/tests/urlparser_data/deep/sub/Main.txt b/tests/urlparser_data/deep/sub/Main.txt new file mode 100644 index 0000000..ec42756 --- /dev/null +++ b/tests/urlparser_data/deep/sub/Main.txt @@ -0,0 +1 @@ +index3 diff --git a/tests/urlparser_data/find_file/dir with spaces/test 4.html b/tests/urlparser_data/find_file/dir with spaces/test 4.html new file mode 100644 index 0000000..1121e31 --- /dev/null +++ b/tests/urlparser_data/find_file/dir with spaces/test 4.html @@ -0,0 +1 @@ +test 4 diff --git a/tests/urlparser_data/find_file/index.txt b/tests/urlparser_data/find_file/index.txt new file mode 100644 index 0000000..6be29bc --- /dev/null +++ b/tests/urlparser_data/find_file/index.txt @@ -0,0 +1 @@ +index1 diff --git a/tests/urlparser_data/find_file/test 3.html b/tests/urlparser_data/find_file/test 3.html new file mode 100644 index 0000000..954a536 --- /dev/null +++ b/tests/urlparser_data/find_file/test 3.html @@ -0,0 +1 @@ +test 3 diff --git a/tests/urlparser_data/find_file/test2.html b/tests/urlparser_data/find_file/test2.html new file mode 100644 index 0000000..180cf83 --- /dev/null +++ b/tests/urlparser_data/find_file/test2.html @@ -0,0 +1 @@ +test2 diff --git a/tests/urlparser_data/hook/__init__.py b/tests/urlparser_data/hook/__init__.py new file mode 100644 index 0000000..985a930 --- /dev/null +++ b/tests/urlparser_data/hook/__init__.py @@ -0,0 +1,10 @@ +from paste import request + +def urlparser_hook(environ): + first, rest = request.path_info_split(environ.get('PATH_INFO', '')) + if not first: + # No username + return + environ['app.user'] = first + environ['SCRIPT_NAME'] += '/' + first + environ['PATH_INFO'] = rest diff --git a/tests/urlparser_data/hook/app.py b/tests/urlparser_data/hook/app.py new file mode 100644 index 0000000..d2714e5 --- /dev/null +++ b/tests/urlparser_data/hook/app.py @@ -0,0 +1,5 @@ +def application(environ, start_response): + start_response('200 OK', [('Content-type', 'text/html')]) + return ['user: %s' % environ['app.user']] + + diff --git a/tests/urlparser_data/hook/index.py b/tests/urlparser_data/hook/index.py new file mode 100644 index 0000000..49e89f0 --- /dev/null +++ b/tests/urlparser_data/hook/index.py @@ -0,0 +1,4 @@ +def application(environ, start_response): + start_response('200 OK', [('Content-type', 'text/html')]) + return ['index: %s' % environ['app.user']] + diff --git a/tests/urlparser_data/not_found/__init__.py b/tests/urlparser_data/not_found/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/tests/urlparser_data/not_found/__init__.py @@ -0,0 +1 @@ +# diff --git a/tests/urlparser_data/not_found/recur/__init__.py b/tests/urlparser_data/not_found/recur/__init__.py new file mode 100644 index 0000000..48205a5 --- /dev/null +++ b/tests/urlparser_data/not_found/recur/__init__.py @@ -0,0 +1,9 @@ +def not_found_hook(environ, start_response): + urlparser = environ['paste.urlparser.not_found_parser'] + path = environ.get('PATH_INFO', '') + if not path: + return urlparser.not_found(environ, start_response) + # Strip off leading _'s + path = '/' + path.lstrip('/').lstrip('_') + environ['PATH_INFO'] = path + return urlparser(environ, start_response) diff --git a/tests/urlparser_data/not_found/recur/isfound.txt b/tests/urlparser_data/not_found/recur/isfound.txt new file mode 100644 index 0000000..c8b8fab --- /dev/null +++ b/tests/urlparser_data/not_found/recur/isfound.txt @@ -0,0 +1 @@ +is found diff --git a/tests/urlparser_data/not_found/simple/__init__.py b/tests/urlparser_data/not_found/simple/__init__.py new file mode 100644 index 0000000..f1e7faa --- /dev/null +++ b/tests/urlparser_data/not_found/simple/__init__.py @@ -0,0 +1,3 @@ +def not_found_hook(environ, start_response): + start_response('200 OK', [('Content-type', 'text/plain')]) + return ['not found'] diff --git a/tests/urlparser_data/not_found/simple/found.txt b/tests/urlparser_data/not_found/simple/found.txt new file mode 100644 index 0000000..c8b8fab --- /dev/null +++ b/tests/urlparser_data/not_found/simple/found.txt @@ -0,0 +1 @@ +is found diff --git a/tests/urlparser_data/not_found/user/__init__.py b/tests/urlparser_data/not_found/user/__init__.py new file mode 100644 index 0000000..4126c04 --- /dev/null +++ b/tests/urlparser_data/not_found/user/__init__.py @@ -0,0 +1,12 @@ +from paste import request + +def not_found_hook(environ, start_response): + urlparser = environ['paste.urlparser.not_found_parser'] + first, rest = request.path_info_split(environ.get('PATH_INFO', '')) + if not first: + # No username + return + environ['app.user'] = first + environ['SCRIPT_NAME'] += '/' + first + environ['PATH_INFO'] = rest + return urlparser(environ, start_response) diff --git a/tests/urlparser_data/not_found/user/list.py b/tests/urlparser_data/not_found/user/list.py new file mode 100644 index 0000000..f6228f0 --- /dev/null +++ b/tests/urlparser_data/not_found/user/list.py @@ -0,0 +1,3 @@ +def application(environ, start_response): + start_response('200 OK', [('Content-type', 'text/plain')]) + return ['user: %s' % environ.get('app.user')] diff --git a/tests/urlparser_data/python/__init__.py b/tests/urlparser_data/python/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/tests/urlparser_data/python/__init__.py @@ -0,0 +1 @@ +# diff --git a/tests/urlparser_data/python/simpleapp.py b/tests/urlparser_data/python/simpleapp.py new file mode 100644 index 0000000..cbef9f1 --- /dev/null +++ b/tests/urlparser_data/python/simpleapp.py @@ -0,0 +1,6 @@ +def application(environ, start_response): + start_response('200 OK', [('Content-type', 'text/html'), + ('test-header', 'TEST!')]) + return ['test1'] + + diff --git a/tests/urlparser_data/python/stream.py b/tests/urlparser_data/python/stream.py new file mode 100644 index 0000000..121b4d1 --- /dev/null +++ b/tests/urlparser_data/python/stream.py @@ -0,0 +1,7 @@ +def stream(): + def app(environ, start_response): + writer = start_response('200 OK', [('Content-type', 'text/html')]) + writer('te') + writer('st') + return ['2'] + return app diff --git a/tests/urlparser_data/python/sub/__init__.py b/tests/urlparser_data/python/sub/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/tests/urlparser_data/python/sub/__init__.py @@ -0,0 +1 @@ +# diff --git a/tests/urlparser_data/python/sub/simpleapp.py b/tests/urlparser_data/python/sub/simpleapp.py new file mode 100644 index 0000000..fd90966 --- /dev/null +++ b/tests/urlparser_data/python/sub/simpleapp.py @@ -0,0 +1,6 @@ +def application(environ, start_response): + start_response('200 OK', [('Content-type', 'text/html'), + ('test-header', 'TEST!')]) + return ['subsimple'] + + diff --git a/tests/urlparser_data/secured.txt b/tests/urlparser_data/secured.txt new file mode 100644 index 0000000..72b11b0 --- /dev/null +++ b/tests/urlparser_data/secured.txt @@ -0,0 +1 @@ +secured |