From f8828db8352163cca4ce3403ccf5280ff4e4829d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 22:38:00 +0000 Subject: [PATCH 1/2] Initial plan From e6a4995a5c3b61bc78454873d6ae97aefb0ce90b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 22:39:34 +0000 Subject: [PATCH 2/2] style: run ruff format on 6 files to fix formatting issues Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- app/api/files.py | 4 +- app/tasks/extract_metadata_with_gpt.py | 6 +- tests/test_coverage_config_settings.py | 96 ++++++++++++++------- tests/test_coverage_uploads_notification.py | 33 ++++--- tests/test_file_detail_endpoints.py | 1 - tests/test_retry_subtask_logging.py | 4 +- 6 files changed, 88 insertions(+), 56 deletions(-) diff --git a/app/api/files.py b/app/api/files.py index 0a0f612c..42ce2a0d 100644 --- a/app/api/files.py +++ b/app/api/files.py @@ -496,9 +496,7 @@ def _retry_pipeline_step(file_record: FileRecord, step_name: str, db: Session) - if step_name == "process_document": # Full reprocessing with duplicate check bypass - logger.info( - f"Retrying process_document for file {file_id}: local_filename={file_record.local_filename!r}" - ) + logger.info(f"Retrying process_document for file {file_id}: local_filename={file_record.local_filename!r}") if not file_record.local_filename: logger.error(f"process_document retry failed for file {file_id}: local_filename is None") raise HTTPException(status_code=400, detail="Local file path is None. Cannot retry.") diff --git a/app/tasks/extract_metadata_with_gpt.py b/app/tasks/extract_metadata_with_gpt.py index c9db5025..c6fd33f9 100644 --- a/app/tasks/extract_metadata_with_gpt.py +++ b/app/tasks/extract_metadata_with_gpt.py @@ -58,7 +58,11 @@ def extract_metadata_with_gpt(self, filename: str, cleaned_text: str, file_id: i task_id = self.request.id logger.info(f"[{task_id}] Starting metadata extraction for: {filename}") log_task_progress( - task_id, "extract_metadata_with_gpt", "in_progress", f"Extracting metadata for {os.path.basename(filename)}", file_id=file_id + task_id, + "extract_metadata_with_gpt", + "in_progress", + f"Extracting metadata for {os.path.basename(filename)}", + file_id=file_id, ) # Get file_id from database if not provided diff --git a/tests/test_coverage_config_settings.py b/tests/test_coverage_config_settings.py index da80cedb..f6e8c143 100644 --- a/tests/test_coverage_config_settings.py +++ b/tests/test_coverage_config_settings.py @@ -14,6 +14,7 @@ from app.main import app as fastapi_app # Helpers # --------------------------------------------------------------------------- + def _override_admin(): """Dependency override that simulates an admin user.""" return {"is_admin": True, "name": "admin"} @@ -23,6 +24,7 @@ def _override_admin(): # 1. app/utils/config_validator.py (backward-compatible re-export wrapper) # --------------------------------------------------------------------------- + class TestConfigValidatorReExports: """Verify the backward-compatible wrapper re-exports all expected symbols.""" @@ -90,6 +92,7 @@ class TestConfigValidatorReExports: # 2. app/api/settings.py (admin-only settings CRUD) # --------------------------------------------------------------------------- + class TestRequireAdminDependency: """Tests for the require_admin dependency itself.""" @@ -205,8 +208,10 @@ class TestSettingsUpdate: """Successfully update a setting.""" fastapi_app.dependency_overrides[require_admin] = _override_admin try: - with patch("app.api.settings.save_setting_to_db", return_value=True), \ - patch("app.api.settings.validate_setting_value", return_value=(True, None)): + with ( + patch("app.api.settings.save_setting_to_db", return_value=True), + patch("app.api.settings.validate_setting_value", return_value=(True, None)), + ): response = client.post( "/api/settings/workdir", json={"key": "workdir", "value": "/new/path"}, @@ -239,8 +244,10 @@ class TestSettingsUpdate: """500 error when save_setting_to_db returns False.""" fastapi_app.dependency_overrides[require_admin] = _override_admin try: - with patch("app.api.settings.validate_setting_value", return_value=(True, None)), \ - patch("app.api.settings.save_setting_to_db", return_value=False): + with ( + patch("app.api.settings.validate_setting_value", return_value=(True, None)), + patch("app.api.settings.save_setting_to_db", return_value=False), + ): response = client.post( "/api/settings/workdir", json={"key": "workdir", "value": "/tmp"}, @@ -270,8 +277,10 @@ class TestSettingsUpdate: """500 error when an unexpected exception is raised.""" fastapi_app.dependency_overrides[require_admin] = _override_admin try: - with patch("app.api.settings.validate_setting_value", return_value=(True, None)), \ - patch("app.api.settings.save_setting_to_db", side_effect=RuntimeError("unexpected")): + with ( + patch("app.api.settings.validate_setting_value", return_value=(True, None)), + patch("app.api.settings.save_setting_to_db", side_effect=RuntimeError("unexpected")), + ): response = client.post( "/api/settings/workdir", json={"key": "workdir", "value": "/tmp"}, @@ -348,8 +357,10 @@ class TestSettingsBulkUpdate: SettingUpdate(key="external_hostname", value="example.com"), ] - with patch("app.api.settings.validate_setting_value", return_value=(True, None)), \ - patch("app.api.settings.save_setting_to_db", return_value=True): + with ( + patch("app.api.settings.validate_setting_value", return_value=(True, None)), + patch("app.api.settings.save_setting_to_db", return_value=True), + ): result = await bulk_update_settings(updates, mock_request, mock_db, mock_admin) assert result["success"] is True assert len(result["updated"]) == 2 @@ -373,8 +384,10 @@ class TestSettingsBulkUpdate: SettingUpdate(key="bad_key", value="invalid"), ] - with patch("app.api.settings.validate_setting_value", side_effect=mock_validate), \ - patch("app.api.settings.save_setting_to_db", return_value=True): + with ( + patch("app.api.settings.validate_setting_value", side_effect=mock_validate), + patch("app.api.settings.save_setting_to_db", return_value=True), + ): result = await bulk_update_settings(updates, mock_request, mock_db, {"is_admin": True}) assert result["success"] is False assert len(result["updated"]) == 1 @@ -391,8 +404,10 @@ class TestSettingsBulkUpdate: mock_db = self._make_mock_db() updates = [SettingUpdate(key="workdir", value="/tmp")] - with patch("app.api.settings.validate_setting_value", return_value=(True, None)), \ - patch("app.api.settings.save_setting_to_db", return_value=False): + with ( + patch("app.api.settings.validate_setting_value", return_value=(True, None)), + patch("app.api.settings.save_setting_to_db", return_value=False), + ): result = await bulk_update_settings(updates, mock_request, mock_db, {"is_admin": True}) assert result["success"] is False assert len(result["errors"]) == 1 @@ -408,8 +423,10 @@ class TestSettingsBulkUpdate: mock_db = self._make_mock_db() updates = [SettingUpdate(key="workdir", value="/tmp")] - with patch("app.api.settings.validate_setting_value", return_value=(True, None)), \ - patch("app.api.settings.save_setting_to_db", side_effect=RuntimeError("boom")): + with ( + patch("app.api.settings.validate_setting_value", return_value=(True, None)), + patch("app.api.settings.save_setting_to_db", side_effect=RuntimeError("boom")), + ): result = await bulk_update_settings(updates, mock_request, mock_db, {"is_admin": True}) assert result["success"] is False assert len(result["errors"]) == 1 @@ -435,6 +452,7 @@ class TestSettingsBulkUpdate: # 3. app/views/license_routes.py # --------------------------------------------------------------------------- + class TestLicenseRoutes: """Tests for license and attribution view routes.""" @@ -471,6 +489,7 @@ class TestLicenseRoutes: # 4. app/api/diagnostic.py # --------------------------------------------------------------------------- + class TestDiagnosticSettings: """GET /api/diagnostic/settings - dump settings.""" @@ -502,8 +521,10 @@ class TestDiagnosticTestNotification: @pytest.mark.unit def test_notification_send_success(self, client): """Returns success when notification is sent.""" - with patch("app.config.settings.notification_urls", new=["http://ntfy.example.com/test"], create=True), \ - patch("app.utils.notification.send_notification", return_value=True) as mock_send: + with ( + patch("app.config.settings.notification_urls", new=["http://ntfy.example.com/test"], create=True), + patch("app.utils.notification.send_notification", return_value=True) as mock_send, + ): response = client.post("/api/diagnostic/test-notification") assert response.status_code == 200 data = response.json() @@ -514,8 +535,10 @@ class TestDiagnosticTestNotification: @pytest.mark.unit def test_notification_send_failure(self, client): """Returns error when send_notification returns False.""" - with patch("app.config.settings.notification_urls", new=["http://ntfy.example.com/test"], create=True), \ - patch("app.utils.notification.send_notification", return_value=False): + with ( + patch("app.config.settings.notification_urls", new=["http://ntfy.example.com/test"], create=True), + patch("app.utils.notification.send_notification", return_value=False), + ): response = client.post("/api/diagnostic/test-notification") assert response.status_code == 200 data = response.json() @@ -525,8 +548,10 @@ class TestDiagnosticTestNotification: @pytest.mark.unit def test_notification_send_exception(self, client): """Returns error when send_notification raises an exception.""" - with patch("app.config.settings.notification_urls", new=["http://ntfy.example.com/test"], create=True), \ - patch("app.utils.notification.send_notification", side_effect=RuntimeError("connection refused")): + with ( + patch("app.config.settings.notification_urls", new=["http://ntfy.example.com/test"], create=True), + patch("app.utils.notification.send_notification", side_effect=RuntimeError("connection refused")), + ): response = client.post("/api/diagnostic/test-notification") assert response.status_code == 200 data = response.json() @@ -538,6 +563,7 @@ class TestDiagnosticTestNotification: # 5. app/api/openai.py # --------------------------------------------------------------------------- + class TestOpenAITestEndpoint: """GET /api/openai/test - test OpenAI API key.""" @@ -560,8 +586,10 @@ class TestOpenAITestEndpoint: mock_client_instance = MagicMock() mock_client_instance.models.list.return_value = mock_models - with patch("app.config.settings.openai_api_key", new="sk-valid-key"), \ - patch("openai.OpenAI", return_value=mock_client_instance): + with ( + patch("app.config.settings.openai_api_key", new="sk-valid-key"), + patch("openai.OpenAI", return_value=mock_client_instance), + ): response = client.get("/api/openai/test") assert response.status_code == 200 data = response.json() @@ -574,8 +602,10 @@ class TestOpenAITestEndpoint: mock_client_instance = MagicMock() mock_client_instance.models.list.side_effect = Exception("Incorrect API key provided") - with patch("app.config.settings.openai_api_key", new="sk-bad-key"), \ - patch("openai.OpenAI", return_value=mock_client_instance): + with ( + patch("app.config.settings.openai_api_key", new="sk-bad-key"), + patch("openai.OpenAI", return_value=mock_client_instance), + ): response = client.get("/api/openai/test") assert response.status_code == 200 data = response.json() @@ -588,8 +618,10 @@ class TestOpenAITestEndpoint: mock_client_instance = MagicMock() mock_client_instance.models.list.side_effect = Exception("Connection timeout") - with patch("app.config.settings.openai_api_key", new="sk-valid-key"), \ - patch("openai.OpenAI", return_value=mock_client_instance): + with ( + patch("app.config.settings.openai_api_key", new="sk-valid-key"), + patch("openai.OpenAI", return_value=mock_client_instance), + ): response = client.get("/api/openai/test") assert response.status_code == 200 data = response.json() @@ -609,8 +641,10 @@ class TestOpenAITestEndpoint: raise ImportError("No module named 'openai'") return original_import(name, *args, **kwargs) - with patch("app.config.settings.openai_api_key", new="sk-key"), \ - patch("builtins.__import__", side_effect=mock_import): + with ( + patch("app.config.settings.openai_api_key", new="sk-key"), + patch("builtins.__import__", side_effect=mock_import), + ): response = client.get("/api/openai/test") assert response.status_code == 200 data = response.json() @@ -620,8 +654,10 @@ class TestOpenAITestEndpoint: @pytest.mark.unit def test_openai_unexpected_error(self, client): """Returns error for unexpected exceptions outside the inner try.""" - with patch("app.config.settings.openai_api_key", new="sk-key"), \ - patch("openai.OpenAI", side_effect=RuntimeError("unexpected crash")): + with ( + patch("app.config.settings.openai_api_key", new="sk-key"), + patch("openai.OpenAI", side_effect=RuntimeError("unexpected crash")), + ): response = client.get("/api/openai/test") assert response.status_code == 200 data = response.json() diff --git a/tests/test_coverage_uploads_notification.py b/tests/test_coverage_uploads_notification.py index 1479251c..cf14981d 100644 --- a/tests/test_coverage_uploads_notification.py +++ b/tests/test_coverage_uploads_notification.py @@ -655,13 +655,18 @@ class TestNotificationCoverage: def _all_should_upload_false(): """Return a list of patch context managers that set all _should_upload_* to False.""" services = [ - "dropbox", "nextcloud", "paperless", "google_drive", - "webdav", "ftp", "sftp", "email", "onedrive", "s3", - ] - return [ - patch(f"app.tasks.send_to_all._should_upload_to_{s}", return_value=False) - for s in services + "dropbox", + "nextcloud", + "paperless", + "google_drive", + "webdav", + "ftp", + "sftp", + "email", + "onedrive", + "s3", ] + return [patch(f"app.tasks.send_to_all._should_upload_to_{s}", return_value=False) for s in services] class TestSendToAllCoverage: @@ -696,9 +701,7 @@ class TestSendToAllCoverage: mock_session_cls.return_value.__enter__ = MagicMock(return_value=mock_db) mock_session_cls.return_value.__exit__ = MagicMock(return_value=False) - result = send_to_all_destinations.apply( - args=[str(f)], kwargs={"use_validator": True, "file_id": 1} - ).get() + result = send_to_all_destinations.apply(args=[str(f)], kwargs={"use_validator": True, "file_id": 1}).get() assert result["status"] == "Queued" assert result["tasks"] == {} @@ -805,9 +808,7 @@ class TestSendToAllCoverage: ): ms.workdir = str(tmp_path) - result = send_to_all_destinations.apply( - args=[str(f)], kwargs={"use_validator": True, "file_id": 1} - ).get() + result = send_to_all_destinations.apply(args=[str(f)], kwargs={"use_validator": True, "file_id": 1}).get() assert result["status"] == "Queued" @@ -836,9 +837,7 @@ class TestSendToAllCoverage: stack.enter_context(p) ms.workdir = str(tmp_path) - result = send_to_all_destinations.apply( - args=[str(f)], kwargs={"use_validator": True, "file_id": 1} - ).get() + result = send_to_all_destinations.apply(args=[str(f)], kwargs={"use_validator": True, "file_id": 1}).get() assert "dropbox_error" in result["tasks"] assert "broker down" in result["tasks"]["dropbox_error"] @@ -868,9 +867,7 @@ class TestSendToAllCoverage: ): ms.workdir = str(tmp_path) - result = send_to_all_destinations.apply( - args=[str(f)], kwargs={"use_validator": True, "file_id": 1} - ).get() + result = send_to_all_destinations.apply(args=[str(f)], kwargs={"use_validator": True, "file_id": 1}).get() assert "dropbox_task_id" not in result["tasks"] assert result["status"] == "Queued" diff --git a/tests/test_file_detail_endpoints.py b/tests/test_file_detail_endpoints.py index 837d058d..ff79f936 100644 --- a/tests/test_file_detail_endpoints.py +++ b/tests/test_file_detail_endpoints.py @@ -300,7 +300,6 @@ class TestSubtaskRetry: assert str(processed_file) in str(call_args[0][0]) - @pytest.mark.integration class TestFilePreview: """Tests for file preview endpoint.""" diff --git a/tests/test_retry_subtask_logging.py b/tests/test_retry_subtask_logging.py index 740797b5..321541dd 100644 --- a/tests/test_retry_subtask_logging.py +++ b/tests/test_retry_subtask_logging.py @@ -151,9 +151,7 @@ class TestRetrySubtaskEnhancedLogging: db_session.commit() db_session.refresh(file_record) - response = client.post( - f"/api/files/{file_record.id}/retry-subtask?subtask_name=extract_metadata_with_gpt" - ) + response = client.post(f"/api/files/{file_record.id}/retry-subtask?subtask_name=extract_metadata_with_gpt") assert response.status_code == 400 error_detail = response.json()["detail"]