Skip to content

Commit 4cea46b

Browse files
test: Add tests for chunked upload plan endpoint (box/box-codegen#975) (#1568)
1 parent 2cfdc80 commit 4cea46b

4 files changed

Lines changed: 103 additions & 5 deletions

File tree

‎.codegen.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{ "engineHash": "04310d4", "specHash": "88cd5aa", "version": "10.14.0" }
1+
{ "engineHash": "bcfc85f", "specHash": "88cd5aa", "version": "10.14.0" }

‎box_sdk_gen/internal/utils.py‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,17 +193,20 @@ def to_string(value: Any) -> Optional[str]:
193193

194194
class HashName(str, Enum):
195195
SHA1 = 'sha1'
196+
SHA512 = 'sha512'
196197

197198

198199
class Hash:
199200
def __init__(self, algorithm: HashName):
200201
self.algorithm = algorithm
201-
self.hash = hashlib.sha1()
202+
self.hash = hashlib.new(algorithm.value)
202203

203204
def update_hash(self, data: Buffer):
204205
self.hash.update(data)
205206

206207
def digest_hash(self, encoding):
208+
if encoding == 'hex':
209+
return self.hash.hexdigest()
207210
return base64.b64encode(self.hash.digest()).decode("utf-8")
208211

209212

‎docs/chunked_uploads.md‎

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,13 @@ This operation is performed by calling function `create_file_upload_session_for_
6161
See the endpoint docs at
6262
[API Reference](https://developer.box.com/reference/post-files-id-upload-sessions/).
6363

64-
_Currently we don't have an example for calling `create_file_upload_session_for_existing_file` in integration tests_
64+
<!-- sample post_files_id_upload_sessions -->
65+
66+
```python
67+
client.chunked_uploads.create_file_upload_session_for_existing_file(
68+
uploaded_file.id, file_size
69+
)
70+
```
6571

6672
### Arguments
6773

@@ -376,7 +382,11 @@ This operation is performed by calling function `create_file_upload_session_plan
376382
See the endpoint docs at
377383
[API Reference](https://developer.box.com/reference/post-files-upload-sessions-id-plan/).
378384

379-
_Currently we don't have an example for calling `create_file_upload_session_plan_by_url` in integration tests_
385+
<!-- sample post_files_upload_sessions_id_plan -->
386+
387+
```python
388+
client.chunked_uploads.create_file_upload_session_plan_by_url(plan_url, parts)
389+
```
380390

381391
### Arguments
382392

@@ -524,7 +534,10 @@ This operation is performed by calling function `upload_big_file`.
524534

525535
```python
526536
client.chunked_uploads.upload_big_file(
527-
file_byte_stream, file_name, file_size, parent_folder_id
537+
generate_byte_stream_from_buffer(file_buffer),
538+
file_name,
539+
file_size,
540+
parent_folder_id,
528541
)
529542
```
530543

‎test/chunked_uploads.py‎

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
from box_sdk_gen.internal.utils import Iterator
1212

13+
from box_sdk_gen.schemas.upload_part_plan_hit import UploadPartPlanHit
14+
1315
from box_sdk_gen.internal.utils import generate_byte_stream_from_buffer
1416

1517
from box_sdk_gen.internal.utils import hex_to_base_64
@@ -30,6 +32,8 @@
3032

3133
from box_sdk_gen.internal.utils import ByteStream
3234

35+
from box_sdk_gen.internal.utils import delay_in_seconds
36+
3337
from test.commons import get_default_client
3438

3539
from box_sdk_gen.schemas.file import File
@@ -38,10 +42,14 @@
3842

3943
from box_sdk_gen.schemas.upload_part import UploadPart
4044

45+
from box_sdk_gen.schemas.upload_part_plan import UploadPartPlan
46+
4147
from box_sdk_gen.schemas.upload_parts import UploadParts
4248

4349
from box_sdk_gen.schemas.uploaded_part import UploadedPart
4450

51+
from box_sdk_gen.schemas.upload_session_plan_response import UploadSessionPlanResponse
52+
4553
from box_sdk_gen.schemas.files import Files
4654

4755
from box_sdk_gen.client import BoxClient
@@ -253,6 +261,80 @@ def testChunkedManualProcessByUrl():
253261
client.chunked_uploads.delete_file_upload_session_by_url(abort_url)
254262

255263

264+
class _TestPartPlanAccumulator:
265+
def __init__(self, last_index: int, parts: List[UploadPartPlan], file_size: int):
266+
self.last_index = last_index
267+
self.parts = parts
268+
self.file_size = file_size
269+
270+
271+
def _reducer_for_upload_session_plan(
272+
acc: _TestPartPlanAccumulator, chunk: ByteStream
273+
) -> _TestPartPlanAccumulator:
274+
last_index: int = acc.last_index
275+
parts: List[UploadPartPlan] = acc.parts
276+
chunk_buffer: Buffer = read_byte_stream(chunk)
277+
hash: Hash = Hash(algorithm=HashName.SHA512)
278+
hash.update_hash(chunk_buffer)
279+
sha_512: str = hash.digest_hash('hex')
280+
chunk_size: int = buffer_length(chunk_buffer)
281+
bytes_start: int = last_index + 1
282+
bytes_end: int = last_index + chunk_size
283+
part: UploadPartPlan = UploadPartPlan(
284+
offset=bytes_start, size=chunk_size, sha_512=sha_512
285+
)
286+
return _TestPartPlanAccumulator(
287+
last_index=bytes_end, parts=parts + [part], file_size=acc.file_size
288+
)
289+
290+
291+
def testUploadSessionPlan():
292+
file_size: int = (20 * 1024) * 1024
293+
file_name: str = get_uuid()
294+
parent_folder_id: str = '0'
295+
file_content_stream: ByteStream = generate_byte_stream(file_size)
296+
file_buffer: Buffer = read_byte_stream(file_content_stream)
297+
uploaded_file: File = client.chunked_uploads.upload_big_file(
298+
generate_byte_stream_from_buffer(file_buffer),
299+
file_name,
300+
file_size,
301+
parent_folder_id,
302+
)
303+
delay_in_seconds(5)
304+
upload_session: UploadSession = (
305+
client.chunked_uploads.create_file_upload_session_for_existing_file(
306+
uploaded_file.id, file_size
307+
)
308+
)
309+
upload_session_id: str = upload_session.id
310+
plan_url: str = upload_session.session_endpoints.plan
311+
part_size: int = upload_session.part_size
312+
total_parts: int = upload_session.total_parts
313+
chunks_iterator: Iterator = iterate_chunks(
314+
generate_byte_stream_from_buffer(file_buffer), part_size, file_size
315+
)
316+
results: _TestPartPlanAccumulator = reduce_iterator(
317+
chunks_iterator,
318+
_reducer_for_upload_session_plan,
319+
_TestPartPlanAccumulator(last_index=-1, parts=[], file_size=file_size),
320+
)
321+
parts: List[UploadPartPlan] = results.parts
322+
plan: UploadSessionPlanResponse = (
323+
client.chunked_uploads.create_file_upload_session_plan_by_url(plan_url, parts)
324+
)
325+
assert plan.upload_session_id == upload_session_id
326+
assert len(plan.hits) == total_parts
327+
assert len(plan.misses) == 0
328+
first_part: UploadPartPlan = parts[0]
329+
first_hit: UploadPartPlanHit = plan.hits[0]
330+
assert first_hit.offset == first_part.offset
331+
assert first_hit.size == first_part.size
332+
assert first_hit.sha_512 == first_part.sha_512
333+
assert not first_hit.part_id == ''
334+
client.chunked_uploads.delete_file_upload_session_by_id(upload_session_id)
335+
client.files.delete_file_by_id(uploaded_file.id)
336+
337+
256338
def testChunkedUploadConvenienceMethod():
257339
file_size: int = (20 * 1024) * 1024
258340
file_byte_stream: ByteStream = generate_byte_stream(file_size)

0 commit comments

Comments
 (0)