imap-migration-tools/test/test_compare_imap_folders.py
Javier Callico caf83d60c5 Add comprehensive tests for IMAP email counting, migration, and common functionalities
- Implement tests for counting emails in single and multiple folders, including handling of empty folders and error scenarios in `test_count_imap_emails.py`.
- Create tests for environment variable verification, IMAP connection handling, folder normalization, MIME header decoding, and message details extraction in `test_imap_common.py`.
- Develop tests for email migration, including duplicate detection, deletion from source after migration, folder creation on destination, and handling of multiple folders in `test_migrate_imap_emails.py`.
- Introduce a mock IMAP server to facilitate testing of IMAP functionalities in `tools/mock_imap_server.py`.
2026-01-25 11:59:34 -05:00

241 lines
8.0 KiB
Python

"""
Tests for compare_imap_folders.py
Tests cover:
- Basic folder comparison between accounts
- Matching counts
- Mismatched counts
- Missing folders on destination
- Empty folder handling
- Configuration validation
"""
import imaplib
import os
import sys
import pytest
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../src")))
import compare_imap_folders
from conftest import make_mock_connection
class TestFolderComparison:
"""Tests for folder comparison functionality."""
def test_matching_counts(self, mock_server_factory, monkeypatch, capsys):
"""Test comparison when source and destination have matching counts."""
data = {
"INBOX": [b"Subject: 1\r\n\r\nB", b"Subject: 2\r\n\r\nB"],
"Sent": [b"Subject: 3\r\n\r\nB"],
}
src_server, dest_server, p1, p2 = mock_server_factory(data, data.copy())
env = {
"SRC_IMAP_HOST": "localhost",
"SRC_IMAP_USERNAME": "src_user",
"SRC_IMAP_PASSWORD": "p",
"DEST_IMAP_HOST": "localhost",
"DEST_IMAP_USERNAME": "dest_user",
"DEST_IMAP_PASSWORD": "p",
}
monkeypatch.setattr(os, "environ", env)
monkeypatch.setattr(sys, "argv", ["compare_imap_folders.py"])
monkeypatch.setattr("imap_common.get_imap_connection", make_mock_connection(p1, p2))
compare_imap_folders.main()
captured = capsys.readouterr()
assert "INBOX" in captured.out
assert "Sent" in captured.out
def test_mismatched_counts(self, mock_server_factory, monkeypatch, capsys):
"""Test comparison when counts differ."""
src_data = {
"INBOX": [b"Subject: 1\r\n\r\nB", b"Subject: 2\r\n\r\nB", b"Subject: 3\r\n\r\nB"],
}
dest_data = {
"INBOX": [b"Subject: 1\r\n\r\nB"],
}
src_server, dest_server, p1, p2 = mock_server_factory(src_data, dest_data)
env = {
"SRC_IMAP_HOST": "localhost",
"SRC_IMAP_USERNAME": "src_user",
"SRC_IMAP_PASSWORD": "p",
"DEST_IMAP_HOST": "localhost",
"DEST_IMAP_USERNAME": "dest_user",
"DEST_IMAP_PASSWORD": "p",
}
monkeypatch.setattr(os, "environ", env)
monkeypatch.setattr(sys, "argv", ["compare_imap_folders.py"])
monkeypatch.setattr("imap_common.get_imap_connection", make_mock_connection(p1, p2))
compare_imap_folders.main()
captured = capsys.readouterr()
# Source has 3, dest has 1
assert "3" in captured.out
assert "1" in captured.out
def test_folder_missing_on_destination(self, mock_server_factory, monkeypatch, capsys):
"""Test when a folder exists on source but not destination."""
src_data = {
"INBOX": [b"Subject: 1\r\n\r\nB"],
"Archive": [b"Subject: 2\r\n\r\nB"],
}
dest_data = {
"INBOX": [b"Subject: 1\r\n\r\nB"],
}
src_server, dest_server, p1, p2 = mock_server_factory(src_data, dest_data)
env = {
"SRC_IMAP_HOST": "localhost",
"SRC_IMAP_USERNAME": "src_user",
"SRC_IMAP_PASSWORD": "p",
"DEST_IMAP_HOST": "localhost",
"DEST_IMAP_USERNAME": "dest_user",
"DEST_IMAP_PASSWORD": "p",
}
monkeypatch.setattr(os, "environ", env)
monkeypatch.setattr(sys, "argv", ["compare_imap_folders.py"])
monkeypatch.setattr("imap_common.get_imap_connection", make_mock_connection(p1, p2))
compare_imap_folders.main()
captured = capsys.readouterr()
# Archive should show N/A for destination
assert "Archive" in captured.out
assert "N/A" in captured.out
class TestEmptyFolders:
"""Tests for empty folder handling."""
def test_empty_folders(self, mock_server_factory, monkeypatch, capsys):
"""Test comparison with empty folders."""
src_data = {"INBOX": [], "Empty": []}
dest_data = {"INBOX": [], "Empty": []}
src_server, dest_server, p1, p2 = mock_server_factory(src_data, dest_data)
env = {
"SRC_IMAP_HOST": "localhost",
"SRC_IMAP_USERNAME": "src_user",
"SRC_IMAP_PASSWORD": "p",
"DEST_IMAP_HOST": "localhost",
"DEST_IMAP_USERNAME": "dest_user",
"DEST_IMAP_PASSWORD": "p",
}
monkeypatch.setattr(os, "environ", env)
monkeypatch.setattr(sys, "argv", ["compare_imap_folders.py"])
monkeypatch.setattr("imap_common.get_imap_connection", make_mock_connection(p1, p2))
compare_imap_folders.main()
captured = capsys.readouterr()
assert "INBOX" in captured.out
assert "0" in captured.out
class TestGetEmailCount:
"""Tests for get_email_count function."""
def test_successful_count(self, single_mock_server):
"""Test successful email count."""
data = {"INBOX": [b"Subject: 1\r\n\r\nB", b"Subject: 2\r\n\r\nB"]}
server, port = single_mock_server(data)
conn = imaplib.IMAP4("localhost", port)
conn.login("user", "pass")
result = compare_imap_folders.get_email_count(conn, "INBOX")
assert result == 2
conn.logout()
def test_nonexistent_folder(self, single_mock_server):
"""Test count for non-existent folder returns None."""
data = {"INBOX": []}
server, port = single_mock_server(data)
conn = imaplib.IMAP4("localhost", port)
conn.login("user", "pass")
result = compare_imap_folders.get_email_count(conn, "NonExistent")
assert result is None
conn.logout()
class TestConfigValidation:
"""Tests for configuration validation."""
def test_missing_source_credentials(self, monkeypatch, capsys):
"""Test that missing source credentials cause exit."""
env = {
"DEST_IMAP_HOST": "localhost",
"DEST_IMAP_USERNAME": "dest",
"DEST_IMAP_PASSWORD": "p",
}
monkeypatch.setattr(os, "environ", env)
monkeypatch.setattr(sys, "argv", ["compare_imap_folders.py"])
with pytest.raises(SystemExit) as exc_info:
compare_imap_folders.main()
assert exc_info.value.code == 1
def test_missing_dest_credentials(self, monkeypatch, capsys):
"""Test that missing destination credentials cause exit."""
env = {
"SRC_IMAP_HOST": "localhost",
"SRC_IMAP_USERNAME": "src",
"SRC_IMAP_PASSWORD": "p",
}
monkeypatch.setattr(os, "environ", env)
monkeypatch.setattr(sys, "argv", ["compare_imap_folders.py"])
with pytest.raises(SystemExit) as exc_info:
compare_imap_folders.main()
assert exc_info.value.code == 1
class TestTotals:
"""Tests for total calculation."""
def test_total_calculation(self, mock_server_factory, monkeypatch, capsys):
"""Test that totals are calculated correctly."""
src_data = {
"INBOX": [b"Subject: 1\r\n\r\nB", b"Subject: 2\r\n\r\nB"],
"Sent": [b"Subject: 3\r\n\r\nB", b"Subject: 4\r\n\r\nB", b"Subject: 5\r\n\r\nB"],
}
dest_data = {
"INBOX": [b"Subject: 1\r\n\r\nB"],
"Sent": [b"Subject: 3\r\n\r\nB"],
}
src_server, dest_server, p1, p2 = mock_server_factory(src_data, dest_data)
env = {
"SRC_IMAP_HOST": "localhost",
"SRC_IMAP_USERNAME": "src_user",
"SRC_IMAP_PASSWORD": "p",
"DEST_IMAP_HOST": "localhost",
"DEST_IMAP_USERNAME": "dest_user",
"DEST_IMAP_PASSWORD": "p",
}
monkeypatch.setattr(os, "environ", env)
monkeypatch.setattr(sys, "argv", ["compare_imap_folders.py"])
monkeypatch.setattr("imap_common.get_imap_connection", make_mock_connection(p1, p2))
compare_imap_folders.main()
captured = capsys.readouterr()
# Total source: 5, Total dest: 2, Diff: 3
assert "TOTAL" in captured.out
assert "5" in captured.out
assert "2" in captured.out