90
107
# Create a SmartServerRequestHandler with a SmartServerRequest subclass
91
108
# that does not implement do_body.
92
109
handler = request.SmartServerRequestHandler(
93
None, {'foo': NoBodyRequest}, '/')
110
None, {b'foo': NoBodyRequest}, '/')
94
111
# Emulate a request with no body (i.e. just args).
95
handler.args_received(('foo',))
112
handler.args_received((b'foo',))
96
113
handler.end_received()
97
114
# Request done, no exception was raised.
99
116
def test_only_request_code_is_jailed(self):
100
117
transport = 'dummy transport'
101
118
handler = request.SmartServerRequestHandler(
102
transport, {'foo': CheckJailRequest}, '/')
103
handler.args_received(('foo',))
119
transport, {b'foo': CheckJailRequest}, '/')
120
handler.args_received((b'foo',))
104
121
self.assertEqual(None, request.jail_info.transports)
105
handler.accept_body('bytes')
122
handler.accept_body(b'bytes')
106
123
self.assertEqual(None, request.jail_info.transports)
107
124
handler.end_received()
108
125
self.assertEqual(None, request.jail_info.transports)
109
126
self.assertEqual(
110
127
[[transport]] * 3, handler._command.jail_transports_log)
129
def test_all_registered_requests_are_safety_qualified(self):
130
unclassified_requests = []
131
allowed_info = ('read', 'idem', 'mutate', 'semivfs', 'semi', 'stream')
132
for key in request.request_handlers.keys():
133
info = request.request_handlers.get_info(key)
134
if info is None or info not in allowed_info:
135
unclassified_requests.append(key)
136
if unclassified_requests:
137
self.fail('These requests were not categorized as safe/unsafe'
138
' to retry: %s' % (unclassified_requests,))
114
141
class TestSmartRequestHandlerErrorTranslation(TestCase):
120
147
self.assertEqual(None, handler.response)
122
149
def assertResponseIsTranslatedError(self, handler):
123
expected_translation = ('NoSuchFile', 'xyzzy')
150
expected_translation = (b'NoSuchFile', b'xyzzy')
124
151
self.assertEqual(
125
152
request.FailedSmartServerResponse(expected_translation),
126
153
handler.response)
128
155
def test_error_translation_from_args_received(self):
129
156
handler = request.SmartServerRequestHandler(
130
None, {'foo': DoErrorRequest}, '/')
131
handler.args_received(('foo',))
157
None, {b'foo': DoErrorRequest}, '/')
158
handler.args_received((b'foo',))
132
159
self.assertResponseIsTranslatedError(handler)
134
161
def test_error_translation_from_chunk_received(self):
135
162
handler = request.SmartServerRequestHandler(
136
None, {'foo': ChunkErrorRequest}, '/')
137
handler.args_received(('foo',))
163
None, {b'foo': ChunkErrorRequest}, '/')
164
handler.args_received((b'foo',))
138
165
self.assertNoResponse(handler)
139
handler.accept_body('bytes')
166
handler.accept_body(b'bytes')
140
167
self.assertResponseIsTranslatedError(handler)
142
169
def test_error_translation_from_end_received(self):
143
170
handler = request.SmartServerRequestHandler(
144
None, {'foo': EndErrorRequest}, '/')
145
handler.args_received(('foo',))
171
None, {b'foo': EndErrorRequest}, '/')
172
handler.args_received((b'foo',))
146
173
self.assertNoResponse(handler)
147
174
handler.end_received()
148
175
self.assertResponseIsTranslatedError(handler)
177
def test_unexpected_error_translation(self):
178
handler = request.SmartServerRequestHandler(
179
None, {b'foo': DoUnexpectedErrorRequest}, '/')
180
handler.args_received((b'foo',))
182
request.FailedSmartServerResponse((b'error', b'KeyError', b"1")),
151
186
class TestRequestHanderErrorTranslation(TestCase):
152
"""Tests for bzrlib.smart.request._translate_error."""
187
"""Tests for breezy.bzr.smart.request._translate_error."""
154
189
def assertTranslationEqual(self, expected_tuple, error):
155
190
self.assertEqual(expected_tuple, request._translate_error(error))
157
192
def test_NoSuchFile(self):
158
193
self.assertTranslationEqual(
159
('NoSuchFile', 'path'), errors.NoSuchFile('path'))
194
(b'NoSuchFile', b'path'), errors.NoSuchFile('path'))
161
196
def test_LockContention(self):
162
197
# For now, LockContentions are always transmitted with no details.
163
198
# Eventually they should include a relpath or url or something else to
164
199
# identify which lock is busy.
165
200
self.assertTranslationEqual(
166
('LockContention',), errors.LockContention('lock', 'msg'))
201
(b'LockContention',), errors.LockContention('lock', 'msg'))
168
203
def test_TokenMismatch(self):
169
204
self.assertTranslationEqual(
170
('TokenMismatch', 'some-token', 'actual-token'),
171
errors.TokenMismatch('some-token', 'actual-token'))
205
(b'TokenMismatch', b'some-token', b'actual-token'),
206
errors.TokenMismatch(b'some-token', b'actual-token'))
208
def test_MemoryError(self):
209
self.assertTranslationEqual((b"MemoryError",), MemoryError())
211
def test_GhostRevisionsHaveNoRevno(self):
212
self.assertTranslationEqual(
213
(b"GhostRevisionsHaveNoRevno", b'revid1', b'revid2'),
214
errors.GhostRevisionsHaveNoRevno(b'revid1', b'revid2'))
216
def test_generic_Exception(self):
217
self.assertTranslationEqual((b'error', b'Exception', b""),
220
def test_generic_BzrError(self):
221
self.assertTranslationEqual((b'error', b'BzrError', b"some text"),
222
errors.BzrError(msg="some text"))
224
def test_generic_zlib_error(self):
225
from zlib import error
226
msg = "Error -3 while decompressing data: incorrect data check"
227
self.assertTranslationEqual((b'error', b'zlib.error', msg.encode('utf-8')),
174
231
class TestRequestJail(TestCaseWithMemoryTransport):
176
233
def test_jail(self):
177
234
transport = self.get_transport('blah')
178
235
req = request.SmartServerRequest(transport)
203
263
# A parent is not allowed
204
264
self.assertRaises(errors.JailBreak, _pre_open_hook, t.clone('..'))
205
265
# A completely unrelated transport is not allowed
207
errors.JailBreak, _pre_open_hook, get_transport('http://host/'))
266
self.assertRaises(errors.JailBreak, _pre_open_hook,
267
transport.get_transport_from_url('http://host/'))
209
269
def test_open_bzrdir_in_non_main_thread(self):
210
270
"""Opening a bzrdir in a non-main thread should work ok.
212
272
This makes sure that the globally-installed
213
bzrlib.smart.request._pre_open_hook, which uses a threading.local(),
273
breezy.bzr.smart.request._pre_open_hook, which uses a threading.local(),
214
274
works in a newly created thread.
216
bzrdir = self.make_bzrdir('.')
276
bzrdir = self.make_controldir('.')
217
277
transport = bzrdir.root_transport
218
278
thread_result = []
220
281
BzrDir.open_from_transport(transport)
221
282
thread_result.append('ok')