You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

127 lines
4.1 KiB

import base64
from pathlib import Path
import pytest
from sqlalchemy import select
from app.models.entities import Backend, BlobReplica
def _encode_metadata(value: str) -> str:
return base64.b64encode(value.encode("utf-8")).decode("utf-8")
@pytest.mark.anyio
async def test_tus_create_head_patch_and_finalize(api_client, session_factory, runtime_paths) -> None:
metadata_header = f"filename {_encode_metadata('movie.mp4')}"
create_response = await api_client.post(
"/files",
headers={
"Tus-Resumable": "1.0.0",
"Upload-Length": "11",
"Upload-Metadata": metadata_header,
},
)
assert create_response.status_code == 201
upload_location = create_response.headers["Location"]
upload_id = upload_location.rsplit("/", 1)[-1]
head_response = await api_client.head(
f"/files/{upload_id}",
headers={"Tus-Resumable": "1.0.0"},
)
assert head_response.status_code == 200
assert head_response.headers["Upload-Offset"] == "0"
assert head_response.headers["Upload-Length"] == "11"
patch_response = await api_client.patch(
f"/files/{upload_id}",
headers={
"Tus-Resumable": "1.0.0",
"Upload-Offset": "0",
"Content-Type": "application/offset+octet-stream",
},
content=b"hello world",
)
assert patch_response.status_code == 204
assert patch_response.headers["Upload-Offset"] == "11"
finalize_response = await api_client.post(
f"/api/uploads/{upload_id}/finalize",
json={"directory_id": "dir_root", "filename": "movie.mp4"},
)
assert finalize_response.status_code == 200
payload = finalize_response.json()
assert payload["upload"]["status"] == "finalized"
assert payload["file"]["name"] == "movie.mp4"
assert payload["file"]["size_bytes"] == 11
detail_response = await api_client.get(f"/api/files/{payload['file']['id']}")
assert detail_response.status_code == 200
assert detail_response.json()["file"]["name"] == "movie.mp4"
async with session_factory() as session:
backend = (
await session.execute(select(Backend).where(Backend.name == "local-default"))
).scalar_one()
replica = (await session.execute(select(BlobReplica).where(BlobReplica.backend_id == backend.id))).scalar_one()
stored_path = Path(runtime_paths["local_storage_dir"]) / replica.storage_key
assert stored_path.exists()
assert stored_path.read_bytes() == b"hello world"
@pytest.mark.anyio
async def test_tus_patch_rejects_wrong_offset(api_client) -> None:
create_response = await api_client.post(
"/files",
headers={
"Tus-Resumable": "1.0.0",
"Upload-Length": "5",
},
)
upload_id = create_response.headers["Location"].rsplit("/", 1)[-1]
patch_response = await api_client.patch(
f"/files/{upload_id}",
headers={
"Tus-Resumable": "1.0.0",
"Upload-Offset": "3",
"Content-Type": "application/offset+octet-stream",
},
content=b"hello",
)
assert patch_response.status_code == 409
assert patch_response.json()["error"]["code"] == "upload_conflict"
@pytest.mark.anyio
async def test_finalize_requires_complete_upload(api_client) -> None:
create_response = await api_client.post(
"/files",
headers={
"Tus-Resumable": "1.0.0",
"Upload-Length": "10",
},
)
upload_id = create_response.headers["Location"].rsplit("/", 1)[-1]
patch_response = await api_client.patch(
f"/files/{upload_id}",
headers={
"Tus-Resumable": "1.0.0",
"Upload-Offset": "0",
"Content-Type": "application/offset+octet-stream",
},
content=b"abc",
)
assert patch_response.status_code == 204
finalize_response = await api_client.post(
f"/api/uploads/{upload_id}/finalize",
json={"directory_id": "dir_root", "filename": "partial.bin"},
)
assert finalize_response.status_code == 409
assert finalize_response.json()["error"]["code"] == "upload_conflict"