##// END OF EJS Templates
tests: fixed some tests because of wrong compare strings
super-admin -
r5097:ff28a588 default
parent child Browse files
Show More
@@ -1,155 +1,155 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 import urllib.parse
21 21
22 22 import mock
23 23 import pytest
24 24 import simplejson as json
25 25
26 26 from rhodecode.lib.vcs.backends.base import Config
27 27 from rhodecode.tests.lib.middleware import mock_scm_app
28 28 import rhodecode.lib.middleware.simplehg as simplehg
29 29
30 30
31 31 def get_environ(url):
32 32 """Construct a minimum WSGI environ based on the URL."""
33 33 parsed_url = urllib.parse.urlparse(url)
34 34 environ = {
35 35 'PATH_INFO': parsed_url.path,
36 36 'QUERY_STRING': parsed_url.query,
37 37 }
38 38
39 39 return environ
40 40
41 41
42 42 @pytest.mark.parametrize(
43 43 'url, expected_action',
44 44 [
45 45 ('/foo/bar?cmd=unbundle&key=tip', 'push'),
46 46 ('/foo/bar?cmd=pushkey&key=tip', 'push'),
47 47 ('/foo/bar?cmd=listkeys&key=tip', 'pull'),
48 48 ('/foo/bar?cmd=changegroup&key=tip', 'pull'),
49 49 ('/foo/bar?cmd=hello', 'pull'),
50 50 ('/foo/bar?cmd=batch', 'push'),
51 51 ('/foo/bar?cmd=putlfile', 'push'),
52 52 # Edge case: unknown argument: assume push
53 53 ('/foo/bar?cmd=unknown&key=tip', 'push'),
54 54 ('/foo/bar?cmd=&key=tip', 'push'),
55 55 # Edge case: not cmd argument
56 56 ('/foo/bar?key=tip', 'push'),
57 57 ])
58 58 def test_get_action(url, expected_action, request_stub):
59 59 app = simplehg.SimpleHg(config={'auth_ret_code': '', 'base_path': ''},
60 60 registry=request_stub.registry)
61 61 assert expected_action == app._get_action(get_environ(url))
62 62
63 63
64 64 @pytest.mark.parametrize(
65 65 'environ, expected_xargs, expected_batch',
66 66 [
67 67 ({},
68 68 [''], ['push']),
69 69
70 70 ({'HTTP_X_HGARG_1': ''},
71 71 [''], ['push']),
72 72
73 73 ({'HTTP_X_HGARG_1': 'cmds=listkeys+namespace%3Dphases'},
74 74 ['listkeys namespace=phases'], ['pull']),
75 75
76 76 ({'HTTP_X_HGARG_1': 'cmds=pushkey+namespace%3Dbookmarks%2Ckey%3Dbm%2Cold%3D%2Cnew%3Dcb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'},
77 77 ['pushkey namespace=bookmarks,key=bm,old=,new=cb9a9f314b8b07ba71012fcdbc544b5a4d82ff5b'], ['push']),
78 78
79 79 ({'HTTP_X_HGARG_1': 'namespace=phases'},
80 80 ['namespace=phases'], ['push']),
81 81
82 82 ])
83 83 def test_xarg_and_batch_commands(environ, expected_xargs, expected_batch):
84 84 app = simplehg.SimpleHg
85 85
86 86 result = app._get_xarg_headers(environ)
87 87 result_batch = app._get_batch_cmd(environ)
88 88 assert expected_xargs == result
89 89 assert expected_batch == result_batch
90 90
91 91
92 92 @pytest.mark.parametrize(
93 93 'url, expected_repo_name',
94 94 [
95 95 ('/foo?cmd=unbundle&key=tip', 'foo'),
96 96 ('/foo/bar?cmd=pushkey&key=tip', 'foo/bar'),
97 97 ('/foo/bar/baz?cmd=listkeys&key=tip', 'foo/bar/baz'),
98 98 # Repos with trailing slashes.
99 99 ('/foo/?cmd=unbundle&key=tip', 'foo'),
100 100 ('/foo/bar/?cmd=pushkey&key=tip', 'foo/bar'),
101 101 ('/foo/bar/baz/?cmd=listkeys&key=tip', 'foo/bar/baz'),
102 102 ])
103 103 def test_get_repository_name(url, expected_repo_name, request_stub):
104 104 app = simplehg.SimpleHg(config={'auth_ret_code': '', 'base_path': ''},
105 105 registry=request_stub.registry)
106 106 assert expected_repo_name == app._get_repository_name(get_environ(url))
107 107
108 108
109 109 def test_get_config(user_util, baseapp, request_stub):
110 110 repo = user_util.create_repo(repo_type='git')
111 111 app = simplehg.SimpleHg(config={'auth_ret_code': '', 'base_path': ''},
112 112 registry=request_stub.registry)
113 113 extras = [('foo', 'FOO', 'bar', 'BAR')]
114 114
115 115 hg_config = app._create_config(extras, repo_name=repo.repo_name)
116 116
117 117 config = simplehg.utils.make_db_config(repo=repo.repo_name)
118 118 config.set('rhodecode', 'RC_SCM_DATA', json.dumps(extras))
119 119 hg_config_org = config
120 120
121 121 expected_config = [
122 122 ('vcs_svn_tag', 'ff89f8c714d135d865f44b90e5413b88de19a55f', '/tags/*'),
123 123 ('web', 'push_ssl', 'False'),
124 124 ('web', 'allow_push', '*'),
125 125 ('web', 'allow_archive', 'gz zip bz2'),
126 126 ('web', 'baseurl', '/'),
127 127 ('vcs_git_lfs', 'store_location', hg_config_org.get('vcs_git_lfs', 'store_location')),
128 128 ('vcs_svn_branch', '9aac1a38c3b8a0cdc4ae0f960a5f83332bc4fa5e', '/branches/*'),
129 129 ('vcs_svn_branch', 'c7e6a611c87da06529fd0dd733308481d67c71a8', '/trunk'),
130 130 ('largefiles', 'usercache', hg_config_org.get('largefiles', 'usercache')),
131 131 ('hooks', 'preoutgoing.pre_pull', 'python:vcsserver.hooks.pre_pull'),
132 132 ('hooks', 'prechangegroup.pre_push', 'python:vcsserver.hooks.pre_push'),
133 133 ('hooks', 'outgoing.pull_logger', 'python:vcsserver.hooks.log_pull_action'),
134 134 ('hooks', 'pretxnchangegroup.pre_push', 'python:vcsserver.hooks.pre_push'),
135 135 ('hooks', 'changegroup.push_logger', 'python:vcsserver.hooks.log_push_action'),
136 136 ('hooks', 'changegroup.repo_size', 'python:vcsserver.hooks.repo_size'),
137 137 ('phases', 'publish', 'True'),
138 138 ('extensions', 'largefiles', ''),
139 139 ('paths', '/', hg_config_org.get('paths', '/')),
140 ('rhodecode', 'RC_SCM_DATA', '[["foo", "FOO", "bar", "BAR"]]')
140 ('rhodecode', 'RC_SCM_DATA', '[["foo","FOO","bar","BAR"]]')
141 141 ]
142 142 for entry in expected_config:
143 143 assert entry in hg_config
144 144
145 145
146 146 def test_create_wsgi_app_uses_scm_app_from_simplevcs(request_stub):
147 147 config = {
148 148 'auth_ret_code': '',
149 149 'base_path': '',
150 150 'vcs.scm_app_implementation':
151 151 'rhodecode.tests.lib.middleware.mock_scm_app',
152 152 }
153 153 app = simplehg.SimpleHg(config=config, registry=request_stub.registry)
154 154 wsgi_app = app._create_wsgi_app('/tmp/test', 'test_repo', {})
155 155 assert wsgi_app is mock_scm_app.mock_hg_wsgi
@@ -1,375 +1,376 b''
1 1
2 2 # Copyright (C) 2010-2023 RhodeCode GmbH
3 3 #
4 4 # This program is free software: you can redistribute it and/or modify
5 5 # it under the terms of the GNU Affero General Public License, version 3
6 6 # (only), as published by the Free Software Foundation.
7 7 #
8 8 # This program is distributed in the hope that it will be useful,
9 9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 11 # GNU General Public License for more details.
12 12 #
13 13 # You should have received a copy of the GNU Affero General Public License
14 14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 15 #
16 16 # This program is dual-licensed. If you wish to learn more about the
17 17 # RhodeCode Enterprise Edition, including its added features, Support services,
18 18 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 19
20 20 import logging
21 21 import io
22 22
23 23 import mock
24 24 import msgpack
25 25 import pytest
26 26
27 27 from rhodecode.lib import hooks_daemon
28 28 from rhodecode.lib.str_utils import safe_bytes
29 29 from rhodecode.tests.utils import assert_message_in_log
30 30 from rhodecode.lib.ext_json import json
31 31
32 32 test_proto = hooks_daemon.HooksHttpHandler.MSGPACK_HOOKS_PROTO
33 33
34 34
35 35 class TestDummyHooksCallbackDaemon(object):
36 36 def test_hooks_module_path_set_properly(self):
37 37 daemon = hooks_daemon.DummyHooksCallbackDaemon()
38 38 assert daemon.hooks_module == 'rhodecode.lib.hooks_daemon'
39 39
40 40 def test_logs_entering_the_hook(self):
41 41 daemon = hooks_daemon.DummyHooksCallbackDaemon()
42 42 with mock.patch.object(hooks_daemon.log, 'debug') as log_mock:
43 43 with daemon as return_value:
44 44 log_mock.assert_called_once_with(
45 45 'Running `%s` callback daemon', 'DummyHooksCallbackDaemon')
46 46 assert return_value == daemon
47 47
48 48 def test_logs_exiting_the_hook(self):
49 49 daemon = hooks_daemon.DummyHooksCallbackDaemon()
50 50 with mock.patch.object(hooks_daemon.log, 'debug') as log_mock:
51 51 with daemon:
52 52 pass
53 53 log_mock.assert_called_with(
54 54 'Exiting `%s` callback daemon', 'DummyHooksCallbackDaemon')
55 55
56 56
57 57 class TestHooks(object):
58 58 def test_hooks_can_be_used_as_a_context_processor(self):
59 59 hooks = hooks_daemon.Hooks()
60 60 with hooks as return_value:
61 61 pass
62 62 assert hooks == return_value
63 63
64 64
65 65 class TestHooksHttpHandler(object):
66 66 def test_read_request_parses_method_name_and_arguments(self):
67 67 data = {
68 68 'method': 'test',
69 69 'extras': {
70 70 'param1': 1,
71 71 'param2': 'a'
72 72 }
73 73 }
74 74 request = self._generate_post_request(data)
75 75 hooks_patcher = mock.patch.object(
76 76 hooks_daemon.Hooks, data['method'], create=True, return_value=1)
77 77
78 78 with hooks_patcher as hooks_mock:
79 79 handler = hooks_daemon.HooksHttpHandler
80 80 handler.DEFAULT_HOOKS_PROTO = test_proto
81 81 handler.wbufsize = 10240
82 82 MockServer(handler, request)
83 83
84 84 hooks_mock.assert_called_once_with(data['extras'])
85 85
86 86 def test_hooks_serialized_result_is_returned(self):
87 87 request = self._generate_post_request({})
88 88 rpc_method = 'test'
89 89 hook_result = {
90 90 'first': 'one',
91 91 'second': 2
92 92 }
93 93 extras = {}
94 94
95 95 # patching our _read to return test method and proto used
96 96 read_patcher = mock.patch.object(
97 97 hooks_daemon.HooksHttpHandler, '_read_request',
98 98 return_value=(test_proto, rpc_method, extras))
99 99
100 100 # patch Hooks instance to return hook_result data on 'test' call
101 101 hooks_patcher = mock.patch.object(
102 102 hooks_daemon.Hooks, rpc_method, create=True,
103 103 return_value=hook_result)
104 104
105 105 with read_patcher, hooks_patcher:
106 106 handler = hooks_daemon.HooksHttpHandler
107 107 handler.DEFAULT_HOOKS_PROTO = test_proto
108 108 handler.wbufsize = 10240
109 109 server = MockServer(handler, request)
110 110
111 111 expected_result = hooks_daemon.HooksHttpHandler.serialize_data(hook_result)
112 112
113 113 server.request.output_stream.seek(0)
114 114 assert server.request.output_stream.readlines()[-1] == expected_result
115 115
116 116 def test_exception_is_returned_in_response(self):
117 117 request = self._generate_post_request({})
118 118 rpc_method = 'test'
119 119
120 120 read_patcher = mock.patch.object(
121 121 hooks_daemon.HooksHttpHandler, '_read_request',
122 122 return_value=(test_proto, rpc_method, {}))
123 123
124 124 hooks_patcher = mock.patch.object(
125 125 hooks_daemon.Hooks, rpc_method, create=True,
126 126 side_effect=Exception('Test exception'))
127 127
128 128 with read_patcher, hooks_patcher:
129 129 handler = hooks_daemon.HooksHttpHandler
130 130 handler.DEFAULT_HOOKS_PROTO = test_proto
131 131 handler.wbufsize = 10240
132 132 server = MockServer(handler, request)
133 133
134 134 server.request.output_stream.seek(0)
135 135 data = server.request.output_stream.readlines()
136 136 msgpack_data = b''.join(data[5:])
137 137 org_exc = hooks_daemon.HooksHttpHandler.deserialize_data(msgpack_data)
138 138 expected_result = {
139 139 'exception': 'Exception',
140 140 'exception_traceback': org_exc['exception_traceback'],
141 141 'exception_args': ['Test exception']
142 142 }
143 143 assert org_exc == expected_result
144 144
145 145 def test_log_message_writes_to_debug_log(self, caplog):
146 146 ip_port = ('0.0.0.0', 8888)
147 147 handler = hooks_daemon.HooksHttpHandler(
148 148 MockRequest('POST /'), ip_port, mock.Mock())
149 149 fake_date = '1/Nov/2015 00:00:00'
150 150 date_patcher = mock.patch.object(
151 151 handler, 'log_date_time_string', return_value=fake_date)
152 152
153 153 with date_patcher, caplog.at_level(logging.DEBUG):
154 154 handler.log_message('Some message %d, %s', 123, 'string')
155 155
156 expected_message = f"HOOKS: {ip_port} - - [{fake_date}] Some message 123, string"
156 expected_message = f"HOOKS: client={ip_port} - - [{fake_date}] Some message 123, string"
157
157 158 assert_message_in_log(
158 159 caplog.records, expected_message,
159 160 levelno=logging.DEBUG, module='hooks_daemon')
160 161
161 162 def _generate_post_request(self, data, proto=test_proto):
162 163 if proto == hooks_daemon.HooksHttpHandler.MSGPACK_HOOKS_PROTO:
163 164 payload = msgpack.packb(data)
164 165 else:
165 166 payload = json.dumps(data)
166 167
167 168 return b'POST / HTTP/1.0\nContent-Length: %d\n\n%b' % (
168 169 len(payload), payload)
169 170
170 171
171 172 class ThreadedHookCallbackDaemon(object):
172 173 def test_constructor_calls_prepare(self):
173 174 prepare_daemon_patcher = mock.patch.object(
174 175 hooks_daemon.ThreadedHookCallbackDaemon, '_prepare')
175 176 with prepare_daemon_patcher as prepare_daemon_mock:
176 177 hooks_daemon.ThreadedHookCallbackDaemon()
177 178 prepare_daemon_mock.assert_called_once_with()
178 179
179 180 def test_run_is_called_on_context_start(self):
180 181 patchers = mock.patch.multiple(
181 182 hooks_daemon.ThreadedHookCallbackDaemon,
182 183 _run=mock.DEFAULT, _prepare=mock.DEFAULT, __exit__=mock.DEFAULT)
183 184
184 185 with patchers as mocks:
185 186 daemon = hooks_daemon.ThreadedHookCallbackDaemon()
186 187 with daemon as daemon_context:
187 188 pass
188 189 mocks['_run'].assert_called_once_with()
189 190 assert daemon_context == daemon
190 191
191 192 def test_stop_is_called_on_context_exit(self):
192 193 patchers = mock.patch.multiple(
193 194 hooks_daemon.ThreadedHookCallbackDaemon,
194 195 _run=mock.DEFAULT, _prepare=mock.DEFAULT, _stop=mock.DEFAULT)
195 196
196 197 with patchers as mocks:
197 198 daemon = hooks_daemon.ThreadedHookCallbackDaemon()
198 199 with daemon as daemon_context:
199 200 assert mocks['_stop'].call_count == 0
200 201
201 202 mocks['_stop'].assert_called_once_with()
202 203 assert daemon_context == daemon
203 204
204 205
205 206 class TestHttpHooksCallbackDaemon(object):
206 207 def test_hooks_callback_generates_new_port(self, caplog):
207 208 with caplog.at_level(logging.DEBUG):
208 209 daemon = hooks_daemon.HttpHooksCallbackDaemon(host='127.0.0.1', port=8881)
209 210 assert daemon._daemon.server_address == ('127.0.0.1', 8881)
210 211
211 212 with caplog.at_level(logging.DEBUG):
212 213 daemon = hooks_daemon.HttpHooksCallbackDaemon(host=None, port=None)
213 214 assert daemon._daemon.server_address[1] in range(0, 66000)
214 215 assert daemon._daemon.server_address[0] != '127.0.0.1'
215 216
216 217 def test_prepare_inits_daemon_variable(self, tcp_server, caplog):
217 218 with self._tcp_patcher(tcp_server), caplog.at_level(logging.DEBUG):
218 219 daemon = hooks_daemon.HttpHooksCallbackDaemon(host='127.0.0.1', port=8881)
219 220 assert daemon._daemon == tcp_server
220 221
221 222 _, port = tcp_server.server_address
222 223
223 224 msg = f"HOOKS: 127.0.0.1:{port} Preparing HTTP callback daemon registering " \
224 225 f"hook object: <class 'rhodecode.lib.hooks_daemon.HooksHttpHandler'>"
225 226 assert_message_in_log(
226 227 caplog.records, msg, levelno=logging.DEBUG, module='hooks_daemon')
227 228
228 229 def test_prepare_inits_hooks_uri_and_logs_it(
229 230 self, tcp_server, caplog):
230 231 with self._tcp_patcher(tcp_server), caplog.at_level(logging.DEBUG):
231 232 daemon = hooks_daemon.HttpHooksCallbackDaemon(host='127.0.0.1', port=8881)
232 233
233 234 _, port = tcp_server.server_address
234 235 expected_uri = '{}:{}'.format('127.0.0.1', port)
235 236 assert daemon.hooks_uri == expected_uri
236 237
237 238 msg = f"HOOKS: 127.0.0.1:{port} Preparing HTTP callback daemon registering " \
238 239 f"hook object: <class 'rhodecode.lib.hooks_daemon.HooksHttpHandler'>"
239 240 assert_message_in_log(
240 241 caplog.records, msg,
241 242 levelno=logging.DEBUG, module='hooks_daemon')
242 243
243 244 def test_run_creates_a_thread(self, tcp_server):
244 245 thread = mock.Mock()
245 246
246 247 with self._tcp_patcher(tcp_server):
247 248 daemon = hooks_daemon.HttpHooksCallbackDaemon()
248 249
249 250 with self._thread_patcher(thread) as thread_mock:
250 251 daemon._run()
251 252
252 253 thread_mock.assert_called_once_with(
253 254 target=tcp_server.serve_forever,
254 255 kwargs={'poll_interval': daemon.POLL_INTERVAL})
255 256 assert thread.daemon is True
256 257 thread.start.assert_called_once_with()
257 258
258 259 def test_run_logs(self, tcp_server, caplog):
259 260
260 261 with self._tcp_patcher(tcp_server):
261 262 daemon = hooks_daemon.HttpHooksCallbackDaemon()
262 263
263 264 with self._thread_patcher(mock.Mock()), caplog.at_level(logging.DEBUG):
264 265 daemon._run()
265 266
266 267 assert_message_in_log(
267 268 caplog.records,
268 'Running event loop of callback daemon in background thread',
269 'Running thread-based loop of callback daemon in background',
269 270 levelno=logging.DEBUG, module='hooks_daemon')
270 271
271 272 def test_stop_cleans_up_the_connection(self, tcp_server, caplog):
272 273 thread = mock.Mock()
273 274
274 275 with self._tcp_patcher(tcp_server):
275 276 daemon = hooks_daemon.HttpHooksCallbackDaemon()
276 277
277 278 with self._thread_patcher(thread), caplog.at_level(logging.DEBUG):
278 279 with daemon:
279 280 assert daemon._daemon == tcp_server
280 281 assert daemon._callback_thread == thread
281 282
282 283 assert daemon._daemon is None
283 284 assert daemon._callback_thread is None
284 285 tcp_server.shutdown.assert_called_with()
285 286 thread.join.assert_called_once_with()
286 287
287 288 assert_message_in_log(
288 289 caplog.records, 'Waiting for background thread to finish.',
289 290 levelno=logging.DEBUG, module='hooks_daemon')
290 291
291 292 def _tcp_patcher(self, tcp_server):
292 293 return mock.patch.object(
293 294 hooks_daemon, 'TCPServer', return_value=tcp_server)
294 295
295 296 def _thread_patcher(self, thread):
296 297 return mock.patch.object(
297 298 hooks_daemon.threading, 'Thread', return_value=thread)
298 299
299 300
300 301 class TestPrepareHooksDaemon(object):
301 302 @pytest.mark.parametrize('protocol', ('http',))
302 303 def test_returns_dummy_hooks_callback_daemon_when_using_direct_calls(
303 304 self, protocol):
304 305 expected_extras = {'extra1': 'value1'}
305 306 callback, extras = hooks_daemon.prepare_callback_daemon(
306 307 expected_extras.copy(), protocol=protocol,
307 308 host='127.0.0.1', use_direct_calls=True)
308 309 assert isinstance(callback, hooks_daemon.DummyHooksCallbackDaemon)
309 310 expected_extras['hooks_module'] = 'rhodecode.lib.hooks_daemon'
310 311 expected_extras['time'] = extras['time']
311 312 assert 'extra1' in extras
312 313
313 314 @pytest.mark.parametrize('protocol, expected_class', (
314 315 ('http', hooks_daemon.HttpHooksCallbackDaemon),
315 316 ))
316 317 def test_returns_real_hooks_callback_daemon_when_protocol_is_specified(
317 318 self, protocol, expected_class):
318 319 expected_extras = {
319 320 'extra1': 'value1',
320 321 'txn_id': 'txnid2',
321 322 'hooks_protocol': protocol.lower()
322 323 }
323 324 callback, extras = hooks_daemon.prepare_callback_daemon(
324 325 expected_extras.copy(), protocol=protocol, host='127.0.0.1',
325 326 use_direct_calls=False,
326 327 txn_id='txnid2')
327 328 assert isinstance(callback, expected_class)
328 329 extras.pop('hooks_uri')
329 330 expected_extras['time'] = extras['time']
330 331 assert extras == expected_extras
331 332
332 333 @pytest.mark.parametrize('protocol', (
333 334 'invalid',
334 335 'Http',
335 336 'HTTP',
336 337 ))
337 338 def test_raises_on_invalid_protocol(self, protocol):
338 339 expected_extras = {
339 340 'extra1': 'value1',
340 341 'hooks_protocol': protocol.lower()
341 342 }
342 343 with pytest.raises(Exception):
343 344 callback, extras = hooks_daemon.prepare_callback_daemon(
344 345 expected_extras.copy(),
345 346 protocol=protocol, host='127.0.0.1',
346 347 use_direct_calls=False)
347 348
348 349
349 350 class MockRequest(object):
350 351
351 352 def __init__(self, request):
352 353 self.request = request
353 354 self.input_stream = io.BytesIO(safe_bytes(self.request))
354 355 self.output_stream = io.BytesIO() # make it un-closable for testing invesitagion
355 356 self.output_stream.close = lambda: None
356 357
357 358 def makefile(self, mode, *args, **kwargs):
358 359 return self.output_stream if mode == 'wb' else self.input_stream
359 360
360 361
361 362 class MockServer(object):
362 363
363 364 def __init__(self, handler_cls, request):
364 365 ip_port = ('0.0.0.0', 8888)
365 366 self.request = MockRequest(request)
366 367 self.server_address = ip_port
367 368 self.handler = handler_cls(self.request, ip_port, self)
368 369
369 370
370 371 @pytest.fixture()
371 372 def tcp_server():
372 373 server = mock.Mock()
373 374 server.server_address = ('127.0.0.1', 8881)
374 375 server.wbufsize = 1024
375 376 return server
General Comments 0
You need to be logged in to leave comments. Login now