From 2fce1282bc86d8d1b31db56c7b7e03342a97ea9b Mon Sep 17 00:00:00 2001 From: Antigravity Agent Date: Tue, 28 Apr 2026 17:37:34 +0100 Subject: [PATCH 1/2] feat: add file existence check to scene queue validation --- eink/backend/background/scene_processor.py | 8 ++ .../tests/unit/test_scene_processor.py | 103 ++++++++++++++++++ 2 files changed, 111 insertions(+) diff --git a/eink/backend/background/scene_processor.py b/eink/backend/background/scene_processor.py index 99bb3e26..d5f26308 100644 --- a/eink/backend/background/scene_processor.py +++ b/eink/backend/background/scene_processor.py @@ -401,6 +401,14 @@ async def update_scene_queue(scene, session): ): needs_work = True + if not needs_work and record.filename: + # Check if file exists on disk + path = os.path.join( + get_storage_path("scene_display"), record.filename + ) + if not os.path.exists(path): + needs_work = True + if needs_work: to_queue.add((display_id, image_id)) diff --git a/eink/backend/tests/unit/test_scene_processor.py b/eink/backend/tests/unit/test_scene_processor.py index c5218ed1..c0602d4c 100644 --- a/eink/backend/tests/unit/test_scene_processor.py +++ b/eink/backend/tests/unit/test_scene_processor.py @@ -390,3 +390,106 @@ async def test_scene_processor_rotation(db_setup, tmp_path): assert config["height"] == 480 # Rotation should be 90 because landscape panel used as portrait assert config["rotation"] == 90 + + +@pytest.mark.asyncio +async def test_scene_processor_retriggers_if_file_missing(db_setup, tmp_path): + """Test that scene_processor retriggers if the file is missing from disk.""" + + async with database.get_session() as session: + # Minimal setup for one display + dt = models.DisplayType( + id="dt1", + name="7.5", + width_mm=163, + height_mm=98, + panel_width_mm=163, + panel_height_mm=98, + width_px=800, + height_px=480, + colour_type="BW", + frame={}, + mat={}, + ) + session.add(dt) + layout = models.Layout( + id="l1", + name="L", + canvas_width_mm=200, + canvas_height_mm=200, + items=[{"id": "d1", "display_type_id": "dt1", "x_mm": 0, "y_mm": 0}], + status="active", + ) + session.add(layout) + img = models.Image( + id="i1", + name="I", + file_path="t.png", + width=100, + height=100, + file_type="png", + status="ACTIVE", + file_hash="h1", + ) + session.add(img) + await session.commit() + await session.refresh(img) + + scene = models.Scene( + id="s1", + name="S", + layout_id="l1", + status="active", + items=[ + { + "id": "it1", + "type": "image", + "displays": ["d1"], + "images": [ + { + "image_id": "i1", + "scaling_factor": 100, + "offset": {"x": 0, "y": 0}, + } + ], + } + ], + ) + session.add(scene) + await session.commit() + await session.refresh(scene) + + # Create record with matching hashes but MISSING file on disk + record = models.SceneDisplayImage( + scene_id="s1", + display_id="d1", + image_id="i1", + image_hash=img.settings_hash, + scene_hash=scene.scene_hash, + file_hash="some-hash", + filename="slice_s1_i1_d1.png", + ) + session.add(record) + await session.commit() + + # Ensure the file does NOT exist on disk + # (It shouldn't anyway in this clean tmp_path test) + path = os.path.join(str(tmp_path), "slice_s1_i1_d1.png") + if os.path.exists(path): + os.remove(path) + + # Mock get_storage_path to point to our tmp_path + with patch( + "backend.background.scene_processor.get_storage_path", + return_value=str(tmp_path), + ): + await scene_handler._update_scene_queue(scene, session) + await session.commit() + + # Verify that it was added to the queue + stmt = select(models.SceneQueue).where(models.SceneQueue.scene_id == "s1") + result = await session.execute(stmt) + queued = result.scalars().all() + assert len(queued) == 1 + assert queued[0].display_id == "d1" + assert queued[0].image_id == "i1" From 0c63116a972ba2befb2ffe071dad8cc84b4bb17c Mon Sep 17 00:00:00 2001 From: Antigravity Agent Date: Tue, 28 Apr 2026 17:42:13 +0100 Subject: [PATCH 2/2] fix: Fix unit test --- eink/backend/tests/integration/test_scene_queue.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/eink/backend/tests/integration/test_scene_queue.py b/eink/backend/tests/integration/test_scene_queue.py index d2130993..d6c6b4d5 100644 --- a/eink/backend/tests/integration/test_scene_queue.py +++ b/eink/backend/tests/integration/test_scene_queue.py @@ -1,6 +1,8 @@ +import os import pytest from sqlalchemy import select from backend import models, database +from backend.utils.storage import get_storage_path @pytest.mark.asyncio @@ -171,6 +173,11 @@ async def test_scene_save_populates_queue(aiohttp_client, app): session.add(sdi) await session.commit() + # Create dummy file to pass the existence check in update_scene_queue + path = os.path.join(get_storage_path("scene_display"), "slice.png") + with open(path, "w") as f: + f.write("dummy") + # 6. Save scene again - should now have 0 in queue as it's up to date resp = await client.put(f"/api/scene/{scene_id}", json=update_data) assert resp.status == 200