diff --git a/src/backend/wopi/conversion/services.py b/src/backend/wopi/conversion/services.py index 2597c725..c2c3dcaf 100644 --- a/src/backend/wopi/conversion/services.py +++ b/src/backend/wopi/conversion/services.py @@ -23,12 +23,15 @@ from wopi.conversion.source_url import build_source_url MIME_SNIFF_BYTES = 2048 -def _validate_conversion(item, user): +def _validate_conversion(item, user, require_ready=True): """Run pre-flight checks and return the target extension and client options.""" if item.type != models.ItemTypeChoices.FILE: raise ConversionRejected("Source item is not a file.") - if item.upload_state != models.ItemUploadStateChoices.READY: + ready_states = [models.ItemUploadStateChoices.READY] + if not require_ready: + ready_states.append(models.ItemUploadStateChoices.ANALYZING) + if item.upload_state not in ready_states: raise ConversionRejected("Source item is not ready.") if not item.get_abilities(user).get("update"): @@ -95,7 +98,7 @@ def prepare_conversion(source_item, user): UI can display the converting state right away. The actual OnlyOffice conversion happens later in a celery task. """ - target_extension, _ = _validate_conversion(source_item, user) + target_extension, _ = _validate_conversion(source_item, user, require_ready=False) parent = _resolve_destination_parent(source_item, user) target_filename = _target_filename(source_item, target_extension, parent, user) diff --git a/src/backend/wopi/tests/conversion/test_services.py b/src/backend/wopi/tests/conversion/test_services.py index 24af1497..353c6293 100644 --- a/src/backend/wopi/tests/conversion/test_services.py +++ b/src/backend/wopi/tests/conversion/test_services.py @@ -341,6 +341,33 @@ def test_prepare_conversion_returns_placeholder_in_converting_state(settings): assert placeholder.parent().id == parent.id +def test_prepare_conversion_accepts_analyzing_source(settings): + """Queue a conversion while the source is still being analyzed.""" + _configure_wopi(settings) + user = factories.UserFactory() + item = _file(user, update_upload_state=models.ItemUploadStateChoices.ANALYZING) + + placeholder = services.prepare_conversion(item, user) + + assert placeholder.upload_state == models.ItemUploadStateChoices.CONVERTING + + +def test_perform_conversion_rejects_analyzing_source(settings): + """Never convert the bytes before analysis confirmed the source is safe.""" + _configure_wopi(settings) + user = factories.UserFactory() + item = _file(user, update_upload_state=models.ItemUploadStateChoices.ANALYZING) + placeholder = factories.ItemFactory( + users=[(user, models.RoleChoices.EDITOR)], + type=models.ItemTypeChoices.FILE, + filename="document.docx", + update_upload_state=models.ItemUploadStateChoices.CONVERTING, + ) + + with pytest.raises(exceptions.ConversionRejected, match="not ready"): + services.perform_conversion(item, placeholder, user) + + def test_prepare_conversion_rejects_unsupported_extensions(settings): """Skip placeholder creation for files that cannot be converted.""" _configure_wopi(settings)