• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / zope.testbrowser / 16098871825

05 Mar 2025 04:01PM UTC coverage: 91.403%. Remained the same
16098871825

push

github

icemac
Back to development: 7.1

427 of 520 branches covered (82.12%)

Branch coverage included in aggregate %.

1859 of 1981 relevant lines covered (93.84%)

0.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.94
/src/zope/testbrowser/wsgi.py
1
##############################################################################
2
#
3
# Copyright (c) 2010-2011 Zope Foundation and Contributors.
4
# All Rights Reserved.
5
#
6
# This software is subject to the provisions of the Zope Public License,
7
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
# FOR A PARTICULAR PURPOSE.
12
#
13
##############################################################################
14
"""WSGI-specific testing code
15
"""
16

17
import base64
1✔
18
import re
1✔
19

20
import zope.testbrowser.browser
1✔
21
from zope.testbrowser.browser import HostNotAllowed  # noqa BBB
1✔
22

23

24
class Browser(zope.testbrowser.browser.Browser):
1✔
25
    def __init__(self, url=None, wsgi_app=None):
1✔
26
        if wsgi_app is None:
1✔
27
            wsgi_app = Layer.get_app()
1✔
28
        if wsgi_app is None:
1✔
29
            raise AssertionError("wsgi_app not provided or "
30
                                 "zope.testbrowser.wsgi.Layer not setup")
31
        super().__init__(url, wsgi_app)
1✔
32

33

34
basicre = re.compile('Basic (.+)?:(.+)?$')
1✔
35

36

37
def auth_header(header):
1✔
38
    """This function takes an authorization HTTP header and encode the
39
    couple user, password into base 64 like the HTTP protocol wants
40
    it.
41
    """
42
    match = basicre.match(header)
1✔
43
    if match:
1✔
44
        u, p = match.group(1, 2)
1✔
45
        if u is None:
1!
46
            u = ''
×
47
        if p is None:
1!
48
            p = ''
×
49
        plain = '{}:{}'.format(u, p)
1✔
50
        auth = base64.encodebytes(plain.encode('utf-8'))
1✔
51
        return 'Basic %s' % str(auth.rstrip().decode('latin1'))
1✔
52
    return header
1✔
53

54

55
def is_wanted_header(header):
1✔
56
    """Return True if the given HTTP header key is wanted.
57
    """
58
    key, value = header
1✔
59
    return key.lower() not in ('x-content-type-warning', 'x-powered-by')
1✔
60

61

62
class AuthorizationMiddleware:
1✔
63
    """This middleware makes the WSGI application compatible with the
64
    HTTPCaller behavior defined in zope.app.testing.functional:
65
    - It modifies the HTTP Authorization header to encode user and
66
      password into base64 if it is Basic authentication.
67
    """
68

69
    def __init__(self, wsgi_stack):
1✔
70
        self.wsgi_stack = wsgi_stack
1✔
71

72
    def __call__(self, environ, start_response):
1✔
73
        # Handle authorization
74
        auth_key = 'HTTP_AUTHORIZATION'
1✔
75
        if auth_key in environ:
1✔
76
            environ[auth_key] = auth_header(environ[auth_key])
1✔
77

78
        # Remove unwanted headers
79
        def application_start_response(status, headers, exc_info=None):
1✔
80
            headers = [h for h in headers if is_wanted_header(h)]
1✔
81
            start_response(status, headers)
1✔
82

83
        yield from self.wsgi_stack(environ, application_start_response)
1✔
84

85

86
_APP_UNDER_TEST = None  # setup and torn down by the Layer class
1✔
87

88

89
class Layer:
1✔
90
    """Test layer which sets up WSGI app for use with WebTest/testbrowser.
91

92
    Inherit from this layer and overwrite `make_wsgi_app` for setup.
93

94
    Composing multiple layers into one is supported using plone.testing.Layer.
95

96
    """
97

98
    __bases__ = ()
1✔
99
    __name__ = 'Layer'
1✔
100

101
    @classmethod
1✔
102
    def get_app(cls):
1✔
103
        return _APP_UNDER_TEST
1✔
104

105
    def make_wsgi_app(self):
1✔
106
        # Override this method in subclasses of this layer in order to set up
107
        # the WSGI application.
108
        raise NotImplementedError
109

110
    def cooperative_super(self, method_name):
1✔
111
        # Calling `super` for multiple inheritance:
112
        method = getattr(super(), method_name, None)
1✔
113
        if method is not None:
1!
114
            method()
×
115

116
    def setUp(self):
1✔
117
        self.cooperative_super('setUp')
1✔
118
        global _APP_UNDER_TEST
119
        if _APP_UNDER_TEST is not None:
1✔
120
            raise AssertionError("Already Setup")
121
        _APP_UNDER_TEST = self.make_wsgi_app()
1✔
122

123
    def tearDown(self):
1✔
124
        global _APP_UNDER_TEST
125
        _APP_UNDER_TEST = None
1✔
126
        self.cooperative_super('tearDown')
1✔
127

128

129
class TestBrowserLayer:
1✔
130
    """Test layer which sets up WSGI app for use with WebTest/testbrowser.
131

132
    This layer is intended for use cases, where `make_wsgi_app` is implemented
133
    by another class using multiple inheritance.
134

135
    We used `testSetUp` and `testTearDown` instead of `setUp` and `tearDown` to
136
    cooperate with layers from other zope packages, e.g.
137
    `zope.app.wsgi.testlayer.BrowserLayer`, since they re-create the DB
138
    connection during `testSetUp`. Therefore we need to re-create the app, too.
139

140
    Make sure this layer always comes first in multiple inheritance, because
141
    the requirements of other layers should be set up before calling
142
    `make_wsgi_app`. In addition, many layers do not make sure to call multiple
143
    superclasses using something like `cooperative_super`, thus the methods of
144
    this layer may not be called if it comes later.
145

146
    """
147

148
    def cooperative_super(self, method_name):
1✔
149
        # Calling `super` for multiple inheritance:
150
        method = getattr(super(), method_name, None)
1✔
151
        if method is not None:
1✔
152
            return method()
1✔
153

154
    def make_wsgi_app(self):
1✔
155
        if not hasattr(super(), 'make_wsgi_app'):
1✔
156
            raise NotImplementedError
157
        return super().make_wsgi_app()
1✔
158

159
    def testSetUp(self):
1✔
160
        self.cooperative_super('testSetUp')
1✔
161
        global _APP_UNDER_TEST
162
        if _APP_UNDER_TEST is not None:
1✔
163
            raise AssertionError("Already Setup")
164
        _APP_UNDER_TEST = self.make_wsgi_app()
1✔
165

166
    def testTearDown(self):
1✔
167
        global _APP_UNDER_TEST
168
        _APP_UNDER_TEST = None
1✔
169
        self.cooperative_super('testTearDown')
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc