diff options
Diffstat (limited to 'paste/tests')
33 files changed, 1181 insertions, 0 deletions
diff --git a/paste/tests/__init__.py b/paste/tests/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/paste/tests/__init__.py @@ -0,0 +1 @@ +# diff --git a/paste/tests/doctest_webapp.py b/paste/tests/doctest_webapp.py new file mode 100755 index 0000000..b0b786c --- /dev/null +++ b/paste/tests/doctest_webapp.py @@ -0,0 +1,403 @@ +#!/usr/bin/env python2.4 +""" +These are functions for use when doctest-testing a document. +""" + +import subprocess +import doctest +import os +import sys +import shutil +import re +import cgi +import rfc822 +from cStringIO import StringIO +from paste import server +from paste import wsgilib +from paste.util.thirdparty import add_package +add_package('PySourceColor') +import PySourceColor + + +here = os.path.abspath(__file__) +paste_parent = os.path.dirname( + os.path.dirname(os.path.dirname(here))) + +def run(command): + """ + Runs the string command, prints any output. + """ + proc = subprocess.Popen(command, shell=True, + stderr=subprocess.STDOUT, + stdout=subprocess.PIPE, env=_make_env()) + data = proc.stdout.read() + proc.wait() + while data.endswith('\n') or data.endswith('\r'): + data = data[:-1] + if data: + data = '\n'.join( + [l for l in data.splitlines() if l]) + print data + +def _make_env(): + env = os.environ.copy() + env['PATH'] = (env.get('PATH', '') + + ':' + + os.path.join(paste_parent, 'scripts') + + ':' + + os.path.join(paste_parent, 'paste', '3rd-party', + 'sqlobject-files', 'scripts')) + env['PYTHONPATH'] = (env.get('PYTHONPATH', '') + + ':' + + paste_parent) + return env + +def clear_dir(dir): + """ + Clears (deletes) the given directory + """ + shutil.rmtree(dir, True) + +def ls(dir=None, recurse=False, indent=0): + """ + Show a directory listing + """ + dir = dir or os.getcwd() + fns = os.listdir(dir) + fns.sort() + for fn in fns: + full = os.path.join(dir, fn) + if os.path.isdir(full): + fn = fn + '/' + print ' '*indent + fn + if os.path.isdir(full) and recurse: + ls(dir=full, recurse=True, indent=indent+2) + +def make_app(dir): + os.chdir(dir) + sys.path.append(os.path.dirname(dir)) + conf, app = server.load_commandline((), allow_reload=False) + assert conf is not None, ( + "server.load_commandline requested exit with code %r" + % app) + return app + +default_app = None +default_url = None + +def set_default_app(app, url): + global default_app + global default_url + default_app = app + default_url = url + +def resource_filename(fn): + """ + Returns the filename of the resource -- generally in the directory + resources/DocumentName/fn + """ + return os.path.join( + os.path.dirname(sys.testing_document_filename), + 'resources', + os.path.splitext(os.path.basename(sys.testing_document_filename))[0], + fn) + +def show(path_info, example_name): + fn = resource_filename(example_name + '.html') + out = StringIO() + assert default_app is not None, ( + "No default_app set") + url = default_url + path_info + out.write('<span class="doctest-url"><a href="%s">%s</a></span><br>\n' + % (url, url)) + out.write('<div class="doctest-example">\n') + proc = subprocess.Popen( + ['wsgi-server', '--server=console', '--no-verbose', + '--url=' + path_info], + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + env=_make_env()) + stdout, errors = proc.communicate() + stdout = StringIO(stdout) + headers = rfc822.Message(stdout) + content = stdout.read() + for header, value in headers.items(): + if header.lower() == 'status' and int(value.split()[0]) == 200: + continue + if header.lower() in ('content-type', 'content-length'): + continue + if (header.lower() == 'set-cookie' + and value.startswith('_SID_')): + continue + out.write('<span class="doctest-header">%s: %s</span><br>\n' + % (header, value)) + lines = [l for l in content.splitlines() if l.strip()] + for line in lines: + out.write(line + '\n') + if errors: + out.write('<pre class="doctest-errors">%s</pre>' + % errors) + out.write('</div>\n') + result = out.getvalue() + if not os.path.exists(fn): + f = open(fn, 'wb') + f.write(result) + f.close() + else: + f = open(fn, 'rb') + expected = f.read() + f.close() + if not html_matches(expected, result): + print 'Pages did not match. Expected from %s:' % fn + print '-'*60 + print expected + print '='*60 + print 'Actual output:' + print '-'*60 + print result + +def html_matches(pattern, text): + return True + regex = re.escape(pattern) + regex = regex.replace(r'\.\.\.', '.*') + regex = re.sub(r'0x[0-9a-f]+', '.*', regex) + regex = '^%s$' % regex + return re.search(regex, text) + +def create_file(path, version, data): + if data.startswith('\n'): + data = data[1:] + lines = data.splitlines() + new_lines = [] + for line in lines: + if line.rstrip() == '.': + new_lines.append('') + else: + new_lines.append(line) + data = '\n'.join(new_lines) + '\n' + write_data(path, data) + show_file(path, version) + +def show_file(path, version): + ext = os.path.splitext(path)[1] + f = open(path, 'rb') + data = f.read() + f.close() + if ext == '.py': + html = ('<div class="source-code">%s</div>' + % PySourceColor.str2html(data, PySourceColor.dark)) + else: + html = '<pre class="source-code">%s</pre>' % cgi.escape(data, 1) + html = '<span class="source-filename">%s</span><br>%s' % ( + path, html) + write_data(resource_filename('%s.%s.gen.html' % (path, version)), + html) + +def call_source_highlight(input, format): + proc = subprocess.Popen(['source-highlight', '--out-format=html', + '--no-doc', '--css=none', + '--src-lang=%s' % format], shell=False, + stdout=subprocess.PIPE) + stdout, stderr = proc.communicate(input) + result = stdout + proc.wait() + return result + + +def write_data(path, data): + dir = os.path.dirname(os.path.abspath(path)) + if not os.path.exists(dir): + os.makedirs(dir) + f = open(path, 'wb') + f.write(data) + f.close() + + +def change_file(path, changes): + f = open(path, 'rb') + lines = f.readlines() + f.close() + for change_type, line, text in changes: + if change_type == 'insert': + lines[line:line] = [text] + elif change_type == 'delete': + lines[line:text] = [] + else: + assert 0, ( + "Unknown change_type: %r" % change_type) + f = open(path, 'wb') + f.write(''.join(lines)) + f.close() + +class LongFormDocTestParser(doctest.DocTestParser): + + """ + This parser recognizes some reST comments as commands, without + prompts or expected output, like: + + .. run: + + do_this(... + ...) + """ + + _EXAMPLE_RE = re.compile(r""" + # Source consists of a PS1 line followed by zero or more PS2 lines. + (?: (?P<source> + (?:^(?P<indent> [ ]*) >>> .*) # PS1 line + (?:\n [ ]* \.\.\. .*)*) # PS2 lines + \n? + # Want consists of any non-blank lines that do not start with PS1. + (?P<want> (?:(?![ ]*$) # Not a blank line + (?![ ]*>>>) # Not a line starting with PS1 + .*$\n? # But any other line + )*)) + | + (?: # This is for longer commands that are prefixed with a reST + # comment like '.. run:' (two colons makes that a directive). + # These commands cannot have any output. + + (?:^\.\.[ ]*(?P<run>run):[ ]*\n) # Leading command/command + (?:[ ]*\n)? # Blank line following + (?P<runsource> + (?:(?P<runindent> [ ]+)[^ ].*$) + (?:\n [ ]+ .*)*) + ) + | + (?: # This is for shell commands + + (?P<shellsource> + (?:^(P<shellindent> [ ]*) [$] .*) # Shell line + (?:\n [ ]* [>] .*)*) # Continuation + \n? + # Want consists of any non-blank lines that do not start with $ + (?P<shellwant> (?:(?![ ]*$) + (?![ ]*[$]$) + .*$\n? + )*)) + """, re.MULTILINE | re.VERBOSE) + + def _parse_example(self, m, name, lineno): + r""" + Given a regular expression match from `_EXAMPLE_RE` (`m`), + return a pair `(source, want)`, where `source` is the matched + example's source code (with prompts and indentation stripped); + and `want` is the example's expected output (with indentation + stripped). + + `name` is the string's name, and `lineno` is the line number + where the example starts; both are used for error messages. + + >>> def parseit(s): + ... p = LongFormDocTestParser() + ... return p._parse_example(p._EXAMPLE_RE.search(s), '<string>', 1) + >>> parseit('>>> 1\n1') + ('1', {}, '1', None) + >>> parseit('>>> (1\n... +1)\n2') + ('(1\n+1)', {}, '2', None) + >>> parseit('.. run:\n\n test1\n test2\n') + ('test1\ntest2', {}, '', None) + """ + # Get the example's indentation level. + runner = m.group('run') or '' + indent = len(m.group('%sindent' % runner)) + + # Divide source into lines; check that they're properly + # indented; and then strip their indentation & prompts. + source_lines = m.group('%ssource' % runner).split('\n') + if runner: + self._check_prefix(source_lines[1:], ' '*indent, name, lineno) + else: + self._check_prompt_blank(source_lines, indent, name, lineno) + self._check_prefix(source_lines[2:], ' '*indent + '.', name, lineno) + if runner: + source = '\n'.join([sl[indent:] for sl in source_lines]) + else: + source = '\n'.join([sl[indent+4:] for sl in source_lines]) + + if runner: + want = '' + exc_msg = None + else: + # Divide want into lines; check that it's properly indented; and + # then strip the indentation. Spaces before the last newline should + # be preserved, so plain rstrip() isn't good enough. + want = m.group('want') + want_lines = want.split('\n') + if len(want_lines) > 1 and re.match(r' *$', want_lines[-1]): + del want_lines[-1] # forget final newline & spaces after it + self._check_prefix(want_lines, ' '*indent, name, + lineno + len(source_lines)) + want = '\n'.join([wl[indent:] for wl in want_lines]) + + # If `want` contains a traceback message, then extract it. + m = self._EXCEPTION_RE.match(want) + if m: + exc_msg = m.group('msg') + else: + exc_msg = None + + # Extract options from the source. + options = self._find_options(source, name, lineno) + + return source, options, want, exc_msg + + + def parse(self, string, name='<string>'): + """ + Divide the given string into examples and intervening text, + and return them as a list of alternating Examples and strings. + Line numbers for the Examples are 0-based. The optional + argument `name` is a name identifying this string, and is only + used for error messages. + """ + string = string.expandtabs() + # If all lines begin with the same indentation, then strip it. + min_indent = self._min_indent(string) + if min_indent > 0: + string = '\n'.join([l[min_indent:] for l in string.split('\n')]) + + output = [] + charno, lineno = 0, 0 + # Find all doctest examples in the string: + for m in self._EXAMPLE_RE.finditer(string): + # Add the pre-example text to `output`. + output.append(string[charno:m.start()]) + # Update lineno (lines before this example) + lineno += string.count('\n', charno, m.start()) + # Extract info from the regexp match. + (source, options, want, exc_msg) = \ + self._parse_example(m, name, lineno) + # Create an Example, and add it to the list. + if not self._IS_BLANK_OR_COMMENT(source): + # @@: Erg, this is the only line I need to change... + output.append( doctest.Example(source, want, exc_msg, + lineno=lineno, + indent=min_indent+len(m.group('indent') or m.group('runindent')), + options=options) ) + # Update lineno (lines inside this example) + lineno += string.count('\n', m.start(), m.end()) + # Update charno. + charno = m.end() + # Add any remaining post-example text to `output`. + output.append(string[charno:]) + return output + + + +if __name__ == '__main__': + import sys + if sys.argv[1:] and sys.argv[1] == 'doctest': + doctest.testmod() + sys.exit() + if not paste_parent in sys.path: + sys.path.append(paste_parent) + for fn in sys.argv[1:]: + fn = os.path.abspath(fn) + # @@: OK, ick; but this module gets loaded twice + sys.testing_document_filename = fn + doctest.testfile(fn, module_relative=False, + parser=LongFormDocTestParser()) + new = os.path.splitext(fn)[0] + '.html' + assert new != fn + os.system('rest2html %s > %s' % (fn, new)) diff --git a/paste/tests/echotest.py b/paste/tests/echotest.py new file mode 100755 index 0000000..885d571 --- /dev/null +++ b/paste/tests/echotest.py @@ -0,0 +1,109 @@ +""" +Tests a WSGI stack, using urllib. Queries the echo application. +""" + +import unittest +import urlparse +import urllib +import os +import sys + +class EchoTest(unittest.TestCase): + + def url(self): + if not os.environ.get('ECHO_URL'): + print 'You must set $ECHO_URL' + sys.exit(1) + url = URL(os.environ['ECHO_URL']) + return url + +class TestEnviron(EchoTest): + + def setUp(self): + self.page = self.url().fetch(environ='true') + self.environ = parse_environ(self.page) + + def testRequiredKeys(self): + environ = self.environ + url = self.url() + required_keys = 'REQUEST_METHOD SCRIPT_NAME PATH_INFO QUERY_STRING SERVER_NAME SERVER_PORT wsgi.errors wsgi.input wsgi.multiprocess wsgi.multithread wsgi.version' + for key in required_keys.split(): + assert environ.has_key(key), "Key %r missing from %r" % (key, environ) + self.assertEqual(environ['PATH_INFO'], '') + self.assertEqual(environ['SCRIPT_NAME'], url.path) + self.assertEqual(environ['REQUEST_METHOD'], 'GET') + self.assertEqual(environ['QUERY_STRING'], 'environ=true') + self.assertEqual(environ['SERVER_PORT'], str(url.port)) + self.assertEqual(environ['SERVER_NAME'], url.host) + self.assertEqual(environ['HTTP_HOST'], url.location) + assert environ['HTTP_USER_AGENT'].startswith('Python-urllib/'), \ + "HTTP_USER_AGENT should start with 'Python-urllib/': %r" % environ['HTTP_USER_AGENT'] + + def testPathInfo(self): + sub = self.url() / '' + environ = parse_environ(sub.fetch(environ='true')) + self.assertEqual(environ['PATH_INFO'], '/') + self.assertEqual(environ['SCRIPT_NAME'], self.url().path) + sub = self.url() / 'test' + environ = parse_environ(sub.fetch(environ='true')) + self.assertEqual(environ['PATH_INFO'], '/test') + self.assertEqual(environ['SCRIPT_NAME'], self.url().path) + + def test_message(self): + data = self.url().fetch(message='test') + self.assertEqual(data, 'test') + data = self.url().fetch(message='') + self.assertEqual(data, '') + +############################################################ +## Utility functions +############################################################ + +def parse_environ(page): + """ + Parses the environment that echo prints (not perfect, but good + enough). + """ + environ = {} + for line in page.splitlines(): + if '=' not in line: + # ignore second line of long lines + continue + name, value = line.split('=', 1) + environ[name] = value + return environ + +class URL: + + def __init__(self, url_string): + self.url_string = url_string + (self.scheme, self.location, self.path, self.query, + self.fragment) = urlparse.urlsplit(url_string) + if ':' in self.location: + self.host, self.port = location.split(':', 1) + else: + self.host = self.location + if self.scheme == 'http': + self.port = '80' + elif self.scheme == 'https': + self.port = '443' + else: + assert 0, "Unknown scheme: %r" % scheme + self.port = int(self.port) + + def fetch(self, **kw): + query = '&'.join(['%s=%s' % (urllib.quote(k), urllib.quote(v)) + for k, v in kw.items()]) + url = self.url_string + if query: + url += '?' + query + f = urllib.urlopen(url) + page = f.read() + f.close() + return page + + def __div__(self, path_part): + return self.__class__(self.url_string + '/' + path_part) + +if __name__ == '__main__': + unittest.main() diff --git a/paste/tests/fixture.py b/paste/tests/fixture.py new file mode 100644 index 0000000..b69c88c --- /dev/null +++ b/paste/tests/fixture.py @@ -0,0 +1,307 @@ +import sys +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO +import types +import re +from py.test.collect import Module, PyCollector +from paste.util import thirdparty +doctest = thirdparty.load_new_module('doctest', (2, 4)) +from paste import wsgilib +from paste import lint + +class NoDefault: + pass + +class Dummy(object): + + def __init__(self, **kw): + for name, value in kw.items(): + if name.startswith('method_'): + name = name[len('method_'):] + value = DummyMethod(value) + setattr(self, name, value) + +class DummyMethod(object): + + def __init__(self, return_value): + self.return_value = return_value + + def __call__(self, *args, **kw): + return self.return_value + +class ParamCollector(PyCollector): + + def collect_function(self, extpy): + if not extpy.check(func=1, basestarts='test_'): + return + func = extpy.resolve() + if hasattr(func, 'params'): + params = func.params + for i, param in enumerate(params): + item = self.Item(extpy, *param) + item.name = item.name + '.%i' % i + yield item + else: + yield self.Item(extpy) + +class DoctestCollector(PyCollector): + + def __init__(self, extpy_or_module): + if isinstance(extpy_or_module, types.ModuleType): + self.module = extpy_or_module + self.extpy = None + else: + self.extpy = extpy_or_module + self.module = self.extpy.getpymodule() + + def __call__(self, extpy): + # we throw it away, because this has been set up to explicitly + # check another module; maybe this isn't clean + if self.extpy is None: + self.extpy = extpy + return self + + def __iter__(self): + finder = doctest.DocTestFinder() + tests = finder.find(self.module) + for t in tests: + yield DoctestItem(self.extpy, t) + +class DoctestItem(DoctestCollector.Item): + + def __init__(self, extpy, doctestitem, *args): + self.extpy = extpy + self.doctestitem = doctestitem + self.name = extpy.basename + self.args = args + + def execute(self, driver): + runner = doctest.DocTestRunner() + driver.setup_path(self.extpy) + target, teardown = driver.setup_method(self.extpy) + try: + (failed, tried), run_output = capture_stdout(runner.run, self.doctestitem) + if failed: + raise self.Failed(msg=run_output, tbindex=-2) + + finally: + if teardown: + teardown(target) + +def capture_stdout(func, *args, **kw): + newstdout = StringIO() + oldstdout = sys.stdout + sys.stdout = newstdout + try: + result = func(*args, **kw) + finally: + sys.stdout = oldstdout + return result, newstdout.getvalue() + +def assert_error(func, *args, **kw): + kw.setdefault('error', Exception) + kw.setdefault('text_re', None) + error = kw.pop('error') + text_re = kw.pop('text_re') + if text_re and isinstance(text_re, str): + real_text_re = re.compile(text_re, re.S) + else: + real_text_re = text_re + try: + value = func(*args, **kw) + except error, e: + if real_text_re and not real_text_re.search(str(e)): + assert False, ( + "Exception did not match pattern; exception:\n %r;\n" + "pattern:\n %r" + % (str(e), text_re)) + except Exception, e: + assert False, ( + "Exception type %s should have been raised; got %s instead (%s)" + % (error, e.__class__, e)) + else: + assert False, ( + "Exception was expected, instead successfully returned %r" + % (value)) + +def sorted(l): + l = list(l) + l.sort() + return l + + +def fake_request(application, path_info='', use_lint=True, **environ): + """ + Runs the application in a fake environment, returning a response object + """ + if use_lint: + application = lint.middleware(application) + status, headers, body, errors = wsgilib.raw_interactive( + application, path_info, **environ) + res = FakeResponse(status, headers, body, errors) + if res.errors: + print 'Errors:' + print res.errors + return res + +class FakeResponse(object): + + def __init__(self, status, headers, body, errors): + self.status = status + self.headers = headers + self.body = body + self.errors = errors + + def status_int__get(self): + return int(self.status.split()[0]) + status_int = property(status_int__get) + + def all_ok(self): + """ + Asserts that there were no errors and the status was 200 OK + """ + assert not self.errors, ( + "Response had errors: %s" % self.errors) + assert self.status_int == 200, ( + "Response did not return 200 OK: %r" % self.status) + + def header(self, name, default=NoDefault): + """ + Returns the named header; an error if there is not exactly one + matching header (unless you give a default -- always an error if + there is more than one header) + """ + found = None + for cur_name, value in self.headers: + if cur_name.lower() == name.lower(): + assert not found, ( + "Ambiguous header: %s matches %r and %r" + % (name, found, value)) + found = value + if found is None: + if default is NoDefault: + raise KeyError( + "No header found: %r (from %s)" + % (name, ', '.join([n for n, v in self.headers]))) + else: + return default + return found + + def all_headers(self, name): + """ + Gets all headers, returns as a list + """ + found = [] + for cur_name, value in self.headers: + if cur_name.lower() == name.lower(): + found.append(value) + return found + + def __contains__(self, s): + return self.body.find(s) != -1 + + def __repr__(self): + return '<Response %s %r>' % (self.status, self.body[:20]) + + def __str__(self): + return 'Response: %s\n%s\n%s' % ( + self.status, + '\n'.join(['%s: %s' % (n, v) for n, v in self.headers]), + self.body) + +class Dummy_smtplib(object): + + existing = None + + def __init__(self, server): + assert not self.existing, ( + "smtplib.SMTP() called again before Dummy_smtplib.existing.reset() " + "called.") + self.server = server + self.open = True + self.__class__.existing = self + + def quit(self): + assert self.open, ( + "Called %s.quit() twice" % self) + self.open = False + + def sendmail(self, from_address, to_addresses, msg): + self.from_address = from_address + self.to_addresses = to_addresses + self.message = msg + + def install(cls): + smtplib.SMTP = cls + + install = classmethod(install) + + def reset(self): + assert not self.open, ( + "SMTP connection not quit") + self.__class__.existing = None + +class FakeFilesystem(object): + + def __init__(self): + self.files = {} + + def make_file(self, filename, content): + self.files[filename] = content + + def open(self, filename, mode='r'): + if not self.files.has_key(filename): + raise IOError("[FakeFS] No such file or directory: %r" % filename) + + +class FakeFile(object): + + def __init__(self, filename, content=None): + self.filename = filename + self.content = content + + def open(self, mode): + if mode == 'r' or mode == 'rb': + if self.content is None: + raise IOError("[FakeFS] No such file or directory: %r" % filename) + return ReaderFile(self) + elif mode == 'w' or mode == 'wb': + return WriterFile(self) + else: + assert 0, "Mode %r not yet implemented" % mode + +class ReaderFile(object): + + def __init__(self, file): + self.file = file + self.stream = StringIO(self.file.content) + self.open = True + + def read(self, *args): + return self.stream.read(*args) + + def close(self): + assert self.open, ( + "Closing open file") + self.open = False + +class WriterFile(object): + + def __init__(self, file): + self.file = file + self.stream = StringIO() + self.open = True + + def write(self, arg): + self.stream.write(arg) + + def close(self): + assert self.open, ( + "Closing an open file") + self.open = False + + + + diff --git a/paste/tests/pyconfig_data/context.py b/paste/tests/pyconfig_data/context.py new file mode 100644 index 0000000..73fa626 --- /dev/null +++ b/paste/tests/pyconfig_data/context.py @@ -0,0 +1,8 @@ +def simplehook(): + return 'calc' +simplehook.config_hook = True + +def complexhook(context): + return context +complexhook.config_hook = True + diff --git a/paste/tests/pyconfig_data/deriv.conf b/paste/tests/pyconfig_data/deriv.conf new file mode 100644 index 0000000..c7cdf3c --- /dev/null +++ b/paste/tests/pyconfig_data/deriv.conf @@ -0,0 +1,3 @@ +import os + +test1 = test1 + '+another' diff --git a/paste/tests/pyconfig_data/nest1.conf b/paste/tests/pyconfig_data/nest1.conf new file mode 100644 index 0000000..4eb06c6 --- /dev/null +++ b/paste/tests/pyconfig_data/nest1.conf @@ -0,0 +1,3 @@ +a = 1 +b = 'shadow' + diff --git a/paste/tests/pyconfig_data/nest2.conf b/paste/tests/pyconfig_data/nest2.conf new file mode 100644 index 0000000..c70b217 --- /dev/null +++ b/paste/tests/pyconfig_data/nest2.conf @@ -0,0 +1,2 @@ +b = 2 +c = 3 diff --git a/paste/tests/pyconfig_data/one.py b/paste/tests/pyconfig_data/one.py new file mode 100644 index 0000000..e03cf8b --- /dev/null +++ b/paste/tests/pyconfig_data/one.py @@ -0,0 +1,2 @@ +name1 = 'n1' +name2 = 'n2' diff --git a/paste/tests/test_authentication.py b/paste/tests/test_authentication.py new file mode 100644 index 0000000..f26e5a4 --- /dev/null +++ b/paste/tests/test_authentication.py @@ -0,0 +1,76 @@ +from paste import wsgilib +from paste import login +from fixture import * + +from_cmdline = 0 + +def application(environ, start_response): + if environ.has_key('REMOTE_USER'): + start_response('200 OK', [('Content-type', 'text/plain')]) + return ['Logged in: ' + environ['REMOTE_USER']] + else: + start_response('401 Unauthorized', + [('Content-type', 'text/plain')]) + return ['Not logged in.'] + +class AuthTest(login.Authenticator): + def check_auth(self, username, password): + return username == password + +def report(res): + if from_cmdline: + print res + +# @@ this should be part of a test fixture, I think +def mk_basic_auth_app(**kw): + kw['http_login'] = True + kw['authenticator'] = AuthTest + app = login.middleware(application, **kw) + return app + +def test_basicauth_noauth(): + res = fake_request(mk_basic_auth_app(), '/') + assert res.status_int == 401 + report(res) + +def run_userpass(user, password): + userpass = user + ':' + password + env = {'HTTP_AUTHORIZATION' : 'Basic ' + userpass.encode('base64')} + return fake_request(mk_basic_auth_app(), '/', **env) + +def test_basicauth_okuser(): + res = run_userpass('test', 'test') # should succeed + assert res.status_int == 200 + report(res) + +def test_basicauth_baduser(): + res = run_userpass('test', 'badpass') # should succeed + assert res.status_int == 401 + report(res) + +def test_basicauth_cookie(): + res = run_userpass('test', 'test') # should succeed + assert res.status_int == 200 + report(res) + cookie_val = res.header('SET-COOKIE') + print "cookie value", cookie_val + app = mk_basic_auth_app() + env = {'HTTP_COOKIE': cookie_val} + res = fake_request(mk_basic_auth_app(), '/', **env) + report(res) + assert res.status_int == 200 + + # ensure that secret is actually used + res = fake_request(mk_basic_auth_app(secret='bogus'), + '/', **env) + report(res) + assert res.status_int == 401 + +if __name__ == '__main__': + from_cmdline = 1 + test_basicauth_noauth() + test_basicauth_okuser() + test_basicauth_baduser() + test_basicauth_cookie() + + diff --git a/paste/tests/test_error_middleware.py b/paste/tests/test_error_middleware.py new file mode 100644 index 0000000..f7731ae --- /dev/null +++ b/paste/tests/test_error_middleware.py @@ -0,0 +1,63 @@ +from fixture import * +from paste.error_middleware import ErrorMiddleware +from paste import lint + +def do_request(app, expect_status=500): + res = fake_request(ErrorMiddleware(lint.middleware(app)), + **{'paste.config': {'debug': True}}) + assert res.status_int == expect_status + return res + +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 + +def test_makes_exception(): + res = do_request(bad_app) + print res + assert '<html' in 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 'error_middleware.py' in res + +def test_start_res(): + res = do_request(start_response_app) + print res + assert 'ValueError: hi' in res + assert 'test_error_middleware.py' in res + assert 'line 17 in <tt>start_response_app</tt>' in res + +def test_after_start(): + res = do_request(after_start_response_app, 200) + print res + assert 'ValueError: error2' in res + assert 'line 21' 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/paste/tests/test_pyconfig.py b/paste/tests/test_pyconfig.py new file mode 100644 index 0000000..1b93d02 --- /dev/null +++ b/paste/tests/test_pyconfig.py @@ -0,0 +1,46 @@ +import os +from wsgikit import pyconfig +from py.test import raises + +def path(name): + return os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'pyconfig_data', name) + +def test_load(): + conf = pyconfig.load(path('one.py')) + assert conf['name1'] == 'n1' + assert conf['name2'] == "n2" + raises(KeyError, "conf['name3']") + +def test_nest(): + conf = pyconfig.load(path('nest1.conf')) + conf.load(path('nest2.conf')) + assert conf['a'] == 1 + assert conf['b'] == 2 + assert conf['c'] == 3 + +def test_derivative(): + conf = pyconfig.Config() + conf.load_dict({'test1': 'a'}) + assert conf['test1'] == 'a' + conf.load(path('deriv.conf')) + assert conf['test1'] == 'a+another' + conf = pyconfig.Config() + conf.load_dict({'test1': 'b'}) + conf.load(path('deriv.conf')) + assert conf['test1'] == 'b+another' + assert not conf.has_key('os') + +def test_command(): + conf = pyconfig.load(path('one.py')) + extra = conf.load_commandline( + ['-h', '--host', 'localhost', '--port=8080', 'arg1', '-f', 'arg2'], + bool_options=['help', 'verbose'], + aliases={'f': 'config_file', 'h': 'help', 'v': 'verbose'}) + assert extra == ['arg1'] + assert conf['name1'] == 'n1' + assert conf['host'] == 'localhost' + assert conf['port'] == 8080 + assert conf['config_file'] == 'arg2' + raises(KeyError, "conf['h']") + raises(KeyError, "conf['f']") diff --git a/paste/tests/test_urlparser.py b/paste/tests/test_urlparser.py new file mode 100644 index 0000000..b41ecfa --- /dev/null +++ b/paste/tests/test_urlparser.py @@ -0,0 +1,83 @@ +from paste.urlparser import * +from fixture import fake_request + + +def path(name): + return os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'urlparser_data', name) + +def make_parser(name): + return URLParser(path(name), name, {'index_names': ['index', 'Main']}) + +def test_find_file(): + p = make_parser('find_file') + res = fake_request(p, '/') + assert 'index1' in res + assert res.header('content-type') == 'text/plain' + res = fake_request(p, '/index') + assert 'index1' in res + assert res.header('content-type') == 'text/plain' + res = fake_request(p, '/index.txt') + assert 'index1' in res + assert res.header('content-type') == 'text/plain' + res = fake_request(p, '/test2.html') + assert 'test2' in res + assert res.header('content-type') == 'text/html' + +def test_deep(): + p = make_parser('deep') + res = fake_request(p, '/') + assert 'index2' in res + res = fake_request(p, '/sub') + assert res.status_int == 301 + print res + assert res.header('location') == 'http://localhost/sub/' + assert 'href="http://localhost/sub/"' in res + res = fake_request(p, '/sub/') + assert 'index3' in res + +def test_python(): + p = make_parser('python') + res = fake_request(p, '/simpleapp') + res.all_ok() + assert 'test1' in res + assert res.header('test-header') == 'TEST!' + assert res.header('content-type') == 'text/html' + res = fake_request(p, '/stream') + res.all_ok() + assert 'test2' in res + res = fake_request(p, '/sub/simpleapp') + res.all_ok() + assert 'subsimple' in res + +def test_hook(): + p = make_parser('hook') + res = fake_request(p, '/bob/app') + res.all_ok() + assert 'user: bob' in res + res = fake_request(p, '/tim/') + res.all_ok() + assert 'index: tim' in res + +def test_not_found_hook(): + p = make_parser('not_found') + res = fake_request(p, '/simple/notfound') + assert res.status_int == 200 + assert 'not found' in res + res = fake_request(p, '/simple/found') + res.all_ok() + assert 'is found' in res + res = fake_request(p, '/recur/__notfound') + assert res.status_int == 404 + # @@: It's unfortunate that the original path doesn't actually show up + assert '/recur/notfound' in res + res = fake_request(p, '/recur/__isfound') + assert res.status_int == 200 + assert 'is found' in res + res = fake_request(p, '/user/list') + res.all_ok() + assert 'user: None' in res + res = fake_request(p, '/user/bob/list') + assert res.status_int == 200 + assert 'user: bob' in res + diff --git a/paste/tests/urlparser_data/__init__.py b/paste/tests/urlparser_data/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/paste/tests/urlparser_data/__init__.py @@ -0,0 +1 @@ +# diff --git a/paste/tests/urlparser_data/deep/index.html b/paste/tests/urlparser_data/deep/index.html new file mode 100644 index 0000000..8913442 --- /dev/null +++ b/paste/tests/urlparser_data/deep/index.html @@ -0,0 +1 @@ +index2 diff --git a/paste/tests/urlparser_data/deep/sub/Main.txt b/paste/tests/urlparser_data/deep/sub/Main.txt new file mode 100644 index 0000000..ec42756 --- /dev/null +++ b/paste/tests/urlparser_data/deep/sub/Main.txt @@ -0,0 +1 @@ +index3 diff --git a/paste/tests/urlparser_data/find_file/index.txt b/paste/tests/urlparser_data/find_file/index.txt new file mode 100644 index 0000000..6be29bc --- /dev/null +++ b/paste/tests/urlparser_data/find_file/index.txt @@ -0,0 +1 @@ +index1 diff --git a/paste/tests/urlparser_data/find_file/test2.html b/paste/tests/urlparser_data/find_file/test2.html new file mode 100644 index 0000000..180cf83 --- /dev/null +++ b/paste/tests/urlparser_data/find_file/test2.html @@ -0,0 +1 @@ +test2 diff --git a/paste/tests/urlparser_data/hook/__init__.py b/paste/tests/urlparser_data/hook/__init__.py new file mode 100644 index 0000000..9b1055c --- /dev/null +++ b/paste/tests/urlparser_data/hook/__init__.py @@ -0,0 +1,10 @@ +from paste import wsgilib + +def urlparser_hook(environ): + first, rest = wsgilib.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/paste/tests/urlparser_data/hook/app.py b/paste/tests/urlparser_data/hook/app.py new file mode 100644 index 0000000..d2714e5 --- /dev/null +++ b/paste/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/paste/tests/urlparser_data/hook/index.py b/paste/tests/urlparser_data/hook/index.py new file mode 100644 index 0000000..49e89f0 --- /dev/null +++ b/paste/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/paste/tests/urlparser_data/not_found/__init__.py b/paste/tests/urlparser_data/not_found/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/paste/tests/urlparser_data/not_found/__init__.py @@ -0,0 +1 @@ +# diff --git a/paste/tests/urlparser_data/not_found/recur/__init__.py b/paste/tests/urlparser_data/not_found/recur/__init__.py new file mode 100644 index 0000000..48205a5 --- /dev/null +++ b/paste/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/paste/tests/urlparser_data/not_found/recur/isfound.txt b/paste/tests/urlparser_data/not_found/recur/isfound.txt new file mode 100644 index 0000000..c8b8fab --- /dev/null +++ b/paste/tests/urlparser_data/not_found/recur/isfound.txt @@ -0,0 +1 @@ +is found diff --git a/paste/tests/urlparser_data/not_found/simple/__init__.py b/paste/tests/urlparser_data/not_found/simple/__init__.py new file mode 100644 index 0000000..f1e7faa --- /dev/null +++ b/paste/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/paste/tests/urlparser_data/not_found/simple/found.txt b/paste/tests/urlparser_data/not_found/simple/found.txt new file mode 100644 index 0000000..c8b8fab --- /dev/null +++ b/paste/tests/urlparser_data/not_found/simple/found.txt @@ -0,0 +1 @@ +is found diff --git a/paste/tests/urlparser_data/not_found/user/__init__.py b/paste/tests/urlparser_data/not_found/user/__init__.py new file mode 100644 index 0000000..c47f88e --- /dev/null +++ b/paste/tests/urlparser_data/not_found/user/__init__.py @@ -0,0 +1,12 @@ +from paste import wsgilib + +def not_found_hook(environ, start_response): + urlparser = environ['paste.urlparser.not_found_parser'] + first, rest = wsgilib.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/paste/tests/urlparser_data/not_found/user/list.py b/paste/tests/urlparser_data/not_found/user/list.py new file mode 100644 index 0000000..f6228f0 --- /dev/null +++ b/paste/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/paste/tests/urlparser_data/python/__init__.py b/paste/tests/urlparser_data/python/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/paste/tests/urlparser_data/python/__init__.py @@ -0,0 +1 @@ +# diff --git a/paste/tests/urlparser_data/python/simpleapp.py b/paste/tests/urlparser_data/python/simpleapp.py new file mode 100644 index 0000000..cbef9f1 --- /dev/null +++ b/paste/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/paste/tests/urlparser_data/python/stream.py b/paste/tests/urlparser_data/python/stream.py new file mode 100644 index 0000000..121b4d1 --- /dev/null +++ b/paste/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/paste/tests/urlparser_data/python/sub/__init__.py b/paste/tests/urlparser_data/python/sub/__init__.py new file mode 100644 index 0000000..792d600 --- /dev/null +++ b/paste/tests/urlparser_data/python/sub/__init__.py @@ -0,0 +1 @@ +# diff --git a/paste/tests/urlparser_data/python/sub/simpleapp.py b/paste/tests/urlparser_data/python/sub/simpleapp.py new file mode 100644 index 0000000..fd90966 --- /dev/null +++ b/paste/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'] + + |
