import json from datetime import UTC, datetime, timedelta import pytest from test_display import submit from pigway_frame_flow.errors import ErrorCode, FlowError def lose_response(service, monkeypatch, change_display=True): driver = service.registry.get("living-room") upload = driver.upload def uncertain(path, name): upload(path, name) if not change_display: driver.client.display(1) raise FlowError( ErrorCode.WRITE_RESULT_UNKNOWN, "Cloud write outcome is unknown", 502 ) monkeypatch.setattr(driver, "upload", uncertain) operation = submit(service) service.process_one() assert service.operation(operation)["status"] == "UNKNOWN" return operation, driver def test_lost_upload_response_resolves_by_exact_bytes_without_reupload( service, monkeypatch ): operation, driver = lose_response(service, monkeypatch) service.sync() assert service.operation(operation)["status"] == "COMPLETED" assert service.operation(operation)["result"]["image_id"] == 2 assert driver.client.uploads == 1 assert not service.blocked("living-room") def test_same_name_wrong_bytes_does_not_unlock(service, monkeypatch): operation, driver = lose_response(service, monkeypatch) monkeypatch.setattr(driver, "download", lambda _: b"unrelated") service.sync() assert service.operation(operation)["status"] == "UNKNOWN" assert service.blocked("living-room") assert driver.client.uploads == 1 def test_ambiguous_gallery_does_not_download_or_unlock(service, monkeypatch): operation, driver = lose_response(service, monkeypatch) driver.client.state["images"].append(dict(driver.client.state["images"][-1], id=3)) monkeypatch.setattr(driver, "download", lambda _: pytest.fail("Ambiguous download")) service.sync() assert service.operation(operation)["status"] == "UNKNOWN" def test_confirmed_upload_not_displayed_closes_failed_display_after_stable_checks( service, monkeypatch ): operation, driver = lose_response(service, monkeypatch, change_display=False) service.sync() result = service.operation(operation)["result"] assert result["upload_confirmed"] start = datetime.fromisoformat(result["upload_intent"]["started_at"]) import pigway_frame_flow.display.upload_reconciliation as module class Later(datetime): @classmethod def now(cls, tz=None): return start + timedelta(seconds=service.settings.sync_timeout + 600) monkeypatch.setattr(module, "datetime", Later) service.unknown_checked.clear() service.sync() assert service.operation(operation)["status"] == "UNKNOWN" result = service.operation(operation)["result"] result["unchanged_observed_at"] = ( datetime.now(UTC) - timedelta(seconds=61) ).isoformat() service.db.execute( "UPDATE operations SET result=? WHERE id=?", (json.dumps(result), operation) ) service.unknown_checked.clear() service.sync() assert service.operation(operation)["status"] == "FAILED" assert service.operation(operation)["error"]["code"] == "DISPLAY_NOT_CONFIRMED" assert not service.blocked("living-room") assert driver.client.uploads == 1 def test_legacy_unknown_without_intent_remains_locked(service): operation = submit(service) service._start_write(operation) service.update(operation, "UNKNOWN") service.sync() assert service.blocked("living-room") def test_recovered_upload_preserves_temporary_restore(service, monkeypatch): driver = service.registry.get("living-room") upload = driver.upload def uncertain(path, name): upload(path, name) raise FlowError(ErrorCode.WRITE_RESULT_UNKNOWN, "Response lost", 502) monkeypatch.setattr(driver, "upload", uncertain) operation = submit(service, duration=60, restore=True) service.process_one() service.sync() assert service.operation(operation)["status"] == "COMPLETED" assert service.top("living-room")["id"] == operation assert service.state("living-room")["managed_image_id"] == 2