14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
"""Tests for smart server request infrastructure (bzrlib.smart.request)."""
17
"""Tests for smart server request infrastructure (breezy.bzr.smart.request)."""
21
from bzrlib import errors
22
from bzrlib.bzrdir import BzrDir
23
from bzrlib.smart import request
24
from bzrlib.tests import TestCase, TestCaseWithMemoryTransport
25
from bzrlib.transport import get_transport
25
from breezy.bzr.bzrdir import BzrDir
26
from breezy.bzr.smart import request
27
from breezy.tests import TestCase, TestCaseWithMemoryTransport
28
30
class NoBodyRequest(request.SmartServerRequest):
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_generic_Exception(self):
212
self.assertTranslationEqual((b'error', b'Exception', b""),
215
def test_generic_BzrError(self):
216
self.assertTranslationEqual((b'error', b'BzrError', b"some text"),
217
errors.BzrError(msg="some text"))
219
def test_generic_zlib_error(self):
220
from zlib import error
221
msg = "Error -3 while decompressing data: incorrect data check"
222
self.assertTranslationEqual((b'error', b'zlib.error', msg.encode('utf-8')),
174
226
class TestRequestJail(TestCaseWithMemoryTransport):
176
228
def test_jail(self):
177
229
transport = self.get_transport('blah')
178
230
req = request.SmartServerRequest(transport)
203
257
# A parent is not allowed
204
258
self.assertRaises(errors.JailBreak, _pre_open_hook, t.clone('..'))
205
259
# A completely unrelated transport is not allowed
207
errors.JailBreak, _pre_open_hook, get_transport('http://host/'))
260
self.assertRaises(errors.JailBreak, _pre_open_hook,
261
transport.get_transport_from_url('http://host/'))
209
263
def test_open_bzrdir_in_non_main_thread(self):
210
264
"""Opening a bzrdir in a non-main thread should work ok.
212
266
This makes sure that the globally-installed
213
bzrlib.smart.request._pre_open_hook, which uses a threading.local(),
267
breezy.bzr.smart.request._pre_open_hook, which uses a threading.local(),
214
268
works in a newly created thread.
216
bzrdir = self.make_bzrdir('.')
270
bzrdir = self.make_controldir('.')
217
271
transport = bzrdir.root_transport
218
272
thread_result = []