"""Driver icons — upload, fetch-from-web, and serve. Global, like Driver rows.""" from __future__ import annotations import io import json import pytest from PIL import Image def _png(width: int = 256, height: int = 256, color: str = "red") -> bytes: buf = io.BytesIO() Image.new("RGBA", (width, height), color=color).save(buf, format="PNG") return buf.getvalue() def _create_driver(sha: str = "b" * 64): from imptune.db.models import Driver return Driver.create( sha256=sha, original_filename="konica_c300i.zip", size_bytes=4096, driver_desc=json.dumps(["KONICA MINOLTA C300i PCL"]), inf_filename="kocpl.inf", architecture="amd64", ) class TestDriverIconUpload: def test_upload_creates_the_record_and_file(self, client, tmp_data_dir): from imptune.db.models import DriverIcon driver = _create_driver() response = client.post( f"/drivers/{driver.id}/icon", files={"file": ("logo.png", io.BytesIO(_png()), "image/png")}, ) assert response.status_code == 200 icon = DriverIcon.get(DriverIcon.driver == driver.id) assert icon.original_filename == "logo.png" assert (tmp_data_dir / "icons" / icon.sha256).exists() def test_off_size_image_is_normalized(self, client, tmp_data_dir): from imptune.db.models import DriverIcon driver = _create_driver() client.post( f"/drivers/{driver.id}/icon", files={"file": ("wide.png", io.BytesIO(_png(600, 120)), "image/png")}, ) icon = DriverIcon.get(DriverIcon.driver == driver.id) with Image.open(tmp_data_dir / "icons" / icon.sha256) as img: assert img.size == (256, 256) def test_second_upload_replaces_the_first(self, client, tmp_data_dir): from imptune.db.models import DriverIcon driver = _create_driver() client.post( f"/drivers/{driver.id}/icon", files={"file": ("a.png", io.BytesIO(_png(color="red")), "image/png")}, ) client.post( f"/drivers/{driver.id}/icon", files={"file": ("b.png", io.BytesIO(_png(color="green")), "image/png")}, ) icons = list(DriverIcon.select().where(DriverIcon.driver == driver.id)) assert len(icons) == 1 assert icons[0].original_filename == "b.png" def test_response_carries_an_oob_row_refresh(self, client, tmp_data_dir): """The dialog stays open, so the table thumbnail is swapped out of band.""" driver = _create_driver() response = client.post( f"/drivers/{driver.id}/icon", files={"file": ("logo.png", io.BytesIO(_png()), "image/png")}, ) assert f'id="driver-thumb-{driver.id}"' in response.text assert 'hx-swap-oob="true"' in response.text def test_rejects_an_undecodable_file(self, client, tmp_data_dir): driver = _create_driver() response = client.post( f"/drivers/{driver.id}/icon", files={"file": ("x.png", io.BytesIO(b"nope"), "image/png")}, ) assert response.status_code == 422 def test_rejects_an_oversized_file(self, client, tmp_data_dir): driver = _create_driver() oversized = _png() + b"\x00" * (750 * 1024) response = client.post( f"/drivers/{driver.id}/icon", files={"file": ("big.png", io.BytesIO(oversized), "image/png")}, ) assert response.status_code == 422 assert "750" in response.text def test_404_for_unknown_driver(self, client, tmp_data_dir): response = client.post( "/drivers/99999/icon", files={"file": ("logo.png", io.BytesIO(_png()), "image/png")}, ) assert response.status_code == 404 class TestDriverIconFromWeb: def test_downloads_and_stores_the_picked_image(self, client, tmp_data_dir, monkeypatch): from imptune.db.models import DriverIcon from imptune.services import websearch driver = _create_driver() monkeypatch.setattr(websearch, "fetch_image", lambda url: _png(400, 400)) response = client.post( f"/drivers/{driver.id}/icon/from-web", data={"url": "https://example.com/pictures/c300i.png"}, ) assert response.status_code == 200 icon = DriverIcon.get(DriverIcon.driver == driver.id) assert icon.original_filename == "c300i.png" with Image.open(tmp_data_dir / "icons" / icon.sha256) as img: assert img.size == (256, 256) def test_reports_a_refused_url(self, client, tmp_data_dir, monkeypatch): from imptune.services import websearch driver = _create_driver() def refuse(url): raise websearch.WebSearchError("10.0.0.5 resolves to a private address — refused.") monkeypatch.setattr(websearch, "fetch_image", refuse) response = client.post( f"/drivers/{driver.id}/icon/from-web", data={"url": "http://10.0.0.5/x.png"} ) assert response.status_code == 400 assert "private address" in response.text def test_reports_a_page_that_is_not_an_image(self, client, tmp_data_dir, monkeypatch): from imptune.services import websearch driver = _create_driver() monkeypatch.setattr(websearch, "fetch_image", lambda url: b"404") response = client.post( f"/drivers/{driver.id}/icon/from-web", data={"url": "https://example.com/x"} ) assert response.status_code == 422 assert "readable image" in response.text class TestDriverIconServe: def test_serves_the_png(self, client, tmp_data_dir): driver = _create_driver() client.post( f"/drivers/{driver.id}/icon", files={"file": ("logo.png", io.BytesIO(_png()), "image/png")}, ) response = client.get(f"/drivers/{driver.id}/icon") assert response.status_code == 200 assert response.headers["content-type"] == "image/png" def test_visible_to_another_owner(self, client, tmp_data_dir): """The driver library is shared, so its icons are not owner-scoped.""" from fastapi.testclient import TestClient from imptune.main import app driver = _create_driver() client.post( f"/drivers/{driver.id}/icon", files={"file": ("logo.png", io.BytesIO(_png()), "image/png")}, ) with TestClient(app) as other: assert other.get(f"/drivers/{driver.id}/icon").status_code == 200 def test_404_without_an_icon(self, client, tmp_data_dir): driver = _create_driver() assert client.get(f"/drivers/{driver.id}/icon").status_code == 404 def test_404_when_the_file_vanished(self, client, tmp_data_dir): from imptune.db.models import DriverIcon driver = _create_driver() client.post( f"/drivers/{driver.id}/icon", files={"file": ("logo.png", io.BytesIO(_png()), "image/png")}, ) icon = DriverIcon.get(DriverIcon.driver == driver.id) (tmp_data_dir / "icons" / icon.sha256).unlink() assert client.get(f"/drivers/{driver.id}/icon").status_code == 404 def test_library_row_shows_the_thumbnail(client, tmp_data_dir): driver = _create_driver() client.post( f"/drivers/{driver.id}/icon", files={"file": ("logo.png", io.BytesIO(_png()), "image/png")}, ) page = client.get("/drivers") assert f'src="/drivers/{driver.id}/icon"' in page.text def test_printer_and_driver_icons_share_one_stored_file(client, owner, tmp_data_dir): """Content-addressed storage: identical bytes land on the same path.""" from imptune.db.models import DriverIcon, Icon, Printer driver = _create_driver() printer = Printer.create( name="P", ip_address="10.0.0.1", port_name="IP_10.0.0.1", owner=owner ) png = _png(300, 300) client.post( f"/drivers/{driver.id}/icon", files={"file": ("a.png", io.BytesIO(png), "image/png")}, ) client.post( f"/printers/{printer.id}/icon", files={"file": ("b.png", io.BytesIO(png), "image/png")}, ) assert ( DriverIcon.get(DriverIcon.driver == driver.id).sha256 == Icon.get(Icon.printer == printer.id).sha256 )