From 7754d140502a7822e3e04dc9c66323fef6df96df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 16:08:15 +0000 Subject: [PATCH 1/6] Initial plan From b42c6f5f648a9d4c0b337f2bd9244f7e27283e3d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 16:12:39 +0000 Subject: [PATCH 2/6] fix: resolve all Ruff linting errors - Fix PLW2901: Use different variable name for stripped lines in loop - Fix E721: Use 'is' instead of '==' for type comparisons - Add noqa comments for intentional security warnings (S321, S507, S110, S603) Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- app/api/dropbox.py | 9 ++++----- app/api/google_drive.py | 9 ++++----- app/api/onedrive.py | 9 ++++----- app/tasks/upload_to_ftp.py | 6 +++--- app/tasks/upload_to_sftp.py | 6 +++--- app/tasks/upload_with_rclone.py | 8 ++++---- app/utils/config_loader.py | 8 ++++---- app/views/status.py | 4 ++-- tests/test_check_credentials.py | 5 +---- tests/test_e2e_full_stack.py | 2 +- tests/test_security_headers.py | 22 ++++++++++++++-------- tests/test_upload_email.py | 3 --- tests/test_upload_ftp_additional.py | 8 ++++---- tests/test_upload_google_drive.py | 2 +- tests/test_views_status.py | 3 --- 15 files changed, 49 insertions(+), 55 deletions(-) diff --git a/app/api/dropbox.py b/app/api/dropbox.py index fde72f16..ea7600aa 100644 --- a/app/api/dropbox.py +++ b/app/api/dropbox.py @@ -224,18 +224,17 @@ async def save_dropbox_settings( updated = set() new_env_lines = [] for line in env_lines: - line = line.rstrip() + stripped_line = line.rstrip() is_updated = False for key, value in dropbox_settings.items(): - if line.startswith(f"{key}=") or line.startswith(f"# {key}="): - if line.startswith("# "): # Uncomment if commented out - line = line[2:] + if stripped_line.startswith(f"{key}=") or stripped_line.startswith(f"# {key}="): + # Uncomment if commented out - check the original stripped line new_env_lines.append(f"{key}={value}") updated.add(key) is_updated = True break if not is_updated: - new_env_lines.append(line) + new_env_lines.append(stripped_line) # Add any settings that weren't updated (they weren't in the file) for key, value in dropbox_settings.items(): diff --git a/app/api/google_drive.py b/app/api/google_drive.py index e1942df4..b626a470 100644 --- a/app/api/google_drive.py +++ b/app/api/google_drive.py @@ -373,18 +373,17 @@ async def save_dropbox_settings( updated = set() new_env_lines = [] for line in env_lines: - line = line.rstrip() + stripped_line = line.rstrip() is_updated = False for key, value in drive_settings.items(): - if line.startswith(f"{key}=") or line.startswith(f"# {key}="): - if line.startswith("# "): # Uncomment if commented out - line = line[2:] + if stripped_line.startswith(f"{key}=") or stripped_line.startswith(f"# {key}="): + # Uncomment if commented out - check the original stripped line new_env_lines.append(f"{key}={value}") updated.add(key) is_updated = True break if not is_updated: - new_env_lines.append(line) + new_env_lines.append(stripped_line) # Add any settings that weren't updated (they weren't in the file) for key, value in drive_settings.items(): diff --git a/app/api/onedrive.py b/app/api/onedrive.py index 1b226768..894a1a44 100644 --- a/app/api/onedrive.py +++ b/app/api/onedrive.py @@ -242,18 +242,17 @@ async def save_onedrive_settings( updated = set() new_env_lines = [] for line in env_lines: - line = line.rstrip() + stripped_line = line.rstrip() is_updated = False for key, value in onedrive_settings.items(): - if line.startswith(f"{key}=") or line.startswith(f"# {key}="): - if line.startswith("# "): # Uncomment if commented out - line = line[2:] + if stripped_line.startswith(f"{key}=") or stripped_line.startswith(f"# {key}="): + # Uncomment if commented out - check the original stripped line new_env_lines.append(f"{key}={value}") updated.add(key) is_updated = True break if not is_updated: - new_env_lines.append(line) + new_env_lines.append(stripped_line) # Add any settings that weren't updated (they weren't in the file) for key, value in onedrive_settings.items(): diff --git a/app/tasks/upload_to_ftp.py b/app/tasks/upload_to_ftp.py index 11227068..e6207e34 100644 --- a/app/tasks/upload_to_ftp.py +++ b/app/tasks/upload_to_ftp.py @@ -57,7 +57,7 @@ def upload_to_ftp(self, file_path: str, file_id: int = None): if use_tls: try: logger.info(f"Attempting FTPS connection to {settings.ftp_host}") - ftp = ftplib.FTP_TLS() + ftp = ftplib.FTP_TLS() # noqa: S321 ftp.connect(host=settings.ftp_host, port=settings.ftp_port or 21) # Login with credentials @@ -74,7 +74,7 @@ def upload_to_ftp(self, file_path: str, file_id: int = None): else: logger.warning(f"FTPS connection failed, falling back to regular FTP: {str(e)}") # Fall back to regular FTP - only if explicitly allowed by configuration - ftp = ftplib.FTP() # nosec B321 - Fallback to FTP intentional when configured + ftp = ftplib.FTP() # nosec B321 - Fallback to FTP intentional when configured # noqa: S321 ftp.connect(host=settings.ftp_host, port=settings.ftp_port or 21) # Login with credentials @@ -88,7 +88,7 @@ def upload_to_ftp(self, file_path: str, file_id: int = None): # Directly use regular FTP if TLS is explicitly disabled logger.warning("Using plaintext FTP - connection is NOT encrypted!") - ftp = ftplib.FTP() # nosec B321 - Plaintext FTP intentional when explicitly configured + ftp = ftplib.FTP() # nosec B321 - Plaintext FTP intentional when explicitly configured # noqa: S321 ftp.connect(host=settings.ftp_host, port=settings.ftp_port or 21) # Login with credentials diff --git a/app/tasks/upload_to_sftp.py b/app/tasks/upload_to_sftp.py index 8e0676dd..98da9261 100644 --- a/app/tasks/upload_to_sftp.py +++ b/app/tasks/upload_to_sftp.py @@ -56,7 +56,7 @@ def upload_to_sftp(self, file_path: str, file_id: int = None): "This should only be used in development/testing. For production, remove " "SFTP_DISABLE_HOST_KEY_VERIFICATION or set it to False and configure known_hosts." ) - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # nosec B507 - Configurable, warns user + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # nosec B507 - Configurable, warns user # noqa: S507 else: # Use system known_hosts for host key verification (more secure) ssh.load_system_host_keys() @@ -149,8 +149,8 @@ def upload_to_sftp(self, file_path: str, file_id: int = None): if "sftp" in locals(): sftp.close() ssh.close() - except Exception: - pass + except Exception: # noqa: S110 + pass # Ignore errors during cleanup error_msg = f"Failed to upload {filename} to SFTP server: {str(e)}" logger.error(f"[{task_id}] {error_msg}") diff --git a/app/tasks/upload_with_rclone.py b/app/tasks/upload_with_rclone.py index 59b83f71..1d02d374 100644 --- a/app/tasks/upload_with_rclone.py +++ b/app/tasks/upload_with_rclone.py @@ -57,7 +57,7 @@ def upload_with_rclone(self, file_path: str, destination: str): # Ensure the remote path exists (create folders if needed) mkdir_cmd = ["rclone", "mkdir", "--config", rclone_config_path, destination] - subprocess.run(mkdir_cmd, check=True, capture_output=True) + subprocess.run(mkdir_cmd, check=True, capture_output=True) # noqa: S603 # Construct the upload command upload_cmd = ["rclone", "copy", "--config", rclone_config_path, file_path, destination, "--progress"] @@ -65,14 +65,14 @@ def upload_with_rclone(self, file_path: str, destination: str): log_task_progress(task_id, "rclone_upload", "in_progress", f"Executing rclone copy to {destination}") # Execute the upload command - result = subprocess.run(upload_cmd, check=True, capture_output=True, text=True) + result = subprocess.run(upload_cmd, check=True, capture_output=True, text=True) # noqa: S603 # Check if upload was successful if result.returncode == 0: # Try to get a public link if possible try: link_cmd = ["rclone", "link", "--config", rclone_config_path, f"{destination}/{filename}"] - link_result = subprocess.run(link_cmd, capture_output=True, text=True, check=False) + link_result = subprocess.run(link_cmd, capture_output=True, text=True, check=False) # noqa: S603 public_url = link_result.stdout.strip() if link_result.returncode == 0 else None except (subprocess.SubprocessError, OSError) as e: logger.warning(f"[{task_id}] Failed to get public link for {filename}: {str(e)}") @@ -135,7 +135,7 @@ def send_to_all_rclone_destinations(self, file_path: str): # Get list of configured destinations from rclone try: remotes_cmd = ["rclone", "listremotes", "--config", rclone_config_path] - result = subprocess.run(remotes_cmd, check=True, capture_output=True, text=True) + result = subprocess.run(remotes_cmd, check=True, capture_output=True, text=True) # noqa: S603 if result.returncode == 0: # Process the list of remotes diff --git a/app/utils/config_loader.py b/app/utils/config_loader.py index 8d251712..2efe846a 100644 --- a/app/utils/config_loader.py +++ b/app/utils/config_loader.py @@ -87,21 +87,21 @@ def convert_setting_value(value: Optional[str], field_type: Any) -> Any: field_type = next((arg for arg in args if arg is not type(None)), str) # Convert based on type - if field_type == bool: + if field_type is bool: return value.lower() in ("true", "1", "yes", "y", "t") - elif field_type == int: + elif field_type is int: try: return int(value) except ValueError: logger.warning(f"Failed to convert '{value}' to int, returning 0") return 0 - elif field_type == float: + elif field_type is float: try: return float(value) except ValueError: logger.warning(f"Failed to convert '{value}' to float, returning 0.0") return 0.0 - elif field_type == list or getattr(field_type, "__origin__", None) == list: + elif field_type is list or getattr(field_type, "__origin__", None) is list: # Handle list types - assume comma-separated values if isinstance(value, str): return [item.strip() for item in value.split(",") if item.strip()] diff --git a/app/views/status.py b/app/views/status.py index da883068..6c5fbe52 100644 --- a/app/views/status.py +++ b/app/views/status.py @@ -57,8 +57,8 @@ async def status_dashboard(request: Request): # Try to get runtime information try: container_info["runtime_info"] = settings.runtime_info - except Exception: - pass + except Exception: # noqa: S110 + pass # Ignore if runtime_info not available else: container_info["is_docker"] = False diff --git a/tests/test_check_credentials.py b/tests/test_check_credentials.py index 31a684df..4aa93c3c 100644 --- a/tests/test_check_credentials.py +++ b/tests/test_check_credentials.py @@ -2,7 +2,7 @@ import json import os -from unittest.mock import MagicMock, Mock, patch +from unittest.mock import Mock, patch import pytest @@ -12,9 +12,6 @@ from app.tasks.check_credentials import ( get_failure_state, save_failure_state, sync_test_azure_connection, - sync_test_dropbox_token, - sync_test_google_drive_token, - sync_test_onedrive_token, sync_test_openai_connection, unwrap_decorated_function, ) diff --git a/tests/test_e2e_full_stack.py b/tests/test_e2e_full_stack.py index 1efa1279..1573c2c3 100644 --- a/tests/test_e2e_full_stack.py +++ b/tests/test_e2e_full_stack.py @@ -449,7 +449,7 @@ class TestFullInfrastructure: # Create SFTP client ssh = paramiko.SSHClient() - ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) # noqa: S507 # Connect to SFTP server ssh.connect( diff --git a/tests/test_security_headers.py b/tests/test_security_headers.py index 203ce29d..4a22f306 100644 --- a/tests/test_security_headers.py +++ b/tests/test_security_headers.py @@ -207,9 +207,10 @@ class TestSecurityHeadersMiddleware: @pytest.mark.asyncio async def test_dispatch_adds_headers_when_enabled(self): """Test dispatch adds security headers when enabled.""" + from fastapi import Response + from app.config import settings from app.middleware.security_headers import SecurityHeadersMiddleware - from fastapi import Response if not settings.security_headers_enabled: pytest.skip("Security headers disabled in configuration") @@ -231,10 +232,10 @@ class TestSecurityHeadersMiddleware: @pytest.mark.asyncio async def test_dispatch_skips_headers_when_disabled(self): """Test dispatch skips headers when disabled.""" - from app.config import settings - from app.middleware.security_headers import SecurityHeadersMiddleware from fastapi import Response + from app.middleware.security_headers import SecurityHeadersMiddleware + # Create a config copy with headers disabled mock_config = Mock() mock_config.security_headers_enabled = False @@ -254,9 +255,10 @@ class TestSecurityHeadersMiddleware: def test_add_security_headers_hsts(self): """Test _add_security_headers adds HSTS header.""" - from app.middleware.security_headers import SecurityHeadersMiddleware from fastapi import Response + from app.middleware.security_headers import SecurityHeadersMiddleware + mock_config = Mock() mock_config.security_headers_enabled = True mock_config.security_header_hsts_enabled = True @@ -275,9 +277,10 @@ class TestSecurityHeadersMiddleware: def test_add_security_headers_csp(self): """Test _add_security_headers adds CSP header.""" - from app.middleware.security_headers import SecurityHeadersMiddleware from fastapi import Response + from app.middleware.security_headers import SecurityHeadersMiddleware + mock_config = Mock() mock_config.security_headers_enabled = True mock_config.security_header_hsts_enabled = False @@ -296,9 +299,10 @@ class TestSecurityHeadersMiddleware: def test_add_security_headers_x_frame_options(self): """Test _add_security_headers adds X-Frame-Options header.""" - from app.middleware.security_headers import SecurityHeadersMiddleware from fastapi import Response + from app.middleware.security_headers import SecurityHeadersMiddleware + mock_config = Mock() mock_config.security_headers_enabled = True mock_config.security_header_hsts_enabled = False @@ -317,9 +321,10 @@ class TestSecurityHeadersMiddleware: def test_add_security_headers_x_content_type_options(self): """Test _add_security_headers adds X-Content-Type-Options header.""" - from app.middleware.security_headers import SecurityHeadersMiddleware from fastapi import Response + from app.middleware.security_headers import SecurityHeadersMiddleware + mock_config = Mock() mock_config.security_headers_enabled = True mock_config.security_header_hsts_enabled = False @@ -337,9 +342,10 @@ class TestSecurityHeadersMiddleware: def test_add_all_security_headers(self): """Test _add_security_headers adds all headers when all enabled.""" - from app.middleware.security_headers import SecurityHeadersMiddleware from fastapi import Response + from app.middleware.security_headers import SecurityHeadersMiddleware + mock_config = Mock() mock_config.security_headers_enabled = True mock_config.security_header_hsts_enabled = True diff --git a/tests/test_upload_email.py b/tests/test_upload_email.py index cf9e319c..e8d6adf2 100644 --- a/tests/test_upload_email.py +++ b/tests/test_upload_email.py @@ -1,11 +1,8 @@ """Tests for app/tasks/upload_to_email.py module.""" import json -import os -import smtplib import socket from email.mime.multipart import MIMEMultipart -from pathlib import Path from unittest.mock import MagicMock, Mock, mock_open, patch import pytest diff --git a/tests/test_upload_ftp_additional.py b/tests/test_upload_ftp_additional.py index 03b5b3ee..86d1225c 100644 --- a/tests/test_upload_ftp_additional.py +++ b/tests/test_upload_ftp_additional.py @@ -1,7 +1,7 @@ """Additional tests for upload_to_ftp task.""" import ftplib -from unittest.mock import MagicMock, Mock, patch +from unittest.mock import Mock, patch import pytest @@ -152,7 +152,7 @@ class TestUploadToFtp: mock_settings.ftp_use_tls = True mock_ftp = Mock() - mock_ftp.cwd.side_effect = [ftplib.error_perm("No such directory"), None] + mock_ftp.cwd.side_effect = [ftplib.error_perm("No such directory"), None] # noqa: S321 mock_ftp_tls.return_value = mock_ftp mock_self = Mock() @@ -248,8 +248,8 @@ class TestUploadToFtp: mock_settings.ftp_use_tls = True mock_ftp = Mock() - mock_ftp.cwd.side_effect = ftplib.error_perm("Permission denied") - mock_ftp.mkd.side_effect = ftplib.error_perm("Cannot create directory") + mock_ftp.cwd.side_effect = ftplib.error_perm("Permission denied") # noqa: S321 + mock_ftp.mkd.side_effect = ftplib.error_perm("Cannot create directory") # noqa: S321 mock_ftp_tls.return_value = mock_ftp mock_self = Mock() diff --git a/tests/test_upload_google_drive.py b/tests/test_upload_google_drive.py index ef8d21b1..6b1882b9 100644 --- a/tests/test_upload_google_drive.py +++ b/tests/test_upload_google_drive.py @@ -1,7 +1,7 @@ """Tests for app/tasks/upload_to_google_drive.py module.""" import json -from unittest.mock import MagicMock, Mock, mock_open, patch +from unittest.mock import Mock, patch import pytest from google.auth.exceptions import RefreshError diff --git a/tests/test_views_status.py b/tests/test_views_status.py index decc1c25..956e7e84 100644 --- a/tests/test_views_status.py +++ b/tests/test_views_status.py @@ -240,7 +240,6 @@ class TestContainerInfoDetection: @patch("builtins.open", new_callable=mock_open, read_data="12:docker:/abc123456789") def test_extracts_container_id(self, mock_file, mock_exists): """Test extracts container ID from cgroup.""" - from app.views.status import status_dashboard mock_exists.return_value = True @@ -250,7 +249,6 @@ class TestContainerInfoDetection: @patch("app.views.status.os.path.exists") def test_handles_missing_cgroup_file(self, mock_exists): """Test handles missing cgroup file gracefully.""" - from app.views.status import status_dashboard mock_exists.side_effect = [True, False] # Docker env exists, but cgroup doesn't @@ -259,7 +257,6 @@ class TestContainerInfoDetection: @patch("app.views.status.settings") def test_includes_runtime_info_when_available(self, mock_settings): """Test includes runtime info when available.""" - from app.views.status import status_dashboard mock_settings.runtime_info = "Python 3.11.5 on Linux" From 307452fc8c5d437f309a050e6358e034f5d24fcb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 16:16:53 +0000 Subject: [PATCH 3/6] fix: add os.path.basename mocks to FTP and Google Drive tests Add patches for os.path.basename to prevent Mock object errors when os.path.exists is patched Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- tests/test_upload_ftp_additional.py | 56 +++++++++++++++++++++++------ tests/test_upload_google_drive.py | 30 ++++++++++++---- 2 files changed, 68 insertions(+), 18 deletions(-) diff --git a/tests/test_upload_ftp_additional.py b/tests/test_upload_ftp_additional.py index 86d1225c..d4b71c7b 100644 --- a/tests/test_upload_ftp_additional.py +++ b/tests/test_upload_ftp_additional.py @@ -18,14 +18,18 @@ class TestUploadToFtp: assert callable(upload_to_ftp) + @patch("app.tasks.upload_to_ftp.os.path.basename") @patch("app.tasks.upload_to_ftp.ftplib.FTP_TLS") @patch("app.tasks.upload_to_ftp.log_task_progress") @patch("app.tasks.upload_to_ftp.os.path.exists") @patch("app.tasks.upload_to_ftp.settings") @patch("builtins.open", create=True) - def test_uploads_file_with_ftps(self, mock_open, mock_settings, mock_exists, mock_log, mock_ftp_tls): + def test_uploads_file_with_ftps( + self, mock_open, mock_settings, mock_exists, mock_log, mock_ftp_tls, mock_basename + ): """Test uploads file using FTPS (FTP with TLS).""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.ftp_host = "ftp.example.com" mock_settings.ftp_port = 21 mock_settings.ftp_username = "user" @@ -47,6 +51,7 @@ class TestUploadToFtp: mock_ftp.login.assert_called_once() mock_ftp.prot_p.assert_called_once() + @patch("app.tasks.upload_to_ftp.os.path.basename") @patch("app.tasks.upload_to_ftp.ftplib.FTP") @patch("app.tasks.upload_to_ftp.ftplib.FTP_TLS") @patch("app.tasks.upload_to_ftp.log_task_progress") @@ -54,10 +59,11 @@ class TestUploadToFtp: @patch("app.tasks.upload_to_ftp.settings") @patch("builtins.open", create=True) def test_falls_back_to_plaintext_ftp( - self, mock_open, mock_settings, mock_exists, mock_log, mock_ftp_tls, mock_ftp + self, mock_open, mock_settings, mock_exists, mock_log, mock_ftp_tls, mock_ftp, mock_basename ): """Test falls back to plaintext FTP when FTPS fails.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.ftp_host = "ftp.example.com" mock_settings.ftp_port = 21 mock_settings.ftp_username = "user" @@ -83,15 +89,17 @@ class TestUploadToFtp: assert result["status"] == "Completed" assert result["used_tls"] is False + @patch("app.tasks.upload_to_ftp.os.path.basename") @patch("app.tasks.upload_to_ftp.ftplib.FTP_TLS") @patch("app.tasks.upload_to_ftp.log_task_progress") @patch("app.tasks.upload_to_ftp.os.path.exists") @patch("app.tasks.upload_to_ftp.settings") def test_raises_error_when_ftps_fails_and_plaintext_forbidden( - self, mock_settings, mock_exists, mock_log, mock_ftp_tls + self, mock_settings, mock_exists, mock_log, mock_ftp_tls, mock_basename ): """Test raises error when FTPS fails and plaintext is forbidden.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.ftp_host = "ftp.example.com" mock_settings.ftp_port = 21 mock_settings.ftp_username = "user" @@ -110,11 +118,13 @@ class TestUploadToFtp: with pytest.raises(Exception, match="FTPS connection failed and plaintext FTP is forbidden"): upload_to_ftp(mock_self, "/tmp/test.pdf") + @patch("app.tasks.upload_to_ftp.os.path.basename") @patch("app.tasks.upload_to_ftp.log_task_progress") @patch("app.tasks.upload_to_ftp.os.path.exists") - def test_raises_error_when_file_not_found(self, mock_exists, mock_log): + def test_raises_error_when_file_not_found(self, mock_exists, mock_log, mock_basename): """Test raises error when file not found.""" mock_exists.return_value = False + mock_basename.return_value = "file.pdf" mock_self = Mock() mock_self.request.id = "test-task-id" @@ -122,12 +132,14 @@ class TestUploadToFtp: with pytest.raises(FileNotFoundError): upload_to_ftp(mock_self, "/nonexistent/file.pdf") + @patch("app.tasks.upload_to_ftp.os.path.basename") @patch("app.tasks.upload_to_ftp.log_task_progress") @patch("app.tasks.upload_to_ftp.os.path.exists") @patch("app.tasks.upload_to_ftp.settings") - def test_raises_error_when_ftp_host_not_configured(self, mock_settings, mock_exists, mock_log): + def test_raises_error_when_ftp_host_not_configured(self, mock_settings, mock_exists, mock_log, mock_basename): """Test raises error when FTP host not configured.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.ftp_host = None mock_self = Mock() @@ -136,14 +148,18 @@ class TestUploadToFtp: with pytest.raises(ValueError, match="FTP host is not configured"): upload_to_ftp(mock_self, "/tmp/test.pdf") + @patch("app.tasks.upload_to_ftp.os.path.basename") @patch("app.tasks.upload_to_ftp.ftplib.FTP_TLS") @patch("app.tasks.upload_to_ftp.log_task_progress") @patch("app.tasks.upload_to_ftp.os.path.exists") @patch("app.tasks.upload_to_ftp.settings") @patch("builtins.open", create=True) - def test_creates_directory_structure(self, mock_open, mock_settings, mock_exists, mock_log, mock_ftp_tls): + def test_creates_directory_structure( + self, mock_open, mock_settings, mock_exists, mock_log, mock_ftp_tls, mock_basename + ): """Test creates directory structure if it doesn't exist.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.ftp_host = "ftp.example.com" mock_settings.ftp_port = 21 mock_settings.ftp_username = "user" @@ -163,14 +179,18 @@ class TestUploadToFtp: assert result["status"] == "Completed" mock_ftp.mkd.assert_called() + @patch("app.tasks.upload_to_ftp.os.path.basename") @patch("app.tasks.upload_to_ftp.ftplib.FTP") @patch("app.tasks.upload_to_ftp.log_task_progress") @patch("app.tasks.upload_to_ftp.os.path.exists") @patch("app.tasks.upload_to_ftp.settings") @patch("builtins.open", create=True) - def test_uses_plaintext_ftp_when_tls_disabled(self, mock_open, mock_settings, mock_exists, mock_log, mock_ftp): + def test_uses_plaintext_ftp_when_tls_disabled( + self, mock_open, mock_settings, mock_exists, mock_log, mock_ftp, mock_basename + ): """Test uses plaintext FTP when TLS is explicitly disabled.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.ftp_host = "ftp.example.com" mock_settings.ftp_port = 21 mock_settings.ftp_username = "user" @@ -190,12 +210,16 @@ class TestUploadToFtp: assert result["status"] == "Completed" assert result["used_tls"] is False + @patch("app.tasks.upload_to_ftp.os.path.basename") @patch("app.tasks.upload_to_ftp.log_task_progress") @patch("app.tasks.upload_to_ftp.os.path.exists") @patch("app.tasks.upload_to_ftp.settings") - def test_raises_error_when_plaintext_forbidden_and_tls_disabled(self, mock_settings, mock_exists, mock_log): + def test_raises_error_when_plaintext_forbidden_and_tls_disabled( + self, mock_settings, mock_exists, mock_log, mock_basename + ): """Test raises error when plaintext is forbidden and TLS is disabled.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.ftp_host = "ftp.example.com" mock_settings.ftp_port = 21 mock_settings.ftp_use_tls = False @@ -207,14 +231,18 @@ class TestUploadToFtp: with pytest.raises(Exception, match="Plaintext FTP is forbidden"): upload_to_ftp(mock_self, "/tmp/test.pdf") + @patch("app.tasks.upload_to_ftp.os.path.basename") @patch("app.tasks.upload_to_ftp.ftplib.FTP_TLS") @patch("app.tasks.upload_to_ftp.log_task_progress") @patch("app.tasks.upload_to_ftp.os.path.exists") @patch("app.tasks.upload_to_ftp.settings") @patch("builtins.open", create=True) - def test_removes_leading_slash_from_folder(self, mock_open, mock_settings, mock_exists, mock_log, mock_ftp_tls): + def test_removes_leading_slash_from_folder( + self, mock_open, mock_settings, mock_exists, mock_log, mock_ftp_tls, mock_basename + ): """Test removes leading slash from folder path.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.ftp_host = "ftp.example.com" mock_settings.ftp_port = 21 mock_settings.ftp_username = "user" @@ -233,13 +261,15 @@ class TestUploadToFtp: # Verify cwd was called with folder without leading slash mock_ftp.cwd.assert_called_with("uploads") + @patch("app.tasks.upload_to_ftp.os.path.basename") @patch("app.tasks.upload_to_ftp.ftplib.FTP_TLS") @patch("app.tasks.upload_to_ftp.log_task_progress") @patch("app.tasks.upload_to_ftp.os.path.exists") @patch("app.tasks.upload_to_ftp.settings") - def test_handles_directory_creation_error(self, mock_settings, mock_exists, mock_log, mock_ftp_tls): + def test_handles_directory_creation_error(self, mock_settings, mock_exists, mock_log, mock_ftp_tls, mock_basename): """Test handles directory creation error.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.ftp_host = "ftp.example.com" mock_settings.ftp_port = 21 mock_settings.ftp_username = "user" @@ -258,14 +288,18 @@ class TestUploadToFtp: with pytest.raises(Exception, match="Failed to change/create directory"): upload_to_ftp(mock_self, "/tmp/test.pdf") + @patch("app.tasks.upload_to_ftp.os.path.basename") @patch("app.tasks.upload_to_ftp.ftplib.FTP_TLS") @patch("app.tasks.upload_to_ftp.log_task_progress") @patch("app.tasks.upload_to_ftp.os.path.exists") @patch("app.tasks.upload_to_ftp.settings") @patch("builtins.open", create=True) - def test_returns_ftp_path_in_result(self, mock_open, mock_settings, mock_exists, mock_log, mock_ftp_tls): + def test_returns_ftp_path_in_result( + self, mock_open, mock_settings, mock_exists, mock_log, mock_ftp_tls, mock_basename + ): """Test returns FTP path in result.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.ftp_host = "ftp.example.com" mock_settings.ftp_port = 21 mock_settings.ftp_username = "user" diff --git a/tests/test_upload_google_drive.py b/tests/test_upload_google_drive.py index 6b1882b9..7d76faa4 100644 --- a/tests/test_upload_google_drive.py +++ b/tests/test_upload_google_drive.py @@ -219,6 +219,7 @@ class TestTruncatePropertyValue: class TestUploadToGoogleDriveTask: """Tests for upload_to_google_drive task.""" + @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.get_google_drive_service") @patch("app.tasks.upload_to_google_drive.extract_metadata_from_file") @patch("app.tasks.upload_to_google_drive.log_task_progress") @@ -226,10 +227,11 @@ class TestUploadToGoogleDriveTask: @patch("app.tasks.upload_to_google_drive.MediaFileUpload") @patch("app.tasks.upload_to_google_drive.settings") def test_uploads_file_successfully( - self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service + self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service, mock_basename ): """Test uploads file to Google Drive successfully.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.google_drive_folder_id = "folder_123" mock_extract.return_value = {} @@ -259,11 +261,13 @@ class TestUploadToGoogleDriveTask: assert result["google_drive_file_id"] == "file_123" assert "webViewLink" in result["google_drive_web_link"] + @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.log_task_progress") @patch("app.tasks.upload_to_google_drive.os.path.exists") - def test_raises_error_when_file_not_found(self, mock_exists, mock_log): + def test_raises_error_when_file_not_found(self, mock_exists, mock_log, mock_basename): """Test raises error when file not found.""" mock_exists.return_value = False + mock_basename.return_value = "file.pdf" mock_self = Mock() mock_self.request.id = "test-task-id" @@ -271,12 +275,14 @@ class TestUploadToGoogleDriveTask: with pytest.raises(FileNotFoundError): upload_to_google_drive(mock_self, "/nonexistent/file.pdf") + @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.get_google_drive_service") @patch("app.tasks.upload_to_google_drive.log_task_progress") @patch("app.tasks.upload_to_google_drive.os.path.exists") - def test_raises_error_when_service_init_fails(self, mock_exists, mock_log, mock_service): + def test_raises_error_when_service_init_fails(self, mock_exists, mock_log, mock_service, mock_basename): """Test raises error when service initialization fails.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_service.return_value = None mock_self = Mock() @@ -285,6 +291,7 @@ class TestUploadToGoogleDriveTask: with pytest.raises(Exception, match="Failed to initialize Google Drive service"): upload_to_google_drive(mock_self, "/tmp/test.pdf") + @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.get_google_drive_service") @patch("app.tasks.upload_to_google_drive.extract_metadata_from_file") @patch("app.tasks.upload_to_google_drive.log_task_progress") @@ -292,10 +299,11 @@ class TestUploadToGoogleDriveTask: @patch("app.tasks.upload_to_google_drive.MediaFileUpload") @patch("app.tasks.upload_to_google_drive.settings") def test_includes_metadata_in_upload( - self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service + self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service, mock_basename ): """Test includes metadata in upload.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.google_drive_folder_id = None metadata = {"document_type": "invoice", "amount": "100.00", "date": "2024-01-01"} @@ -324,6 +332,7 @@ class TestUploadToGoogleDriveTask: assert result["metadata_included"] is True + @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.get_google_drive_service") @patch("app.tasks.upload_to_google_drive.extract_metadata_from_file") @patch("app.tasks.upload_to_google_drive.log_task_progress") @@ -331,10 +340,11 @@ class TestUploadToGoogleDriveTask: @patch("app.tasks.upload_to_google_drive.MediaFileUpload") @patch("app.tasks.upload_to_google_drive.settings") def test_skips_nested_metadata_objects( - self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service + self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service, mock_basename ): """Test skips nested objects in metadata.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.google_drive_folder_id = None metadata = {"simple_field": "value", "nested_object": {"key": "value"}, "nested_list": [1, 2, 3]} @@ -370,14 +380,18 @@ class TestUploadToGoogleDriveTask: assert "nested_object" not in file_metadata["properties"] assert "nested_list" not in file_metadata["properties"] + @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.get_google_drive_service") @patch("app.tasks.upload_to_google_drive.log_task_progress") @patch("app.tasks.upload_to_google_drive.os.path.exists") @patch("app.tasks.upload_to_google_drive.MediaFileUpload") @patch("app.tasks.upload_to_google_drive.settings") - def test_handles_upload_exception(self, mock_settings, mock_media, mock_exists, mock_log, mock_service): + def test_handles_upload_exception( + self, mock_settings, mock_media, mock_exists, mock_log, mock_service, mock_basename + ): """Test handles upload exception.""" mock_exists.return_value = True + mock_basename.return_value = "test.pdf" mock_settings.google_drive_folder_id = None mock_drive_service = Mock() @@ -392,6 +406,7 @@ class TestUploadToGoogleDriveTask: with pytest.raises(Exception, match="Failed to upload"): upload_to_google_drive(mock_self, "/tmp/test.pdf") + @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.get_google_drive_service") @patch("app.tasks.upload_to_google_drive.extract_metadata_from_file") @patch("app.tasks.upload_to_google_drive.log_task_progress") @@ -399,9 +414,10 @@ class TestUploadToGoogleDriveTask: @patch("app.tasks.upload_to_google_drive.MediaFileUpload") @patch("app.tasks.upload_to_google_drive.settings") def test_sets_parent_folder_when_configured( - self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service + self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service, mock_basename ): """Test sets parent folder when configured.""" + mock_basename.return_value = "test.pdf" mock_exists.return_value = True mock_settings.google_drive_folder_id = "parent_folder_123" mock_extract.return_value = {} From aa077f2b6c06647be896a7579bd00055ea6d0f90 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 16:20:32 +0000 Subject: [PATCH 4/6] fix: track TLS usage with variable instead of isinstance check Replace isinstance(ftp, ftplib.FTP_TLS) with a boolean flag to avoid issues when FTP_TLS is mocked in tests. Also fix Google Drive test parameter passing. Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- app/tasks/upload_to_ftp.py | 4 +++- tests/test_upload_google_drive.py | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/app/tasks/upload_to_ftp.py b/app/tasks/upload_to_ftp.py index e6207e34..b7d8c9c8 100644 --- a/app/tasks/upload_to_ftp.py +++ b/app/tasks/upload_to_ftp.py @@ -53,6 +53,7 @@ def upload_to_ftp(self, file_path: str, file_id: int = None): # First attempt FTPS (FTP with TLS) use_tls = getattr(settings, "ftp_use_tls", True) # Default to try TLS allow_plaintext = getattr(settings, "ftp_allow_plaintext", True) # Default to allow plaintext fallback + used_tls = False # Track whether we successfully used TLS if use_tls: try: @@ -66,6 +67,7 @@ def upload_to_ftp(self, file_path: str, file_id: int = None): # Enable data protection - encrypt the data channel ftp.prot_p() logger.info("Successfully established FTPS connection with TLS") + used_tls = True except Exception as e: if not allow_plaintext: error_msg = f"FTPS connection failed and plaintext FTP is forbidden: {str(e)}" @@ -137,7 +139,7 @@ def upload_to_ftp(self, file_path: str, file_id: int = None): "file": file_path, "ftp_host": settings.ftp_host, "ftp_path": f"{settings.ftp_folder}/{filename}" if settings.ftp_folder else filename, - "used_tls": isinstance(ftp, ftplib.FTP_TLS), + "used_tls": used_tls, } except Exception as e: diff --git a/tests/test_upload_google_drive.py b/tests/test_upload_google_drive.py index 7d76faa4..75cc2fb2 100644 --- a/tests/test_upload_google_drive.py +++ b/tests/test_upload_google_drive.py @@ -328,7 +328,7 @@ class TestUploadToGoogleDriveTask: mock_self = Mock() mock_self.request.id = "test-task-id" - result = upload_to_google_drive(mock_self, "/tmp/test.pdf", include_metadata=True) + result = upload_to_google_drive(mock_self, "/tmp/test.pdf", True) assert result["metadata_included"] is True @@ -368,7 +368,7 @@ class TestUploadToGoogleDriveTask: mock_self = Mock() mock_self.request.id = "test-task-id" - result = upload_to_google_drive(mock_self, "/tmp/test.pdf", include_metadata=True) + result = upload_to_google_drive(mock_self, "/tmp/test.pdf", True) # Verify the create call was made mock_files.create.assert_called_once() From ddad5f222b19d71b5b91b7b991b3f4338cdde8ba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 16:24:25 +0000 Subject: [PATCH 5/6] fix: add os.path.splitext mocks and fix test assertions Add os.path.splitext patches to Google Drive tests, fix FTP directory creation test, and correct webViewLink assertion Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- tests/test_upload_ftp_additional.py | 9 ++++++++- tests/test_upload_google_drive.py | 29 ++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 8 deletions(-) diff --git a/tests/test_upload_ftp_additional.py b/tests/test_upload_ftp_additional.py index d4b71c7b..92102078 100644 --- a/tests/test_upload_ftp_additional.py +++ b/tests/test_upload_ftp_additional.py @@ -168,7 +168,14 @@ class TestUploadToFtp: mock_settings.ftp_use_tls = True mock_ftp = Mock() - mock_ftp.cwd.side_effect = [ftplib.error_perm("No such directory"), None] # noqa: S321 + # First cwd fails (trying full path), then for each subfolder: fail then succeed after mkd + mock_ftp.cwd.side_effect = [ + ftplib.error_perm("No such directory"), # noqa: S321 # Initial try for full path + ftplib.error_perm("No such directory"), # noqa: S321 # /uploads doesn't exist + None, # /uploads now exists after mkd + ftplib.error_perm("No such directory"), # noqa: S321 # /uploads/documents doesn't exist + None, # /uploads/documents now exists after mkd + ] mock_ftp_tls.return_value = mock_ftp mock_self = Mock() diff --git a/tests/test_upload_google_drive.py b/tests/test_upload_google_drive.py index 75cc2fb2..17e3567a 100644 --- a/tests/test_upload_google_drive.py +++ b/tests/test_upload_google_drive.py @@ -219,6 +219,7 @@ class TestTruncatePropertyValue: class TestUploadToGoogleDriveTask: """Tests for upload_to_google_drive task.""" + @patch("app.tasks.upload_to_google_drive.os.path.splitext") @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.get_google_drive_service") @patch("app.tasks.upload_to_google_drive.extract_metadata_from_file") @@ -227,11 +228,12 @@ class TestUploadToGoogleDriveTask: @patch("app.tasks.upload_to_google_drive.MediaFileUpload") @patch("app.tasks.upload_to_google_drive.settings") def test_uploads_file_successfully( - self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service, mock_basename + self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service, mock_basename, mock_splitext ): """Test uploads file to Google Drive successfully.""" mock_exists.return_value = True mock_basename.return_value = "test.pdf" + mock_splitext.return_value = ("/tmp/test", ".pdf") mock_settings.google_drive_folder_id = "folder_123" mock_extract.return_value = {} @@ -259,7 +261,7 @@ class TestUploadToGoogleDriveTask: assert result["status"] == "Completed" assert result["google_drive_file_id"] == "file_123" - assert "webViewLink" in result["google_drive_web_link"] + assert result["google_drive_web_link"] == "https://drive.google.com/file/d/file_123" @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.log_task_progress") @@ -275,14 +277,18 @@ class TestUploadToGoogleDriveTask: with pytest.raises(FileNotFoundError): upload_to_google_drive(mock_self, "/nonexistent/file.pdf") + @patch("app.tasks.upload_to_google_drive.os.path.splitext") @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.get_google_drive_service") @patch("app.tasks.upload_to_google_drive.log_task_progress") @patch("app.tasks.upload_to_google_drive.os.path.exists") - def test_raises_error_when_service_init_fails(self, mock_exists, mock_log, mock_service, mock_basename): + def test_raises_error_when_service_init_fails( + self, mock_exists, mock_log, mock_service, mock_basename, mock_splitext + ): """Test raises error when service initialization fails.""" mock_exists.return_value = True mock_basename.return_value = "test.pdf" + mock_splitext.return_value = ("/tmp/test", ".pdf") mock_service.return_value = None mock_self = Mock() @@ -291,6 +297,8 @@ class TestUploadToGoogleDriveTask: with pytest.raises(Exception, match="Failed to initialize Google Drive service"): upload_to_google_drive(mock_self, "/tmp/test.pdf") + @patch("app.tasks.upload_to_google_drive.os.path.splitext") + @patch("app.tasks.upload_to_google_drive.os.path.splitext") @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.get_google_drive_service") @patch("app.tasks.upload_to_google_drive.extract_metadata_from_file") @@ -299,11 +307,12 @@ class TestUploadToGoogleDriveTask: @patch("app.tasks.upload_to_google_drive.MediaFileUpload") @patch("app.tasks.upload_to_google_drive.settings") def test_includes_metadata_in_upload( - self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service, mock_basename + self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service, mock_basename, mock_splitext ): """Test includes metadata in upload.""" mock_exists.return_value = True mock_basename.return_value = "test.pdf" + mock_splitext.return_value = ("/tmp/test", ".pdf") mock_settings.google_drive_folder_id = None metadata = {"document_type": "invoice", "amount": "100.00", "date": "2024-01-01"} @@ -332,6 +341,7 @@ class TestUploadToGoogleDriveTask: assert result["metadata_included"] is True + @patch("app.tasks.upload_to_google_drive.os.path.splitext") @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.get_google_drive_service") @patch("app.tasks.upload_to_google_drive.extract_metadata_from_file") @@ -340,11 +350,12 @@ class TestUploadToGoogleDriveTask: @patch("app.tasks.upload_to_google_drive.MediaFileUpload") @patch("app.tasks.upload_to_google_drive.settings") def test_skips_nested_metadata_objects( - self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service, mock_basename + self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service, mock_basename, mock_splitext ): """Test skips nested objects in metadata.""" mock_exists.return_value = True mock_basename.return_value = "test.pdf" + mock_splitext.return_value = ("/tmp/test", ".pdf") mock_settings.google_drive_folder_id = None metadata = {"simple_field": "value", "nested_object": {"key": "value"}, "nested_list": [1, 2, 3]} @@ -380,6 +391,7 @@ class TestUploadToGoogleDriveTask: assert "nested_object" not in file_metadata["properties"] assert "nested_list" not in file_metadata["properties"] + @patch("app.tasks.upload_to_google_drive.os.path.splitext") @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.get_google_drive_service") @patch("app.tasks.upload_to_google_drive.log_task_progress") @@ -387,11 +399,12 @@ class TestUploadToGoogleDriveTask: @patch("app.tasks.upload_to_google_drive.MediaFileUpload") @patch("app.tasks.upload_to_google_drive.settings") def test_handles_upload_exception( - self, mock_settings, mock_media, mock_exists, mock_log, mock_service, mock_basename + self, mock_settings, mock_media, mock_exists, mock_log, mock_service, mock_basename, mock_splitext ): """Test handles upload exception.""" mock_exists.return_value = True mock_basename.return_value = "test.pdf" + mock_splitext.return_value = ("/tmp/test", ".pdf") mock_settings.google_drive_folder_id = None mock_drive_service = Mock() @@ -406,6 +419,7 @@ class TestUploadToGoogleDriveTask: with pytest.raises(Exception, match="Failed to upload"): upload_to_google_drive(mock_self, "/tmp/test.pdf") + @patch("app.tasks.upload_to_google_drive.os.path.splitext") @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.get_google_drive_service") @patch("app.tasks.upload_to_google_drive.extract_metadata_from_file") @@ -414,10 +428,11 @@ class TestUploadToGoogleDriveTask: @patch("app.tasks.upload_to_google_drive.MediaFileUpload") @patch("app.tasks.upload_to_google_drive.settings") def test_sets_parent_folder_when_configured( - self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service, mock_basename + self, mock_settings, mock_media, mock_exists, mock_log, mock_extract, mock_service, mock_basename, mock_splitext ): """Test sets parent folder when configured.""" mock_basename.return_value = "test.pdf" + mock_splitext.return_value = ("/tmp/test", ".pdf") mock_exists.return_value = True mock_settings.google_drive_folder_id = "parent_folder_123" mock_extract.return_value = {} From 92194e2286cb6cd2e954bae3f9bae003d5a0e6fa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 16:26:00 +0000 Subject: [PATCH 6/6] fix: remove duplicate os.path.splitext patch decorator Remove accidentally duplicated patch decorator that was causing test parameter count mismatch Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com> --- tests/test_upload_google_drive.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_upload_google_drive.py b/tests/test_upload_google_drive.py index 17e3567a..9cb4a8c1 100644 --- a/tests/test_upload_google_drive.py +++ b/tests/test_upload_google_drive.py @@ -297,7 +297,6 @@ class TestUploadToGoogleDriveTask: with pytest.raises(Exception, match="Failed to initialize Google Drive service"): upload_to_google_drive(mock_self, "/tmp/test.pdf") - @patch("app.tasks.upload_to_google_drive.os.path.splitext") @patch("app.tasks.upload_to_google_drive.os.path.splitext") @patch("app.tasks.upload_to_google_drive.os.path.basename") @patch("app.tasks.upload_to_google_drive.get_google_drive_service")