Files
gh-christianlouis-docuelevate/tests/test_upload_to_dropbox.py
T
2026-02-13 09:10:52 +00:00

328 lines
13 KiB
Python

"""
Tests for app/tasks/upload_to_dropbox.py module.
Covers _validate_dropbox_settings, get_dropbox_access_token, get_dropbox_client,
and upload_to_dropbox Celery task.
"""
from unittest.mock import Mock, patch
import pytest
from dropbox.exceptions import ApiError, AuthError
@pytest.mark.unit
class TestValidateDropboxSettings:
"""Tests for _validate_dropbox_settings function."""
def test_all_settings_present(self):
"""Test validation passes when all settings are present."""
from app.tasks.upload_to_dropbox import _validate_dropbox_settings
with patch("app.tasks.upload_to_dropbox.settings") as mock_settings:
mock_settings.dropbox_refresh_token = "token"
mock_settings.dropbox_app_key = "key"
mock_settings.dropbox_app_secret = "secret"
assert _validate_dropbox_settings() is True
def test_missing_refresh_token(self):
"""Test validation fails when refresh token is missing."""
from app.tasks.upload_to_dropbox import _validate_dropbox_settings
with patch("app.tasks.upload_to_dropbox.settings") as mock_settings:
mock_settings.dropbox_refresh_token = ""
mock_settings.dropbox_app_key = "key"
mock_settings.dropbox_app_secret = "secret"
assert _validate_dropbox_settings() is False
def test_missing_app_key(self):
"""Test validation fails when app key is missing."""
from app.tasks.upload_to_dropbox import _validate_dropbox_settings
with patch("app.tasks.upload_to_dropbox.settings") as mock_settings:
mock_settings.dropbox_refresh_token = "token"
mock_settings.dropbox_app_key = ""
mock_settings.dropbox_app_secret = "secret"
assert _validate_dropbox_settings() is False
def test_missing_app_secret(self):
"""Test validation fails when app secret is missing."""
from app.tasks.upload_to_dropbox import _validate_dropbox_settings
with patch("app.tasks.upload_to_dropbox.settings") as mock_settings:
mock_settings.dropbox_refresh_token = "token"
mock_settings.dropbox_app_key = "key"
mock_settings.dropbox_app_secret = ""
assert _validate_dropbox_settings() is False
def test_missing_all_settings(self):
"""Test validation fails when all settings are missing."""
from app.tasks.upload_to_dropbox import _validate_dropbox_settings
with patch("app.tasks.upload_to_dropbox.settings") as mock_settings:
mock_settings.dropbox_refresh_token = None
mock_settings.dropbox_app_key = None
mock_settings.dropbox_app_secret = None
assert _validate_dropbox_settings() is False
@pytest.mark.unit
class TestGetDropboxAccessToken:
"""Tests for get_dropbox_access_token function."""
@patch("app.tasks.upload_to_dropbox.requests.post")
@patch("app.tasks.upload_to_dropbox.settings")
def test_successful_refresh(self, mock_settings, mock_post):
"""Test successful token refresh."""
from app.tasks.upload_to_dropbox import get_dropbox_access_token
mock_settings.dropbox_refresh_token = "refresh-token"
mock_settings.dropbox_app_key = "app-key"
mock_settings.dropbox_app_secret = "app-secret"
mock_settings.http_request_timeout = 30
mock_response = Mock()
mock_response.status_code = 200
mock_response.json.return_value = {"access_token": "new-access-token"}
mock_post.return_value = mock_response
token = get_dropbox_access_token()
assert token == "new-access-token"
@patch("app.tasks.upload_to_dropbox.requests.post")
@patch("app.tasks.upload_to_dropbox.settings")
def test_refresh_failure_raises(self, mock_settings, mock_post):
"""Test that failed token refresh raises exception."""
from app.tasks.upload_to_dropbox import get_dropbox_access_token
mock_settings.dropbox_refresh_token = "refresh-token"
mock_settings.dropbox_app_key = "app-key"
mock_settings.dropbox_app_secret = "app-secret"
mock_settings.http_request_timeout = 30
mock_response = Mock()
mock_response.status_code = 400
mock_response.text = "invalid_grant"
mock_post.return_value = mock_response
with pytest.raises(Exception, match="Failed to refresh"):
get_dropbox_access_token()
def test_returns_none_when_settings_missing(self):
"""Test returns None when settings are not configured."""
from app.tasks.upload_to_dropbox import get_dropbox_access_token
with patch("app.tasks.upload_to_dropbox.settings") as mock_settings:
mock_settings.dropbox_refresh_token = None
mock_settings.dropbox_app_key = None
mock_settings.dropbox_app_secret = None
result = get_dropbox_access_token()
assert result is None
@pytest.mark.unit
class TestGetDropboxClient:
"""Tests for get_dropbox_client function."""
@patch("app.tasks.upload_to_dropbox.dropbox.Dropbox")
@patch("app.tasks.upload_to_dropbox.settings")
def test_successful_client_creation(self, mock_settings, mock_dropbox):
"""Test successful Dropbox client creation."""
from app.tasks.upload_to_dropbox import get_dropbox_client
mock_settings.dropbox_app_key = "app-key"
mock_settings.dropbox_app_secret = "app-secret"
mock_settings.dropbox_refresh_token = "refresh-token"
mock_instance = Mock()
mock_dropbox.return_value = mock_instance
client = get_dropbox_client()
assert client == mock_instance
mock_instance.users_get_current_account.assert_called_once()
@patch("app.tasks.upload_to_dropbox.settings")
def test_missing_app_key_raises(self, mock_settings):
"""Test that missing app key raises ValueError."""
from app.tasks.upload_to_dropbox import get_dropbox_client
mock_settings.dropbox_app_key = ""
mock_settings.dropbox_app_secret = "secret"
mock_settings.dropbox_refresh_token = "token"
with pytest.raises(ValueError, match="app key or app secret"):
get_dropbox_client()
@patch("app.tasks.upload_to_dropbox.settings")
def test_missing_refresh_token_raises(self, mock_settings):
"""Test that missing refresh token raises ValueError."""
from app.tasks.upload_to_dropbox import get_dropbox_client
mock_settings.dropbox_app_key = "key"
mock_settings.dropbox_app_secret = "secret"
mock_settings.dropbox_refresh_token = ""
with pytest.raises(ValueError, match="refresh token"):
get_dropbox_client()
@patch("app.tasks.upload_to_dropbox.dropbox.Dropbox")
@patch("app.tasks.upload_to_dropbox.settings")
def test_auth_error_propagated(self, mock_settings, mock_dropbox):
"""Test that AuthError is propagated."""
from app.tasks.upload_to_dropbox import get_dropbox_client
mock_settings.dropbox_app_key = "key"
mock_settings.dropbox_app_secret = "secret"
mock_settings.dropbox_refresh_token = "token"
mock_instance = Mock()
mock_instance.users_get_current_account.side_effect = AuthError("req-id", "Invalid token")
mock_dropbox.return_value = mock_instance
with pytest.raises(AuthError):
get_dropbox_client()
@pytest.mark.unit
class TestUploadToDropbox:
"""Tests for upload_to_dropbox Celery task."""
@patch("app.tasks.upload_to_dropbox.log_task_progress")
def test_file_not_found(self, mock_log):
"""Test that missing file raises FileNotFoundError."""
from app.tasks.upload_to_dropbox import upload_to_dropbox
with pytest.raises(FileNotFoundError):
upload_to_dropbox.__wrapped__("/nonexistent/file.pdf", file_id=1)
@patch("app.tasks.upload_to_dropbox.log_task_progress")
@patch("app.tasks.upload_to_dropbox.settings")
def test_skipped_when_not_configured(self, mock_settings, mock_log, tmp_path):
"""Test upload skipped when Dropbox not configured."""
from app.tasks.upload_to_dropbox import upload_to_dropbox
mock_settings.dropbox_app_key = ""
mock_settings.dropbox_app_secret = ""
mock_settings.dropbox_refresh_token = ""
test_file = tmp_path / "test.pdf"
test_file.write_bytes(b"test content")
result = upload_to_dropbox.apply(args=[str(test_file)], kwargs={"file_id": 1}).get()
assert result["status"] == "Skipped"
@patch("app.tasks.upload_to_dropbox.get_unique_filename")
@patch("app.tasks.upload_to_dropbox.extract_remote_path")
@patch("app.tasks.upload_to_dropbox.get_dropbox_client")
@patch("app.tasks.upload_to_dropbox.log_task_progress")
@patch("app.tasks.upload_to_dropbox.settings")
def test_successful_small_file_upload(
self, mock_settings, mock_log, mock_client, mock_extract, mock_unique, tmp_path
):
"""Test successful upload of a small file."""
from app.tasks.upload_to_dropbox import upload_to_dropbox
mock_settings.dropbox_app_key = "key"
mock_settings.dropbox_app_secret = "secret"
mock_settings.dropbox_refresh_token = "token"
mock_settings.dropbox_folder = "/uploads"
mock_settings.workdir = str(tmp_path)
test_file = tmp_path / "test.pdf"
test_file.write_bytes(b"small file content")
mock_dbx = Mock()
mock_client.return_value = mock_dbx
mock_extract.return_value = "uploads/test.pdf"
mock_unique.return_value = "/uploads/test.pdf"
result = upload_to_dropbox.apply(args=[str(test_file)], kwargs={"file_id": 1}).get()
assert result["status"] == "Completed"
assert result["file_path"] == str(test_file)
mock_dbx.files_upload.assert_called_once()
@patch("app.tasks.upload_to_dropbox.get_unique_filename")
@patch("app.tasks.upload_to_dropbox.extract_remote_path")
@patch("app.tasks.upload_to_dropbox.get_dropbox_client")
@patch("app.tasks.upload_to_dropbox.log_task_progress")
@patch("app.tasks.upload_to_dropbox.settings")
def test_large_file_chunked_upload(self, mock_settings, mock_log, mock_client, mock_extract, mock_unique, tmp_path):
"""Test chunked upload for large files (>10MB)."""
from app.tasks.upload_to_dropbox import upload_to_dropbox
mock_settings.dropbox_app_key = "key"
mock_settings.dropbox_app_secret = "secret"
mock_settings.dropbox_refresh_token = "token"
mock_settings.dropbox_folder = "/uploads"
mock_settings.workdir = str(tmp_path)
# Create a file larger than 10MB threshold
test_file = tmp_path / "large.pdf"
test_file.write_bytes(b"x" * (11 * 1024 * 1024))
mock_dbx = Mock()
mock_client.return_value = mock_dbx
mock_extract.return_value = "uploads/large.pdf"
mock_unique.return_value = "/uploads/large.pdf"
mock_session = Mock()
mock_session.session_id = "session-123"
mock_dbx.files_upload_session_start.return_value = mock_session
result = upload_to_dropbox.apply(args=[str(test_file)], kwargs={"file_id": 1}).get()
assert result["status"] == "Completed"
mock_dbx.files_upload_session_start.assert_called_once()
@patch("app.tasks.upload_to_dropbox.get_dropbox_client")
@patch("app.tasks.upload_to_dropbox.log_task_progress")
@patch("app.tasks.upload_to_dropbox.settings")
def test_auth_error_handling(self, mock_settings, mock_log, mock_client, tmp_path):
"""Test AuthError during upload is caught and re-raised."""
from app.tasks.upload_to_dropbox import upload_to_dropbox
mock_settings.dropbox_app_key = "key"
mock_settings.dropbox_app_secret = "secret"
mock_settings.dropbox_refresh_token = "token"
mock_settings.dropbox_folder = "/uploads"
mock_settings.workdir = str(tmp_path)
test_file = tmp_path / "test.pdf"
test_file.write_bytes(b"test content")
mock_client.side_effect = AuthError("req-id", "Invalid token")
with pytest.raises(Exception, match="Authentication failed"):
upload_to_dropbox.apply(args=[str(test_file)], kwargs={"file_id": 1}).get()
@patch("app.tasks.upload_to_dropbox.get_dropbox_client")
@patch("app.tasks.upload_to_dropbox.log_task_progress")
@patch("app.tasks.upload_to_dropbox.settings")
def test_api_error_handling(self, mock_settings, mock_log, mock_client, tmp_path):
"""Test ApiError during upload is caught and re-raised."""
from app.tasks.upload_to_dropbox import upload_to_dropbox
mock_settings.dropbox_app_key = "key"
mock_settings.dropbox_app_secret = "secret"
mock_settings.dropbox_refresh_token = "token"
mock_settings.dropbox_folder = "/uploads"
mock_settings.workdir = str(tmp_path)
test_file = tmp_path / "test.pdf"
test_file.write_bytes(b"test content")
mock_error = Mock()
mock_error.is_path.return_value = False
mock_client.side_effect = ApiError("req-id", mock_error, "user msg", "header")
with pytest.raises(Exception, match="Failed to upload"):
upload_to_dropbox.apply(args=[str(test_file)], kwargs={"file_id": 1}).get()