test: add tests for gmail_apply_labels=False and cloud source type dispatching

Add test_gmail_labels_disabled_when_gmail_apply_labels_false to verify
that Gmail star/label operations are skipped when gmail_apply_labels=False.

Add four tests to TestPullUserIntegrationWatchFolders for cloud source
type dispatching: S3, Dropbox, unknown provider, and explicit local type.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-09 02:26:10 +00:00
parent e7d1289f1b
commit 79fc11b452
2 changed files with 181 additions and 0 deletions
+53
View File
@@ -687,6 +687,59 @@ class TestPullInbox:
mock_star.assert_called_once_with(mock_mail, b"1")
mock_label.assert_called_once_with(mock_mail, b"1", label="Ingested")
@patch("app.tasks.imap_tasks.email_already_has_label")
@patch("app.tasks.imap_tasks.mark_as_processed_with_label")
@patch("app.tasks.imap_tasks.mark_as_processed_with_star")
@patch("app.tasks.imap_tasks.fetch_attachments_and_enqueue")
@patch("app.tasks.imap_tasks.imaplib.IMAP4_SSL")
@patch("app.tasks.imap_tasks.load_processed_emails")
@patch("app.tasks.imap_tasks.save_processed_emails")
@patch("app.tasks.imap_tasks.settings")
def test_gmail_labels_disabled_when_gmail_apply_labels_false(
self,
mock_settings,
mock_save,
mock_load,
mock_imap_class,
mock_fetch,
mock_star,
mock_label,
mock_has_label,
):
"""Gmail star/label operations should be skipped when gmail_apply_labels=False."""
mock_settings.workdir = "/tmp"
mock_settings.imap_readonly_mode = False
mock_load.return_value = {}
mock_mail = MagicMock()
mock_imap_class.return_value = mock_mail
import email
msg = email.message.EmailMessage()
msg["Message-ID"] = "<test-no-labels@gmail.com>"
raw_email = msg.as_bytes()
mock_mail.login.return_value = ("OK", [])
mock_mail.select.return_value = ("OK", [])
mock_mail.search.return_value = ("OK", [b"1"])
mock_mail.fetch.return_value = ("OK", [[None, raw_email]])
pull_inbox(
mailbox_key="imap2",
host="imap.gmail.com",
port=993,
username="user@gmail.com",
password=_TEST_CREDENTIAL,
use_ssl=True,
delete_after_process=False,
gmail_apply_labels=False,
)
mock_star.assert_not_called()
mock_label.assert_not_called()
mock_has_label.assert_not_called()
mock_fetch.assert_called()
@patch("app.tasks.imap_tasks.email_already_has_label")
@patch("app.tasks.imap_tasks.imaplib.IMAP4_SSL")
@patch("app.tasks.imap_tasks.load_processed_emails")
+128
View File
@@ -3843,6 +3843,134 @@ class TestPullUserIntegrationWatchFolders:
result = _pull_user_integration_watch_folders()
assert result["status"] == "ok"
@patch("app.tasks.watch_folder_tasks._get_db_session")
@patch("app.tasks.watch_folder_tasks._load_cache", return_value={})
@patch("app.tasks.watch_folder_tasks._save_cache")
def test_dispatches_s3_source_type(self, mock_save, mock_load, mock_session_factory):
"""WATCH_FOLDER with source_type 's3' should dispatch to _scan_user_s3_folder."""
from app.tasks.watch_folder_tasks import (
_USER_WF_CLOUD_HANDLERS,
_pull_user_integration_watch_folders,
)
mock_integ = MagicMock()
mock_integ.id = 30
mock_integ.owner_id = "owner-s3"
mock_integ.config = (
'{"source_type": "s3", "bucket": "test-bucket", "prefix": "inbox/", "delete_after_process": false}'
)
mock_integ.is_active = True
mock_integ.credentials = "encrypted-s3-creds"
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.all.return_value = [mock_integ]
mock_session_factory.return_value = mock_db
mock_s3_handler = MagicMock(return_value=3)
original_handler = _USER_WF_CLOUD_HANDLERS.get("s3")
_USER_WF_CLOUD_HANDLERS["s3"] = mock_s3_handler
try:
with patch(
"app.utils.encryption.decrypt_value",
return_value='{"access_key_id": "AKI", "secret_access_key": "SK"}',
):
result = _pull_user_integration_watch_folders()
assert result["status"] == "ok"
assert result["files_enqueued"] == 3
mock_s3_handler.assert_called_once()
args = mock_s3_handler.call_args
assert args[0][0]["source_type"] == "s3"
assert args[0][4] == "owner-s3"
finally:
if original_handler is not None:
_USER_WF_CLOUD_HANDLERS["s3"] = original_handler
@patch("app.tasks.watch_folder_tasks._get_db_session")
@patch("app.tasks.watch_folder_tasks._load_cache", return_value={})
@patch("app.tasks.watch_folder_tasks._save_cache")
def test_dispatches_dropbox_source_type(self, mock_save, mock_load, mock_session_factory):
"""WATCH_FOLDER with source_type 'dropbox' should dispatch to _scan_user_dropbox_folder."""
from app.tasks.watch_folder_tasks import (
_USER_WF_CLOUD_HANDLERS,
_pull_user_integration_watch_folders,
)
mock_integ = MagicMock()
mock_integ.id = 31
mock_integ.owner_id = "owner-dbx"
mock_integ.config = '{"source_type": "dropbox", "folder_path": "/Inbox", "delete_after_process": false}'
mock_integ.is_active = True
mock_integ.credentials = "encrypted-dbx-creds"
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.all.return_value = [mock_integ]
mock_session_factory.return_value = mock_db
mock_dbx_handler = MagicMock(return_value=5)
original_handler = _USER_WF_CLOUD_HANDLERS.get("dropbox")
_USER_WF_CLOUD_HANDLERS["dropbox"] = mock_dbx_handler
try:
with patch(
"app.utils.encryption.decrypt_value",
return_value='{"refresh_token": "tok", "app_key": "ak", "app_secret": "as"}',
):
result = _pull_user_integration_watch_folders()
assert result["status"] == "ok"
assert result["files_enqueued"] == 5
mock_dbx_handler.assert_called_once()
args = mock_dbx_handler.call_args
assert args[0][0]["source_type"] == "dropbox"
assert args[0][4] == "owner-dbx"
finally:
if original_handler is not None:
_USER_WF_CLOUD_HANDLERS["dropbox"] = original_handler
@patch("app.tasks.watch_folder_tasks._get_db_session")
def test_unknown_source_type_skipped(self, mock_session_factory):
"""Unknown source_type should be skipped gracefully."""
from app.tasks.watch_folder_tasks import _pull_user_integration_watch_folders
mock_integ = MagicMock()
mock_integ.id = 32
mock_integ.owner_id = "owner-unknown"
mock_integ.config = '{"source_type": "unknown_provider", "delete_after_process": false}'
mock_integ.is_active = True
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.all.return_value = [mock_integ]
mock_session_factory.return_value = mock_db
result = _pull_user_integration_watch_folders()
assert result["status"] == "ok"
assert result["files_enqueued"] == 0
@patch("app.tasks.watch_folder_tasks._get_db_session")
@patch("app.tasks.watch_folder_tasks._scan_user_watch_folder", return_value=4)
@patch("app.tasks.watch_folder_tasks._load_cache", return_value={})
@patch("app.tasks.watch_folder_tasks._save_cache")
def test_local_source_type_uses_local_scanner(self, mock_save, mock_load, mock_scan_local, mock_session_factory):
"""Explicit source_type 'local' should use the local filesystem scanner."""
from app.tasks.watch_folder_tasks import _pull_user_integration_watch_folders
mock_integ = MagicMock()
mock_integ.id = 33
mock_integ.owner_id = "owner-local"
mock_integ.config = '{"source_type": "local", "folder_path": "/data/scans", "delete_after_process": false}'
mock_integ.is_active = True
mock_db = MagicMock()
mock_db.query.return_value.filter.return_value.all.return_value = [mock_integ]
mock_session_factory.return_value = mock_db
result = _pull_user_integration_watch_folders()
assert result["status"] == "ok"
assert result["files_enqueued"] == 4
mock_scan_local.assert_called_once()
assert mock_scan_local.call_args[0][0] == "/data/scans"
assert mock_scan_local.call_args[0][3] == "owner-local"
@pytest.mark.unit
class TestScanAllWatchFoldersIncludesUserIntegrations: