11import asyncio
22import 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
46
57import pytest
68
@@ -22,7 +24,7 @@ def test_init(client):
2224
2325def test_delete (client : DBFSClient ):
2426 session = MagicMock ()
25- resp = MagicMock ()
27+ resp = AsyncMock ()
2628 setattr (type (resp ), "status" , PropertyMock (return_value = 200 ))
2729 session .post .return_value = create_async_with_result (resp )
2830 asyncio .run (client .delete (sub_path = "foo/bar" , session = session ))
@@ -39,7 +41,7 @@ def test_delete_secure(client: DBFSClient):
3941 mock_config = mocked_props (token = "fake-token" , host = "http://fakehost.asdf/" , insecure = False )
4042 client = DBFSClient (base_path = "/tmp/foo" , config = mock_config )
4143 session = MagicMock ()
42- resp = MagicMock ()
44+ resp = AsyncMock ()
4345 setattr (type (resp ), "status" , PropertyMock (return_value = 200 ))
4446 session .post .return_value = create_async_with_result (resp )
4547 asyncio .run (client .delete (sub_path = "foo/bar" , session = session ))
@@ -50,21 +52,6 @@ def test_delete_secure(client: DBFSClient):
5052 assert session .post .call_args [1 ]["ssl" ] is True
5153
5254
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-
6855def test_delete_backslash (client : DBFSClient ):
6956 session = MagicMock ()
7057 resp = MagicMock ()
@@ -82,7 +69,7 @@ def test_delete_no_path(client: DBFSClient):
8269
8370def test_delete_recursive (client : DBFSClient ):
8471 session = MagicMock ()
85- resp = MagicMock ()
72+ resp = AsyncMock ()
8673 setattr (type (resp ), "status" , PropertyMock (return_value = 200 ))
8774 session .post .return_value = create_async_with_result (resp )
8875 asyncio .run (client .delete (sub_path = "foo/bar" , session = session , recursive = True ))
@@ -98,7 +85,7 @@ def test_delete_rate_limited(client: DBFSClient):
9885 rate_limit_resp = MagicMock ()
9986 setattr (type (rate_limit_resp ), "status" , PropertyMock (return_value = 429 ))
10087
101- success_resp = MagicMock ()
88+ success_resp = AsyncMock ()
10289 setattr (type (success_resp ), "status" , PropertyMock (return_value = 200 ))
10390 setattr (type (rate_limit_resp ), "headers" , PropertyMock (return_value = {"Retry-After" : None }))
10491
@@ -118,7 +105,7 @@ def test_delete_rate_limited_retry_after(client: DBFSClient):
118105 setattr (type (rate_limit_resp ), "status" , PropertyMock (return_value = 429 ))
119106 setattr (type (rate_limit_resp ), "headers" , PropertyMock (return_value = {"Retry-After" : 1 }))
120107
121- success_resp = MagicMock ()
108+ success_resp = AsyncMock ()
122109 setattr (type (success_resp ), "status" , PropertyMock (return_value = 200 ))
123110
124111 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):
146133
147134def test_mkdirs (client : DBFSClient ):
148135 session = MagicMock ()
149- resp = MagicMock ()
136+ resp = AsyncMock ()
150137 setattr (type (resp ), "status" , PropertyMock (return_value = 200 ))
151138 session .post .return_value = create_async_with_result (resp )
152139 asyncio .run (client .mkdirs (sub_path = "foo/bar" , session = session ))
@@ -179,7 +166,7 @@ def test_mkdirs_rate_limited(client: DBFSClient):
179166 rate_limit_resp = MagicMock ()
180167 setattr (type (rate_limit_resp ), "status" , PropertyMock (return_value = 429 ))
181168
182- success_resp = MagicMock ()
169+ success_resp = AsyncMock ()
183170 setattr (type (success_resp ), "status" , PropertyMock (return_value = 200 ))
184171 setattr (type (rate_limit_resp ), "headers" , PropertyMock (return_value = {"Retry-After" : None }))
185172
@@ -199,7 +186,7 @@ def test_mkdirs_rate_limited_retry_after(client: DBFSClient):
199186 setattr (type (rate_limit_resp ), "status" , PropertyMock (return_value = 429 ))
200187 setattr (type (rate_limit_resp ), "headers" , PropertyMock (return_value = {"Retry-After" : 1 }))
201188
202- success_resp = MagicMock ()
189+ success_resp = AsyncMock ()
203190 setattr (type (success_resp ), "status" , PropertyMock (return_value = 200 ))
204191
205192 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):
227214
228215def test_put (client : DBFSClient , dummy_file_path : str ):
229216 session = MagicMock ()
230- resp = MagicMock ()
217+ resp = AsyncMock ()
231218 setattr (type (resp ), "status" , PropertyMock (return_value = 200 ))
232219 session .post .return_value = create_async_with_result (resp )
233220
@@ -244,6 +231,61 @@ def test_put(client: DBFSClient, dummy_file_path: str):
244231 assert is_dbfs_user_agent (session .post .call_args [1 ]["headers" ]["user-agent" ])
245232
246233
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+
247289def test_put_backslash (client : DBFSClient , dummy_file_path : str ):
248290 session = MagicMock ()
249291 resp = MagicMock ()
@@ -267,7 +309,7 @@ def test_put_rate_limited(client: DBFSClient, dummy_file_path: str):
267309 rate_limit_resp = MagicMock ()
268310 setattr (type (rate_limit_resp ), "status" , PropertyMock (return_value = 429 ))
269311
270- success_resp = MagicMock ()
312+ success_resp = AsyncMock ()
271313 setattr (type (success_resp ), "status" , PropertyMock (return_value = 200 ))
272314 setattr (type (rate_limit_resp ), "headers" , PropertyMock (return_value = {"Retry-After" : None }))
273315
@@ -291,7 +333,7 @@ def test_put_rate_limited_retry_after(client: DBFSClient, dummy_file_path: str):
291333 setattr (type (rate_limit_resp ), "status" , PropertyMock (return_value = 429 ))
292334 setattr (type (rate_limit_resp ), "headers" , PropertyMock (return_value = {"Retry-After" : 1 }))
293335
294- success_resp = MagicMock ()
336+ success_resp = AsyncMock ()
295337 setattr (type (success_resp ), "status" , PropertyMock (return_value = 200 ))
296338
297339 session .post .side_effect = [create_async_with_result (rate_limit_resp ), create_async_with_result (success_resp )]
0 commit comments