107
90
# Create a SmartServerRequestHandler with a SmartServerRequest subclass
108
91
# that does not implement do_body.
109
92
handler = request.SmartServerRequestHandler(
110
None, {b'foo': NoBodyRequest}, '/')
93
None, {'foo': NoBodyRequest}, '/')
111
94
# Emulate a request with no body (i.e. just args).
112
handler.args_received((b'foo',))
95
handler.args_received(('foo',))
113
96
handler.end_received()
114
97
# Request done, no exception was raised.
116
99
def test_only_request_code_is_jailed(self):
117
100
transport = 'dummy transport'
118
101
handler = request.SmartServerRequestHandler(
119
transport, {b'foo': CheckJailRequest}, '/')
120
handler.args_received((b'foo',))
102
transport, {'foo': CheckJailRequest}, '/')
103
handler.args_received(('foo',))
121
104
self.assertEqual(None, request.jail_info.transports)
122
handler.accept_body(b'bytes')
105
handler.accept_body('bytes')
123
106
self.assertEqual(None, request.jail_info.transports)
124
107
handler.end_received()
125
108
self.assertEqual(None, request.jail_info.transports)
126
109
self.assertEqual(
127
110
[[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,))
141
114
class TestSmartRequestHandlerErrorTranslation(TestCase):
147
120
self.assertEqual(None, handler.response)
149
122
def assertResponseIsTranslatedError(self, handler):
150
expected_translation = (b'NoSuchFile', b'xyzzy')
123
expected_translation = ('NoSuchFile', 'xyzzy')
151
124
self.assertEqual(
152
125
request.FailedSmartServerResponse(expected_translation),
153
126
handler.response)
155
128
def test_error_translation_from_args_received(self):
156
129
handler = request.SmartServerRequestHandler(
157
None, {b'foo': DoErrorRequest}, '/')
158
handler.args_received((b'foo',))
130
None, {'foo': DoErrorRequest}, '/')
131
handler.args_received(('foo',))
159
132
self.assertResponseIsTranslatedError(handler)
161
134
def test_error_translation_from_chunk_received(self):
162
135
handler = request.SmartServerRequestHandler(
163
None, {b'foo': ChunkErrorRequest}, '/')
164
handler.args_received((b'foo',))
136
None, {'foo': ChunkErrorRequest}, '/')
137
handler.args_received(('foo',))
165
138
self.assertNoResponse(handler)
166
handler.accept_body(b'bytes')
139
handler.accept_body('bytes')
167
140
self.assertResponseIsTranslatedError(handler)
169
142
def test_error_translation_from_end_received(self):
170
143
handler = request.SmartServerRequestHandler(
171
None, {b'foo': EndErrorRequest}, '/')
172
handler.args_received((b'foo',))
144
None, {'foo': EndErrorRequest}, '/')
145
handler.args_received(('foo',))
173
146
self.assertNoResponse(handler)
174
147
handler.end_received()
175
148
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")),
186
151
class TestRequestHanderErrorTranslation(TestCase):
187
"""Tests for breezy.bzr.smart.request._translate_error."""
152
"""Tests for bzrlib.smart.request._translate_error."""
189
154
def assertTranslationEqual(self, expected_tuple, error):
190
155
self.assertEqual(expected_tuple, request._translate_error(error))
192
157
def test_NoSuchFile(self):
193
158
self.assertTranslationEqual(
194
(b'NoSuchFile', b'path'), errors.NoSuchFile('path'))
159
('NoSuchFile', 'path'), errors.NoSuchFile('path'))
196
161
def test_LockContention(self):
197
162
# For now, LockContentions are always transmitted with no details.
198
163
# Eventually they should include a relpath or url or something else to
199
164
# identify which lock is busy.
200
165
self.assertTranslationEqual(
201
(b'LockContention',), errors.LockContention('lock', 'msg'))
166
('LockContention',), errors.LockContention('lock', 'msg'))
203
168
def test_TokenMismatch(self):
204
169
self.assertTranslationEqual(
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')),
170
('TokenMismatch', 'some-token', 'actual-token'),
171
errors.TokenMismatch('some-token', 'actual-token'))
231
174
class TestRequestJail(TestCaseWithMemoryTransport):
233
176
def test_jail(self):
234
177
transport = self.get_transport('blah')
235
178
req = request.SmartServerRequest(transport)
263
203
# A parent is not allowed
264
204
self.assertRaises(errors.JailBreak, _pre_open_hook, t.clone('..'))
265
205
# A completely unrelated transport is not allowed
266
self.assertRaises(errors.JailBreak, _pre_open_hook,
267
transport.get_transport_from_url('http://host/'))
207
errors.JailBreak, _pre_open_hook, get_transport('http://host/'))
269
209
def test_open_bzrdir_in_non_main_thread(self):
270
210
"""Opening a bzrdir in a non-main thread should work ok.
272
212
This makes sure that the globally-installed
273
breezy.bzr.smart.request._pre_open_hook, which uses a threading.local(),
213
bzrlib.smart.request._pre_open_hook, which uses a threading.local(),
274
214
works in a newly created thread.
276
bzrdir = self.make_controldir('.')
216
bzrdir = self.make_bzrdir('.')
277
217
transport = bzrdir.root_transport
278
218
thread_result = []
281
220
BzrDir.open_from_transport(transport)
282
221
thread_result.append('ok')