Add IMAP to Nextcloud Calendar importer

- IMAP mailbox monitoring with SSL/TLS, STARTTLS and plain connection support
- ICS attachment extraction from emails and import to Nextcloud via CalDAV
- Create or update calendar events by UID to avoid duplicates
- Event filtering by summary (contains, regex or exact match, with inversion)
- Deduplication by regex pattern extracted from event summaries
- Move or mark processed emails as seen after import
- Continuous polling mode and one-time run mode
- Fully configurable via environment variables
- Docker and Docker Compose support
- Test suite with pytest
This commit is contained in:
2026-01-06 21:50:45 +00:00
commit c87a99f570
10 changed files with 1695 additions and 0 deletions

96
tests/conftest.py Normal file
View File

@@ -0,0 +1,96 @@
"""Pytest configuration and fixtures for imap2caldav tests."""
import pytest
import sys
from pathlib import Path
# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))
@pytest.fixture
def default_config():
"""Return a default configuration dictionary for testing."""
return {
'IMAP_SERVER': 'imap.test.com',
'IMAP_PORT': 993,
'IMAP_EMAIL': 'test@test.com',
'IMAP_PASSWORD': 'testpass',
'IMAP_MAILBOX': 'INBOX',
'IMAP_PROCESSED_FOLDER': 'Processed',
'IMAP_USE_SSL': True,
'IMAP_USE_STARTTLS': False,
'IMAP_VERIFY_SSL': True,
'CALDAV_URL': 'https://caldav.test.com/dav',
'CALDAV_USERNAME': 'testuser',
'CALDAV_PASSWORD': 'testpass',
'CALENDAR_NAME': 'personal',
'CALDAV_VERIFY_SSL': True,
'MARK_AS_SEEN': True,
'MOVE_PROCESSED': False,
'CHECK_INTERVAL': 300,
'RUN_ONCE': True,
'SUMMARY_FILTER': '',
'SUMMARY_FILTER_MODE': 'contains',
'INVERT_FILTER': False,
'DEDUPE_ENABLED': False,
'DEDUPE_BY_SUMMARY_PATTERN': '',
'LOG_LEVEL': 'WARNING',
}
@pytest.fixture
def sample_ics():
"""Return a sample ICS calendar event."""
return """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Test//Test//EN
BEGIN:VEVENT
UID:test-event-123@test.com
DTSTART:20240115T100000Z
DTEND:20240115T110000Z
SUMMARY:Test Meeting
DESCRIPTION:This is a test event
END:VEVENT
END:VCALENDAR"""
@pytest.fixture
def sample_ics_no_summary():
"""Return a sample ICS calendar event without a summary."""
return """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Test//Test//EN
BEGIN:VEVENT
UID:no-summary-456@test.com
DTSTART:20240115T100000Z
DTEND:20240115T110000Z
END:VEVENT
END:VCALENDAR"""
@pytest.fixture
def sample_html_with_product_table():
"""Return sample HTML email body with a product table."""
return """<!DOCTYPE html>
<html>
<body>
<p>Your order details:</p>
<table id="productTable">
<tr><th>Product</th><th>Quantity</th><th>Price</th></tr>
<tr><td>Widget A</td><td>2</td><td>$10.00</td></tr>
<tr><td>Widget B</td><td>5</td><td>$25.00</td></tr>
</table>
</body>
</html>"""
@pytest.fixture
def sample_html_without_product_table():
"""Return sample HTML email body without a product table."""
return """<!DOCTYPE html>
<html>
<body>
<p>This is a regular email without a product table.</p>
</body>
</html>"""