Build Pigway Frame Flow with bilingual UI, complete KoKonna driver and safe automation
This commit is contained in:
@@ -0,0 +1,153 @@
|
||||
from datetime import UTC, datetime, timedelta
|
||||
|
||||
import pytest
|
||||
|
||||
from pigway_frame_flow.display.service import DisplayService
|
||||
from pigway_frame_flow.errors import ErrorCode, FlowError
|
||||
from pigway_frame_flow.models import DisplayRequest
|
||||
|
||||
|
||||
def submit(service, duration=0, restore=False, priority=50, key=None, content=None):
|
||||
request = DisplayRequest(
|
||||
content=content or {"type": "template", "template": "quote", "data": {}},
|
||||
options={
|
||||
"duration": duration,
|
||||
"restore_previous": restore,
|
||||
"priority": priority,
|
||||
},
|
||||
)
|
||||
return service.submit(
|
||||
"display",
|
||||
request.model_dump(mode="json"),
|
||||
"living-room",
|
||||
"admin",
|
||||
"request-test",
|
||||
key,
|
||||
)
|
||||
|
||||
|
||||
def expired(service, id):
|
||||
service.db.execute(
|
||||
"UPDATE overrides SET expires_at=? WHERE id=?",
|
||||
((datetime.now(UTC) - timedelta(seconds=1)).isoformat(), id),
|
||||
)
|
||||
|
||||
|
||||
def test_display_restore(service):
|
||||
id = submit(service, 60, True)
|
||||
service.process_one()
|
||||
service.sync()
|
||||
assert service.operation(id)["status"] == "COMPLETED"
|
||||
assert service.registry.get("living-room").get_current_image() == 2
|
||||
expired(service, id)
|
||||
service.expire()
|
||||
service.sync()
|
||||
assert service.registry.get("living-room").get_current_image() == 1
|
||||
|
||||
|
||||
def test_priority_lower_defers_upload(service):
|
||||
high = submit(service, 300, True, 90)
|
||||
service.process_one()
|
||||
low = submit(service, 60, True, 10)
|
||||
service.process_one()
|
||||
assert service.operation(low)["status"] == "QUEUED"
|
||||
assert service.registry.get("living-room").client.uploads == 1
|
||||
expired(service, high)
|
||||
service.expire()
|
||||
service.process_one()
|
||||
assert service.registry.get("living-room").client.uploads == 2
|
||||
|
||||
|
||||
def test_nested_overrides(service):
|
||||
a = submit(service, 300, True, 10)
|
||||
service.process_one()
|
||||
b = submit(service, 60, True, 90)
|
||||
service.process_one()
|
||||
expired(service, b)
|
||||
service.expire()
|
||||
assert service.registry.get("living-room").get_current_image() == 2
|
||||
expired(service, a)
|
||||
service.expire()
|
||||
assert service.registry.get("living-room").get_current_image() == 1
|
||||
|
||||
|
||||
def test_no_retry_unknown(service):
|
||||
driver = service.registry.get("living-room")
|
||||
|
||||
def timeout(*a):
|
||||
raise FlowError(ErrorCode.WRITE_RESULT_UNKNOWN, "Write outcome unknown", 502)
|
||||
|
||||
driver.upload = timeout
|
||||
id = submit(service)
|
||||
service.process_one()
|
||||
assert service.operation(id)["status"] == "UNKNOWN"
|
||||
with pytest.raises(FlowError):
|
||||
submit(service)
|
||||
assert service.process_one() is False
|
||||
assert service.reconcile(id)["resolved"] is False
|
||||
assert service.reconcile(id, True)["resolved"] is True
|
||||
|
||||
|
||||
def test_restart_preserves_override(service):
|
||||
id = submit(service, 60, True)
|
||||
service.process_one()
|
||||
restarted = DisplayService(
|
||||
service.db, service.settings, service.registry, service.assets, service.renderer
|
||||
)
|
||||
expired(restarted, id)
|
||||
restarted.expire()
|
||||
assert service.registry.get("living-room").get_current_image() == 1
|
||||
|
||||
|
||||
def test_restart_marks_inflight_unknown(service):
|
||||
id = submit(service)
|
||||
service._start_write(id)
|
||||
DisplayService(
|
||||
service.db, service.settings, service.registry, service.assets, service.renderer
|
||||
)
|
||||
assert service.operation(id)["status"] == "UNKNOWN"
|
||||
|
||||
|
||||
def test_idempotency(service):
|
||||
first = submit(service, key="repeat")
|
||||
second = submit(service, key="repeat")
|
||||
assert first == second
|
||||
service.process_one()
|
||||
service.process_one()
|
||||
assert service.registry.get("living-room").client.uploads == 1
|
||||
with pytest.raises(FlowError):
|
||||
submit(service, priority=10, key="repeat")
|
||||
|
||||
|
||||
def test_manual_change_prevents_restore(service):
|
||||
id = submit(service, 60, True)
|
||||
service.process_one()
|
||||
driver = service.registry.get("living-room")
|
||||
driver.client.display(1)
|
||||
driver.invalidate()
|
||||
expired(service, id)
|
||||
service.expire()
|
||||
assert driver.get_current_image() == 1
|
||||
assert not service.db.all("SELECT * FROM overrides")
|
||||
|
||||
|
||||
def test_delete_restore_reference_protected(service):
|
||||
submit(service, 60, True)
|
||||
service.process_one()
|
||||
delete = service.submit(
|
||||
"delete",
|
||||
{"image_id": 1, "confirm_current": True},
|
||||
"living-room",
|
||||
"admin",
|
||||
"delete-test",
|
||||
)
|
||||
service.process_one()
|
||||
assert service.operation(delete)["status"] == "FAILED"
|
||||
assert service.operation(delete)["error"]["code"] == "CONFLICT"
|
||||
|
||||
|
||||
def test_offline(service):
|
||||
service.registry.get("living-room").client.online = False
|
||||
id = submit(service)
|
||||
service.process_one()
|
||||
assert service.operation(id)["error"]["code"] == "DEVICE_OFFLINE"
|
||||
Reference in New Issue
Block a user