444dc0a6d1
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
110 lines
3.4 KiB
Python
110 lines
3.4 KiB
Python
"""#99: the vision routes accept BMP (the Opto bench cameras save it); TIFF and
|
|
non-images stay refused. files.py (attachments) is not touched by this."""
|
|
import io
|
|
|
|
import pytest
|
|
from PIL import Image
|
|
from sqlalchemy import select
|
|
|
|
from src.backend.config import settings
|
|
from src.backend.models.orm.vision import VisionReferenceImage
|
|
from src.backend.tests.conftest import auth_headers, create_test_recipe
|
|
from src.backend.tests.test_vision_upload_limits import ( # noqa: F401 - fixtures
|
|
recipe_parts, redirect_uploads, worker_says,
|
|
)
|
|
|
|
|
|
def a_bmp() -> bytes:
|
|
buffer = io.BytesIO()
|
|
Image.new("L", (64, 64), color=0).save(buffer, format="BMP")
|
|
return buffer.getvalue()
|
|
|
|
|
|
def a_tiff() -> bytes:
|
|
buffer = io.BytesIO()
|
|
Image.new("L", (64, 64), color=0).save(buffer, format="TIFF")
|
|
return buffer.getvalue()
|
|
|
|
|
|
async def a_camera_task(db_session, user):
|
|
recipe = await create_test_recipe(db_session, user.id)
|
|
_, task, _ = await recipe_parts(db_session, recipe.id)
|
|
task.task_type = "camera_measure"
|
|
task.vision_json = {"schema_version": 1, "name": "p", "tools": []}
|
|
await db_session.commit()
|
|
return task
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.parametrize("content_type", ["image/bmp", "image/x-ms-bmp"])
|
|
async def test_preview_accepts_a_bmp(
|
|
client, db_session, maker_user, worker_says, content_type,
|
|
):
|
|
task = await a_camera_task(db_session, maker_user)
|
|
worker_says({"t1.diameter": 10.0})
|
|
|
|
response = await client.post(
|
|
"/api/vision/preview",
|
|
headers=auth_headers(maker_user),
|
|
files={"image": ("p.bmp", a_bmp(), content_type)},
|
|
data={"task_id": str(task.id)},
|
|
)
|
|
|
|
assert response.status_code != 400, response.text
|
|
assert response.status_code != 422, response.text
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_preview_still_rejects_a_tiff(
|
|
client, db_session, maker_user, worker_says,
|
|
):
|
|
task = await a_camera_task(db_session, maker_user)
|
|
worker_says({"t1.diameter": 10.0})
|
|
|
|
response = await client.post(
|
|
"/api/vision/preview",
|
|
headers=auth_headers(maker_user),
|
|
files={"image": ("p.tif", a_tiff(), "image/tiff")},
|
|
data={"task_id": str(task.id)},
|
|
)
|
|
|
|
assert response.status_code == 400
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_a_text_file_declared_bmp_is_refused_on_its_bytes(
|
|
client, db_session, maker_user, worker_says,
|
|
):
|
|
task = await a_camera_task(db_session, maker_user)
|
|
worker_says({"t1.diameter": 10.0})
|
|
|
|
response = await client.post(
|
|
"/api/vision/preview",
|
|
headers=auth_headers(maker_user),
|
|
files={"image": ("p.bmp", b"i am not an image", "image/bmp")},
|
|
data={"task_id": str(task.id)},
|
|
)
|
|
|
|
assert response.status_code == 422
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_reference_image_bmp_is_saved_with_the_bmp_extension(
|
|
client, db_session, maker_user,
|
|
):
|
|
recipe = await create_test_recipe(db_session, maker_user.id)
|
|
_, task, _ = await recipe_parts(db_session, recipe.id)
|
|
await db_session.commit()
|
|
|
|
response = await client.post(
|
|
"/api/vision/reference-images",
|
|
headers=auth_headers(maker_user),
|
|
files={"image": ("p.bmp", a_bmp(), "image/x-ms-bmp")},
|
|
data={"task_id": str(task.id)},
|
|
)
|
|
|
|
assert response.status_code == 200, response.text
|
|
saved = (await db_session.execute(select(VisionReferenceImage))).scalars().one()
|
|
assert saved.path.endswith(".bmp")
|
|
assert (settings.upload_path / saved.path).exists()
|