Commit 2f444231 authored by Hanno Schlichting's avatar Hanno Schlichting

Move `ZPublisher.Publish` module into ZServer distribution.

Change Testing to use the WSGI publisher for functional and testbrowser
based tests incl. functional doctests. Alternatives are available
in `ZServer.Testing`.
parent c92eb929
...@@ -31,6 +31,12 @@ Features Added ...@@ -31,6 +31,12 @@ Features Added
Restructuring Restructuring
+++++++++++++ +++++++++++++
- Change Testing to use the WSGI publisher for functional and testbrowser
based tests incl. functional doctests. Alternatives are available
in `ZServer.Testing`.
- Move `ZPublisher.Publish` module into ZServer distribution.
- Remove `Globals` package, opened database are now found in - Remove `Globals` package, opened database are now found in
`Zope2.opened` next to `Zope2.DB`. `Zope2.opened` next to `Zope2.DB`.
......
...@@ -16,14 +16,16 @@ After Marius Gedminas' functional.py module for Zope3. ...@@ -16,14 +16,16 @@ After Marius Gedminas' functional.py module for Zope3.
""" """
import base64 import base64
import re from functools import partial
import sys import sys
import transaction import transaction
import sandbox
import interfaces
from zope.interface import implements from zope.interface import implements
from Testing.ZopeTestCase import interfaces
from Testing.ZopeTestCase import sandbox
from Zope2.Startup.httpexceptions import HTTPExceptionHandler
def savestate(func): def savestate(func):
'''Decorator saving thread local state before executing func '''Decorator saving thread local state before executing func
...@@ -61,8 +63,10 @@ class Functional(sandbox.Sandboxed): ...@@ -61,8 +63,10 @@ class Functional(sandbox.Sandboxed):
from StringIO import StringIO from StringIO import StringIO
from ZPublisher.HTTPRequest import HTTPRequest as Request from ZPublisher.HTTPRequest import HTTPRequest as Request
from ZPublisher.HTTPResponse import HTTPResponse as Response from ZPublisher.WSGIPublisher import (
from ZPublisher.Publish import publish_module publish_module,
WSGIResponse,
)
# Commit the sandbox for good measure # Commit the sandbox for good measure
transaction.commit() transaction.commit()
...@@ -76,6 +80,7 @@ class Functional(sandbox.Sandboxed): ...@@ -76,6 +80,7 @@ class Functional(sandbox.Sandboxed):
env['SERVER_NAME'] = request['SERVER_NAME'] env['SERVER_NAME'] = request['SERVER_NAME']
env['SERVER_PORT'] = request['SERVER_PORT'] env['SERVER_PORT'] = request['SERVER_PORT']
env['SERVER_PROTOCOL'] = 'HTTP/1.1'
env['REQUEST_METHOD'] = request_method env['REQUEST_METHOD'] = request_method
p = path.split('?') p = path.split('?')
...@@ -93,42 +98,54 @@ class Functional(sandbox.Sandboxed): ...@@ -93,42 +98,54 @@ class Functional(sandbox.Sandboxed):
stdin = StringIO() stdin = StringIO()
outstream = StringIO() outstream = StringIO()
response = Response(stdout=outstream, stderr=sys.stderr) response = WSGIResponse(stdout=outstream, stderr=sys.stderr)
request = Request(stdin, env, response) request = Request(stdin, env, response)
request.retry_max_count = 0
for k, v in extra.items(): for k, v in extra.items():
request[k] = v request[k] = v
publish_module('Zope2', wsgi_headers = StringIO()
debug=not handle_errors,
request=request,
response=response)
return ResponseWrapper(response, outstream, path) def start_response(status, headers):
wsgi_headers.write('HTTP/1.1 %s\r\n' % status)
headers = '\r\n'.join([': '.join(x) for x in headers])
wsgi_headers.write(headers)
wsgi_headers.write('\r\n\r\n')
publish = partial(publish_module, _request=request, _response=response)
if handle_errors:
publish = HTTPExceptionHandler(publish)
class ResponseWrapper: wsgi_result = publish(env, start_response)
'''Decorates a response object with additional introspective methods.'''
return ResponseWrapper(response, outstream, path,
wsgi_result, wsgi_headers)
_bodyre = re.compile('\r\n\r\n(.*)', re.MULTILINE | re.DOTALL)
def __init__(self, response, outstream, path): class ResponseWrapper(object):
'''Decorates a response object with additional introspective methods.'''
def __init__(self, response, outstream, path,
wsgi_result=(), wsgi_headers=''):
self._response = response self._response = response
self._outstream = outstream self._outstream = outstream
self._path = path self._path = path
self._wsgi_result = wsgi_result
self._wsgi_headers = wsgi_headers
def __getattr__(self, name): def __getattr__(self, name):
return getattr(self._response, name) return getattr(self._response, name)
def __str__(self):
return self.getOutput()
def getOutput(self): def getOutput(self):
'''Returns the complete output, headers and all.''' '''Returns the complete output, headers and all.'''
return self._outstream.getvalue() return self._wsgi_headers.getvalue() + self.getBody()
def getBody(self): def getBody(self):
'''Returns the page body, i.e. the output par headers.''' '''Returns the page body, i.e. the output par headers.'''
body = self._bodyre.search(self.getOutput()) return ''.join(self._wsgi_result)
if body is not None:
body = body.group(1)
return body
def getPath(self): def getPath(self):
'''Returns the path used by the request.''' '''Returns the path used by the request.'''
......
...@@ -100,7 +100,7 @@ Test Unauthorized ...@@ -100,7 +100,7 @@ Test Unauthorized
... """, handle_errors=True)) ... """, handle_errors=True))
HTTP/1.1 401 Unauthorized HTTP/1.1 401 Unauthorized
... ...
Www-Authenticate: basic realm=... WWW-Authenticate: basic realm=...
Test Basic Authentication Test Basic Authentication
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
import base64 import base64
import doctest import doctest
from functools import partial
import re import re
import sys import sys
import warnings import warnings
...@@ -32,6 +33,7 @@ from Testing.ZopeTestCase import standard_permissions ...@@ -32,6 +33,7 @@ from Testing.ZopeTestCase import standard_permissions
from Testing.ZopeTestCase.sandbox import AppZapper from Testing.ZopeTestCase.sandbox import AppZapper
from Testing.ZopeTestCase.functional import ResponseWrapper from Testing.ZopeTestCase.functional import ResponseWrapper
from Testing.ZopeTestCase.functional import savestate from Testing.ZopeTestCase.functional import savestate
from Zope2.Startup.httpexceptions import HTTPExceptionHandler
if sys.version_info >= (3, ): if sys.version_info >= (3, ):
basestring = str basestring = str
...@@ -82,16 +84,12 @@ class DocResponseWrapper(ResponseWrapper): ...@@ -82,16 +84,12 @@ class DocResponseWrapper(ResponseWrapper):
"""Response Wrapper for use in doctests """Response Wrapper for use in doctests
""" """
def __init__(self, response, outstream, path, header_output): def __init__(self, response, outstream, path, header_output,
ResponseWrapper.__init__(self, response, outstream, path) wsgi_result=(), wsgi_headers=''):
ResponseWrapper.__init__(self, response, outstream, path,
wsgi_result, wsgi_headers)
self.header_output = header_output self.header_output = header_output
def __str__(self):
body = self.getBody()
if body:
return "%s\n\n%s" % (self.header_output, body)
return "%s\n" % (self.header_output)
basicre = re.compile('Basic (.+)?:(.+)?$') basicre = re.compile('Basic (.+)?:(.+)?$')
headerre = re.compile('(\S+): (.+)$') headerre = re.compile('(\S+): (.+)$')
...@@ -131,8 +129,11 @@ def http(request_string, handle_errors=True): ...@@ -131,8 +129,11 @@ def http(request_string, handle_errors=True):
import urllib import urllib
import rfc822 import rfc822
from cStringIO import StringIO from cStringIO import StringIO
from ZPublisher.HTTPResponse import HTTPResponse as Response from ZPublisher.HTTPRequest import HTTPRequest as Request
from ZPublisher.Publish import publish_module from ZPublisher.WSGIPublisher import (
publish_module,
WSGIResponse,
)
# Commit work done by previous python code. # Commit work done by previous python code.
transaction.commit() transaction.commit()
...@@ -185,13 +186,24 @@ def http(request_string, handle_errors=True): ...@@ -185,13 +186,24 @@ def http(request_string, handle_errors=True):
env['HTTP_AUTHORIZATION'] = auth_header(env['HTTP_AUTHORIZATION']) env['HTTP_AUTHORIZATION'] = auth_header(env['HTTP_AUTHORIZATION'])
outstream = StringIO() outstream = StringIO()
response = Response(stdout=outstream, stderr=sys.stderr) response = WSGIResponse(stdout=outstream, stderr=sys.stderr)
request = Request(instream, env, response)
request.retry_max_count = 0
env['wsgi.input'] = instream
wsgi_headers = StringIO()
def start_response(status, headers):
wsgi_headers.write('HTTP/1.1 %s\r\n' % status)
headers = '\r\n'.join([': '.join(x) for x in headers])
wsgi_headers.write(headers)
wsgi_headers.write('\r\n\r\n')
publish = partial(publish_module, _request=request, _response=response)
if handle_errors:
publish = HTTPExceptionHandler(publish)
publish_module('Zope2', wsgi_result = publish(env, start_response)
response=response,
stdin=instream,
environ=env,
debug=not handle_errors)
header_output.setResponseStatus(response.getStatus(), response.errmsg) header_output.setResponseStatus(response.getStatus(), response.errmsg)
header_output.setResponseHeaders(response.headers) header_output.setResponseHeaders(response.headers)
...@@ -200,7 +212,8 @@ def http(request_string, handle_errors=True): ...@@ -200,7 +212,8 @@ def http(request_string, handle_errors=True):
sync() sync()
return DocResponseWrapper(response, outstream, path, header_output) return DocResponseWrapper(
response, outstream, path, header_output, wsgi_result, wsgi_headers)
class ZopeSuiteFactory: class ZopeSuiteFactory:
......
...@@ -32,6 +32,7 @@ class PublisherConnection(object): ...@@ -32,6 +32,7 @@ class PublisherConnection(object):
def __init__(self, host, timeout=None): def __init__(self, host, timeout=None):
self.caller = functional.http self.caller = functional.http
self.host = host self.host = host
self.response = None
def set_debuglevel(self, level): def set_debuglevel(self, level):
pass pass
...@@ -79,35 +80,20 @@ class PublisherConnection(object): ...@@ -79,35 +80,20 @@ class PublisherConnection(object):
def getresponse(self): def getresponse(self):
"""Return a ``urllib2`` compatible response. """Return a ``urllib2`` compatible response.
The goal of ths method is to convert the Zope Publisher's reseponse to The goal of ths method is to convert the Zope Publisher's response to
a ``urllib2`` compatible response, which is also understood by a ``urllib2`` compatible response, which is also understood by
mechanize. mechanize.
""" """
real_response = self.response._response real_response = self.response._response
status = real_response.getStatus() status = real_response.getStatus()
reason = status_reasons[real_response.status] reason = status_reasons[real_response.status]
headers = []
# Convert header keys to camel case. This is basically a copy # Replace HTTP/1.1 200 OK with Status: 200 OK line.
# paste from ZPublisher.HTTPResponse headers = ['Status: %s %s' % (status, reason)]
for key, val in real_response.headers.items(): wsgi_headers = self.response._wsgi_headers.getvalue().split('\r\n')
if key.lower() == key: headers += [line for line in wsgi_headers[1:]]
# only change non-literal header names headers = '\r\n'.join(headers)
key = "%s%s" % (key[:1].upper(), key[1:]) content = self.response.getBody()
start = 0
l = key.find('-', start)
while l >= start:
key = "%s-%s%s" % (
key[:l], key[l + 1:l + 2].upper(), key[l + 2:])
start = l + 1
l = key.find('-', start)
headers.append((key, val))
# get the cookies, breaking them into tuples for sorting
cookies = real_response._cookie_list()
headers.extend(cookies)
headers.sort()
headers.insert(0, ('Status', "%s %s" % (status, reason)))
headers = '\r\n'.join('%s: %s' % h for h in headers)
content = real_response.body
return PublisherResponse(content, headers, status, reason) return PublisherResponse(content, headers, status, reason)
......
This diff is collapsed.
...@@ -304,21 +304,30 @@ _request_closer_for_repoze_tm = _RequestCloserForTransaction() ...@@ -304,21 +304,30 @@ _request_closer_for_repoze_tm = _RequestCloserForTransaction()
def publish_module(environ, start_response, def publish_module(environ, start_response,
_publish=publish, # only for testing _publish=publish, # only for testing
_response_factory=WSGIResponse, # only for testing _response=None,
_request_factory=HTTPRequest, # only for testing _response_factory=WSGIResponse,
_request=None,
_request_factory=HTTPRequest,
module_name='Zope2',
): ):
module_info = get_module_info() module_info = get_module_info(module_name)
transactions_manager = module_info[7] transactions_manager = module_info[7]
status = 200 status = 200
stdout = StringIO() stdout = StringIO()
stderr = StringIO() stderr = StringIO()
response = _response_factory(stdout=stdout, stderr=stderr) if _response is None:
response = _response_factory(stdout=stdout, stderr=stderr)
else:
response = _response
response._http_version = environ['SERVER_PROTOCOL'].split('/')[1] response._http_version = environ['SERVER_PROTOCOL'].split('/')[1]
response._server_version = environ.get('SERVER_SOFTWARE') response._server_version = environ.get('SERVER_SOFTWARE')
request = _request_factory(environ['wsgi.input'], environ, response) if _request is None:
request = _request_factory(environ['wsgi.input'], environ, response)
else:
request = _request
repoze_tm_active = 'repoze.tm.active' in environ repoze_tm_active = 'repoze.tm.active' in environ
......
Exception handling
------------------
These tests capture the current behavior. Maybe some of that behavior should
be changed. The behavior caused by handleErrors=False shows only up in tests.
Create the browser object we'll be using.
>>> from Testing.testbrowser import Browser
>>> browser = Browser()
>>> # XXX: browser has no API for disabling redirects
>>> browser.mech_browser.set_handle_redirect(False)
Create the objects that are raising exceptions.
>>> dummy = app.test_folder_1_._setObject('foo', ExceptionRaiser1())
>>> dummy = app.test_folder_1_._setObject('bar', ExceptionRaiser2())
>>> dummy = app.test_folder_1_._setObject('baz', ExceptionRaiser3())
Handle AttributeError.
>>> app.test_folder_1_.foo.exception = AttributeError('ERROR VALUE')
>>> browser.handleErrors = True
>>> browser.open('http://localhost/test_folder_1_/foo')
Traceback (most recent call last):
...
HTTPError: HTTP Error 500: Internal Server Error
>>> browser.handleErrors = False
>>> browser.open('http://localhost/test_folder_1_/foo')
Traceback (most recent call last):
...
AttributeError: ERROR VALUE
>>> browser.contents
Handle ImportError.
>>> app.test_folder_1_.foo.exception = ImportError('ERROR VALUE')
>>> browser.handleErrors = True
>>> browser.open('http://localhost/test_folder_1_/foo')
Traceback (most recent call last):
...
HTTPError: HTTP Error 500: Internal Server Error
>>> browser.handleErrors = False
>>> browser.open('http://localhost/test_folder_1_/foo')
Traceback (most recent call last):
...
ImportError: ERROR VALUE
>>> browser.contents
Handle zope.publisher.interfaces.NotFound.
>>> from zope.publisher.interfaces import NotFound
>>> app.test_folder_1_.foo.exception = NotFound('OBJECT','NAME')
>>> browser.handleErrors = True
>>> browser.open('http://localhost/test_folder_1_/foo')
Traceback (most recent call last):
...
HTTPError: HTTP Error 404: Not Found
>>> browser.handleErrors = False
>>> browser.open('http://localhost/test_folder_1_/foo')
Traceback (most recent call last):
...
NotFound: Object: 'OBJECT', name: 'NAME'
>>> browser.contents
Don't handle SystemExit, even if handleErrors is True.
>>> app.test_folder_1_.foo.exception = SystemExit('ERROR VALUE')
>>> browser.handleErrors = True
>>> browser.open('http://localhost/test_folder_1_/foo')
Traceback (most recent call last):
...
SystemExit: ERROR VALUE
>>> browser.contents
>>> browser.handleErrors = False
>>> browser.open('http://localhost/test_folder_1_/foo')
Traceback (most recent call last):
...
SystemExit: ERROR VALUE
>>> browser.contents
Handle zExceptions.Redirect.
>>> from zExceptions import Redirect
>>> app.test_folder_1_.foo.exception = Redirect('LOCATION')
>>> browser.handleErrors = True
>>> browser.open('http://localhost/test_folder_1_/foo')
Traceback (most recent call last):
...
HTTPError: HTTP Error 302: Found
>>> browser.contents
''
>>> browser.headers['Location']
'LOCATION'
>>> browser.handleErrors = False
>>> browser.open('http://localhost/test_folder_1_/foo')
Traceback (most recent call last):
...
Redirect: LOCATION
>>> browser.contents
Handle zExceptions.Unauthorized raised by the object. We take the
'WWW-Authenticate' header as a sign that HTTPResponse._unauthorized was called.
>>> from zExceptions import Unauthorized
>>> app.test_folder_1_.foo.exception = Unauthorized('ERROR VALUE')
>>> browser.handleErrors = True
>>> browser.open('http://localhost/test_folder_1_/foo')
Traceback (most recent call last):
...
HTTPError: HTTP Error 401: Unauthorized
>>> browser.headers['WWW-Authenticate']
'basic realm="Zope2"'
>>> browser.handleErrors = False
>>> browser.open('http://localhost/test_folder_1_/foo')
Traceback (most recent call last):
...
Unauthorized: ERROR VALUE
>>> browser.contents
And the same with unicode error value.
>>> app.test_folder_1_.foo.exception = Unauthorized(u'ERROR VALUE \u03A9')
>>> browser.handleErrors = True
>>> browser.open('http://localhost/test_folder_1_/foo')
Traceback (most recent call last):
...
HTTPError: HTTP Error 401: Unauthorized
>>> browser.headers['WWW-Authenticate']
'basic realm="Zope2"'
>>> browser.handleErrors = False
>>> try:
... browser.open('http://localhost/test_folder_1_/foo')
... except Unauthorized, e:
... e._message == u'ERROR VALUE \u03A9'
... else:
... print "Unauthorized not raised"
True
>>> browser.contents
Handle zExceptions.Unauthorized raised by BaseRequest.traverse. We take the
'WWW-Authenticate' header as a sign that HTTPResponse._unauthorized was called.
>>> browser.handleErrors = True
>>> browser.open('http://localhost/test_folder_1_/bar')
Traceback (most recent call last):
...
HTTPError: HTTP Error 401: Unauthorized
>>> 'You are not authorized to access this resource.' in browser.contents
True
>>> browser.headers['WWW-Authenticate']
'basic realm="Zope2"'
>>> browser.handleErrors = False
>>> browser.open('http://localhost/test_folder_1_/bar')
Traceback (most recent call last):
...
Unauthorized: You are not authorized to access this resource...
>>> browser.contents
Handle zExceptions.Forbidden raised by BaseRequest.traverse. 'traverse'
converts it into zExceptions.NotFound if we are not in debug mode.
>>> browser.handleErrors = True
>>> browser.open('http://localhost/test_folder_1_/baz')
Traceback (most recent call last):
...
HTTPError: HTTP Error 404: Not Found
>>> '<p><strong>Resource not found</strong></p>' in browser.contents
True
>>> '<p><b>Resource:</b> index_html</p>' in browser.contents
True
>>> browser.handleErrors = False
>>> browser.open('http://localhost/test_folder_1_/baz')
Traceback (most recent call last):
...
NotFound: <html>
...<h2>Site Error</h2>
...<p><strong>Resource not found</strong></p>...
...<p><b>Resource:</b> index_html</p>...
>>> browser.contents
This diff is collapsed.
##############################################################################
#
# Copyright (c) 2010 Zope Foundation and Contributors.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
""" Functional tests for exception handling.
"""
import unittest
from Testing.ZopeTestCase import FunctionalDocFileSuite
from OFS.SimpleItem import SimpleItem
class ExceptionRaiser1(SimpleItem):
def index_html(self):
"""DOCSTRING
"""
raise self.exception
class ExceptionRaiser2(ExceptionRaiser1):
__roles__ = ()
class ExceptionRaiser3(SimpleItem):
def index_html(self):
return 'NO DOCSTRING'
def test_suite():
return unittest.TestSuite([
FunctionalDocFileSuite(
'exception_handling.txt',
globs={
'ExceptionRaiser1': ExceptionRaiser1,
'ExceptionRaiser2': ExceptionRaiser2,
'ExceptionRaiser3': ExceptionRaiser3,
}),
])
...@@ -6,9 +6,7 @@ from ZODB.POSException import ConflictError ...@@ -6,9 +6,7 @@ from ZODB.POSException import ConflictError
from zope.interface.verify import verifyObject from zope.interface.verify import verifyObject
from zope.event import subscribers from zope.event import subscribers
from ZPublisher.Publish import publish, Retry
from ZPublisher.BaseRequest import BaseRequest from ZPublisher.BaseRequest import BaseRequest
from ZPublisher.HTTPResponse import HTTPResponse
from ZPublisher.pubevents import ( from ZPublisher.pubevents import (
PubStart, PubSuccess, PubFailure, PubStart, PubSuccess, PubFailure,
PubAfterTraversal, PubBeforeCommit, PubBeforeAbort, PubAfterTraversal, PubBeforeCommit, PubBeforeAbort,
...@@ -19,6 +17,10 @@ from ZPublisher.interfaces import ( ...@@ -19,6 +17,10 @@ from ZPublisher.interfaces import (
IPubAfterTraversal, IPubBeforeCommit, IPubAfterTraversal, IPubBeforeCommit,
IPubBeforeStreaming, IPubBeforeStreaming,
) )
from ZPublisher import Retry
from ZPublisher.WSGIPublisher import publish_module
from ZPublisher.WSGIPublisher import WSGIResponse
PUBMODULE = 'TEST_testpubevents' PUBMODULE = 'TEST_testpubevents'
...@@ -71,85 +73,77 @@ class TestPubEvents(TestCase): ...@@ -71,85 +73,77 @@ class TestPubEvents(TestCase):
del modules[PUBMODULE] del modules[PUBMODULE]
subscribers[:] = self._saved_subscribers subscribers[:] = self._saved_subscribers
def _publish(self, request, module_name):
def start_response(status, headers):
pass
publish_module({
'SERVER_PROTOCOL': 'HTTP/1.1',
'SERVER_NAME': 'localhost',
'SERVER_PORT': 'localhost',
'REQUEST_METHOD': 'GET',
}, start_response, _request=request, module_name=module_name)
def testSuccess(self): def testSuccess(self):
r = self.request r = self.request
r.action = 'succeed' r.action = 'succeed'
publish(r, PUBMODULE, [None]) self._publish(r, PUBMODULE)
events = self.reporter.events events = self.reporter.events
self.assertEqual(len(events), 4)
self.assert_(isinstance(events[0], PubStart)) self.assert_(isinstance(events[0], PubStart))
self.assertEqual(events[0].request, r) self.assertEqual(events[0].request, r)
self.assert_(isinstance(events[-1], PubSuccess))
self.assertEqual(events[-1].request, r)
# test AfterTraversal and BeforeCommit as well
self.assert_(isinstance(events[1], PubAfterTraversal)) self.assert_(isinstance(events[1], PubAfterTraversal))
self.assertEqual(events[1].request, r) self.assertEqual(events[1].request, r)
self.assert_(isinstance(events[2], PubBeforeCommit)) self.assert_(isinstance(events[2], PubBeforeCommit))
self.assertEqual(events[2].request, r) self.assertEqual(events[2].request, r)
self.assert_(isinstance(events[3], PubSuccess))
self.assertEqual(events[3].request, r)
def testFailureReturn(self): def testFailureReturn(self):
r = self.request r = self.request
r.action = 'fail_return' r.action = 'fail_return'
publish(r, PUBMODULE, [None]) self.assertRaises(Exception, self._publish, r, PUBMODULE)
events = self.reporter.events events = self.reporter.events
self.assertEqual(len(events), 3)
self.assert_(isinstance(events[0], PubStart)) self.assert_(isinstance(events[0], PubStart))
self.assertEqual(events[0].request, r) self.assertEqual(events[0].request, r)
self.assert_(isinstance(events[1], PubBeforeAbort)) self.assert_(isinstance(events[1], PubBeforeAbort))
self.assertEqual(events[1].request, r) self.assertEqual(events[1].request, r)
self.assertEqual(events[1].retry, False)
self.assert_(isinstance(events[2], PubFailure)) self.assert_(isinstance(events[2], PubFailure))
self.assertEqual(events[2].request, r) self.assertEqual(events[2].request, r)
self.assertEqual(events[2].retry, False)
self.assertEqual(len(events[2].exc_info), 3) self.assertEqual(len(events[2].exc_info), 3)
def testFailureException(self): def testFailureException(self):
r = self.request r = self.request
r.action = 'fail_exception' r.action = 'fail_exception'
self.assertRaises(Exception, publish, r, PUBMODULE, [None]) self.assertRaises(Exception, self._publish, r, PUBMODULE)
events = self.reporter.events events = self.reporter.events
self.assertEqual(len(events), 3)
self.assert_(isinstance(events[0], PubStart)) self.assert_(isinstance(events[0], PubStart))
self.assertEqual(events[0].request, r) self.assertEqual(events[0].request, r)
self.assert_(isinstance(events[1], PubBeforeAbort)) self.assert_(isinstance(events[1], PubBeforeAbort))
self.assertEqual(events[1].request, r) self.assertEqual(events[1].request, r)
self.assertEqual(events[1].retry, False)
self.assertEqual(len(events[1].exc_info), 3) self.assertEqual(len(events[1].exc_info), 3)
self.assert_(isinstance(events[2], PubFailure)) self.assert_(isinstance(events[2], PubFailure))
self.assertEqual(events[2].request, r) self.assertEqual(events[2].request, r)
self.assertEqual(events[2].retry, False)
self.assertEqual(len(events[2].exc_info), 3) self.assertEqual(len(events[2].exc_info), 3)
def testFailureConflict(self): def testFailureConflict(self):
r = self.request r = self.request
r.action = 'conflict' r.action = 'conflict'
publish(r, PUBMODULE, [None]) self.assertRaises(ConflictError, self._publish, r, PUBMODULE)
events = self.reporter.events events = self.reporter.events
self.assertEqual(len(events), 7)
self.assert_(isinstance(events[0], PubStart)) self.assert_(isinstance(events[0], PubStart))
self.assertEqual(events[0].request, r) self.assertEqual(events[0].request, r)
self.assert_(isinstance(events[1], PubBeforeAbort)) self.assert_(isinstance(events[1], PubBeforeAbort))
self.assertEqual(events[1].request, r) self.assertEqual(events[1].request, r)
self.assertEqual(events[1].retry, True)
self.assertEqual(len(events[1].exc_info), 3) self.assertEqual(len(events[1].exc_info), 3)
self.assert_(isinstance(events[1].exc_info[1], ConflictError)) self.assert_(isinstance(events[1].exc_info[1], ConflictError))
self.assert_(isinstance(events[2], PubFailure)) self.assert_(isinstance(events[2], PubFailure))
self.assertEqual(events[2].request, r) self.assertEqual(events[2].request, r)
self.assertEqual(events[2].retry, True)
self.assertEqual(len(events[2].exc_info), 3) self.assertEqual(len(events[2].exc_info), 3)
self.assert_(isinstance(events[2].exc_info[1], ConflictError)) self.assert_(isinstance(events[2].exc_info[1], ConflictError))
self.assert_(isinstance(events[3], PubStart))
self.assert_(isinstance(events[4], PubAfterTraversal))
self.assert_(isinstance(events[5], PubBeforeCommit))
self.assert_(isinstance(events[6], PubSuccess))
def testStreaming(self): def testStreaming(self):
out = StringIO() out = StringIO()
response = HTTPResponse(stdout=out) response = WSGIResponse(stdout=out)
response.write('datachunk1') response.write('datachunk1')
response.write('datachunk2') response.write('datachunk2')
...@@ -184,7 +178,7 @@ class _Response(object): ...@@ -184,7 +178,7 @@ class _Response(object):
class _Request(BaseRequest): class _Request(BaseRequest):
response = _Response() response = WSGIResponse()
_hacked_path = False _hacked_path = False
args = () args = ()
...@@ -193,14 +187,6 @@ class _Request(BaseRequest): ...@@ -193,14 +187,6 @@ class _Request(BaseRequest):
self['PATH_INFO'] = self['URL'] = '' self['PATH_INFO'] = self['URL'] = ''
self.steps = [] self.steps = []
def supports_retry(self):
return True
def retry(self):
r = self.__class__()
r.action = 'succeed'
return r
def traverse(self, *unused, **unused_kw): def traverse(self, *unused, **unused_kw):
action = self.action action = self.action
if action.startswith('fail'): if action.startswith('fail'):
...@@ -216,8 +202,28 @@ class _Request(BaseRequest): ...@@ -216,8 +202,28 @@ class _Request(BaseRequest):
# override to get rid of the 'EndRequestEvent' notification # override to get rid of the 'EndRequestEvent' notification
pass pass
class _TransactionsManager(object):
def __init__(self, *args, **kw):
self.tracer = []
def abort(self):
self.tracer.append('abort')
def begin(self):
self.tracer.append('begin')
def commit(self):
self.tracer.append('commit')
def recordMetaData(self, obj, request):
pass
# define things necessary for publication # define things necessary for publication
bobo_application = _Application() bobo_application = _Application()
zpublisher_transactions_manager = _TransactionsManager()
def zpublisher_exception_hook(parent, request, *unused): def zpublisher_exception_hook(parent, request, *unused):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment