1
1
import asyncio
2
2
import base64
3
- from unittest .mock import AsyncMock , MagicMock , PropertyMock
3
+ import textwrap
4
+ from tests .unit .sync .utils import create_async_with_result
5
+ from unittest .mock import AsyncMock , MagicMock , PropertyMock , call
4
6
5
7
import pytest
6
8
@@ -22,7 +24,7 @@ def test_init(client):
22
24
23
25
def test_delete (client : DBFSClient ):
24
26
session = MagicMock ()
25
- resp = MagicMock ()
27
+ resp = AsyncMock ()
26
28
setattr (type (resp ), "status" , PropertyMock (return_value = 200 ))
27
29
session .post .return_value = create_async_with_result (resp )
28
30
asyncio .run (client .delete (sub_path = "foo/bar" , session = session ))
@@ -39,7 +41,7 @@ def test_delete_secure(client: DBFSClient):
39
41
mock_config = mocked_props (token = "fake-token" , host = "http://fakehost.asdf/" , insecure = False )
40
42
client = DBFSClient (base_path = "/tmp/foo" , config = mock_config )
41
43
session = MagicMock ()
42
- resp = MagicMock ()
44
+ resp = AsyncMock ()
43
45
setattr (type (resp ), "status" , PropertyMock (return_value = 200 ))
44
46
session .post .return_value = create_async_with_result (resp )
45
47
asyncio .run (client .delete (sub_path = "foo/bar" , session = session ))
@@ -50,21 +52,6 @@ def test_delete_secure(client: DBFSClient):
50
52
assert session .post .call_args [1 ]["ssl" ] is True
51
53
52
54
53
- def test_delete_secure (client : DBFSClient ):
54
- mock_config = mocked_props (token = "fake-token" , host = "http://fakehost.asdf/" , insecure = True )
55
- client = DBFSClient (base_path = "/tmp/foo" , config = mock_config )
56
- session = MagicMock ()
57
- resp = MagicMock ()
58
- setattr (type (resp ), "status" , PropertyMock (return_value = 200 ))
59
- session .post .return_value = create_async_with_result (resp )
60
- asyncio .run (client .delete (sub_path = "foo/bar" , session = session ))
61
-
62
- assert session .post .call_count == 1
63
- assert session .post .call_args [1 ]["url" ] == "http://fakehost.asdf/api/2.0/dbfs/delete"
64
- assert session .post .call_args [1 ]["json" ] == {"path" : "dbfs:/tmp/foo/foo/bar" }
65
- assert session .post .call_args [1 ]["ssl" ] is False
66
-
67
-
68
55
def test_delete_backslash (client : DBFSClient ):
69
56
session = MagicMock ()
70
57
resp = MagicMock ()
@@ -82,7 +69,7 @@ def test_delete_no_path(client: DBFSClient):
82
69
83
70
def test_delete_recursive (client : DBFSClient ):
84
71
session = MagicMock ()
85
- resp = MagicMock ()
72
+ resp = AsyncMock ()
86
73
setattr (type (resp ), "status" , PropertyMock (return_value = 200 ))
87
74
session .post .return_value = create_async_with_result (resp )
88
75
asyncio .run (client .delete (sub_path = "foo/bar" , session = session , recursive = True ))
@@ -98,7 +85,7 @@ def test_delete_rate_limited(client: DBFSClient):
98
85
rate_limit_resp = MagicMock ()
99
86
setattr (type (rate_limit_resp ), "status" , PropertyMock (return_value = 429 ))
100
87
101
- success_resp = MagicMock ()
88
+ success_resp = AsyncMock ()
102
89
setattr (type (success_resp ), "status" , PropertyMock (return_value = 200 ))
103
90
setattr (type (rate_limit_resp ), "headers" , PropertyMock (return_value = {"Retry-After" : None }))
104
91
@@ -118,7 +105,7 @@ def test_delete_rate_limited_retry_after(client: DBFSClient):
118
105
setattr (type (rate_limit_resp ), "status" , PropertyMock (return_value = 429 ))
119
106
setattr (type (rate_limit_resp ), "headers" , PropertyMock (return_value = {"Retry-After" : 1 }))
120
107
121
- success_resp = MagicMock ()
108
+ success_resp = AsyncMock ()
122
109
setattr (type (success_resp ), "status" , PropertyMock (return_value = 200 ))
123
110
124
111
session .post .side_effect = [create_async_with_result (rate_limit_resp ), create_async_with_result (success_resp )]
@@ -146,7 +133,7 @@ def test_delete_unauthorized(client: DBFSClient):
146
133
147
134
def test_mkdirs (client : DBFSClient ):
148
135
session = MagicMock ()
149
- resp = MagicMock ()
136
+ resp = AsyncMock ()
150
137
setattr (type (resp ), "status" , PropertyMock (return_value = 200 ))
151
138
session .post .return_value = create_async_with_result (resp )
152
139
asyncio .run (client .mkdirs (sub_path = "foo/bar" , session = session ))
@@ -179,7 +166,7 @@ def test_mkdirs_rate_limited(client: DBFSClient):
179
166
rate_limit_resp = MagicMock ()
180
167
setattr (type (rate_limit_resp ), "status" , PropertyMock (return_value = 429 ))
181
168
182
- success_resp = MagicMock ()
169
+ success_resp = AsyncMock ()
183
170
setattr (type (success_resp ), "status" , PropertyMock (return_value = 200 ))
184
171
setattr (type (rate_limit_resp ), "headers" , PropertyMock (return_value = {"Retry-After" : None }))
185
172
@@ -199,7 +186,7 @@ def test_mkdirs_rate_limited_retry_after(client: DBFSClient):
199
186
setattr (type (rate_limit_resp ), "status" , PropertyMock (return_value = 429 ))
200
187
setattr (type (rate_limit_resp ), "headers" , PropertyMock (return_value = {"Retry-After" : 1 }))
201
188
202
- success_resp = MagicMock ()
189
+ success_resp = AsyncMock ()
203
190
setattr (type (success_resp ), "status" , PropertyMock (return_value = 200 ))
204
191
205
192
session .post .side_effect = [create_async_with_result (rate_limit_resp ), create_async_with_result (success_resp )]
@@ -227,7 +214,7 @@ def test_mkdirs_unauthorized(client: DBFSClient):
227
214
228
215
def test_put (client : DBFSClient , dummy_file_path : str ):
229
216
session = MagicMock ()
230
- resp = MagicMock ()
217
+ resp = AsyncMock ()
231
218
setattr (type (resp ), "status" , PropertyMock (return_value = 200 ))
232
219
session .post .return_value = create_async_with_result (resp )
233
220
@@ -244,6 +231,61 @@ def test_put(client: DBFSClient, dummy_file_path: str):
244
231
assert is_dbfs_user_agent (session .post .call_args [1 ]["headers" ]["user-agent" ])
245
232
246
233
234
+ def test_put_max_block_size_exceeded (client : DBFSClient , dummy_file_path_2mb : str ):
235
+ expected_handle = 1234
236
+
237
+ async def mock_json (* args , ** kwargs ):
238
+ return {"handle" : expected_handle }
239
+
240
+ def mock_post (url , * args , ** kwargs ):
241
+ resp = AsyncMock ()
242
+ setattr (type (resp ), "status" , PropertyMock (return_value = 200 ))
243
+ if "/api/2.0/dbfs/put" in url :
244
+ contents = kwargs .get ("json" ).get ("contents" )
245
+ if len (contents ) > 1024 * 1024 : # replicate the api error thrown when contents exceeds max allowed
246
+ setattr (type (resp ), "status" , PropertyMock (return_value = 400 ))
247
+ elif "/api/2.0/dbfs/create" in url :
248
+ # return a mock response json
249
+ resp .json = MagicMock (side_effect = mock_json )
250
+
251
+ return create_async_with_result (resp )
252
+
253
+ session = AsyncMock ()
254
+ post = MagicMock (side_effect = mock_post )
255
+ session .post = post
256
+
257
+ asyncio .run (client .put (sub_path = "foo/bar" , full_source_path = dummy_file_path_2mb , session = session ))
258
+
259
+ with open (dummy_file_path_2mb , "r" ) as f :
260
+ expected_contents = f .read ()
261
+
262
+ chunks = textwrap .wrap (base64 .b64encode (bytes (expected_contents , encoding = "utf8" )).decode ("ascii" ), 1024 * 1024 )
263
+
264
+ assert session .post .call_count == len (chunks ) + 2
265
+ assert session .post .call_args_list [0 ][1 ]["url" ] == "http://fakehost.asdf/api/2.0/dbfs/create"
266
+ assert session .post .call_args_list [1 ][1 ]["url" ] == "http://fakehost.asdf/api/2.0/dbfs/add-block"
267
+ assert session .post .call_args_list [2 ][1 ]["url" ] == "http://fakehost.asdf/api/2.0/dbfs/add-block"
268
+ assert session .post .call_args_list [3 ][1 ]["url" ] == "http://fakehost.asdf/api/2.0/dbfs/add-block"
269
+ assert session .post .call_args_list [4 ][1 ]["url" ] == "http://fakehost.asdf/api/2.0/dbfs/close"
270
+
271
+ assert session .post .call_args_list [0 ][1 ]["json" ] == {
272
+ "path" : "dbfs:/tmp/foo/foo/bar" ,
273
+ "overwrite" : True ,
274
+ }
275
+
276
+ for i , chunk in enumerate (chunks ):
277
+ assert session .post .call_args_list [i + 1 ][1 ]["json" ] == {
278
+ "data" : chunk ,
279
+ "path" : "dbfs:/tmp/foo/foo/bar" ,
280
+ "handle" : expected_handle ,
281
+ }, f"invalid json for chunk { i } "
282
+
283
+ assert session .post .call_args_list [4 ][1 ]["json" ] == {
284
+ "path" : "dbfs:/tmp/foo/foo/bar" ,
285
+ "handle" : expected_handle ,
286
+ }
287
+
288
+
247
289
def test_put_backslash (client : DBFSClient , dummy_file_path : str ):
248
290
session = MagicMock ()
249
291
resp = MagicMock ()
@@ -267,7 +309,7 @@ def test_put_rate_limited(client: DBFSClient, dummy_file_path: str):
267
309
rate_limit_resp = MagicMock ()
268
310
setattr (type (rate_limit_resp ), "status" , PropertyMock (return_value = 429 ))
269
311
270
- success_resp = MagicMock ()
312
+ success_resp = AsyncMock ()
271
313
setattr (type (success_resp ), "status" , PropertyMock (return_value = 200 ))
272
314
setattr (type (rate_limit_resp ), "headers" , PropertyMock (return_value = {"Retry-After" : None }))
273
315
@@ -291,7 +333,7 @@ def test_put_rate_limited_retry_after(client: DBFSClient, dummy_file_path: str):
291
333
setattr (type (rate_limit_resp ), "status" , PropertyMock (return_value = 429 ))
292
334
setattr (type (rate_limit_resp ), "headers" , PropertyMock (return_value = {"Retry-After" : 1 }))
293
335
294
- success_resp = MagicMock ()
336
+ success_resp = AsyncMock ()
295
337
setattr (type (success_resp ), "status" , PropertyMock (return_value = 200 ))
296
338
297
339
session .post .side_effect = [create_async_with_result (rate_limit_resp ), create_async_with_result (success_resp )]
0 commit comments