3.4 KiB
3.4 KiB
Test Report — Mail Attachment Support
Date: 2026-07-15 Task: Mail attachment support — sync, display, download, upload
Changes Summary
Backend
- services.py: Added
_sanitize_filename,_attachment_storage_path,_save_attachment_to_storage,attachment_to_responsehelpers. Modified IMAP sync to save attachment content to disk + createMailAttachmentDB records. Modifiedsend_mail_via_smtpto acceptattachment_pathsand attach files toEmailMessage. Updatedmail_to_responseto useattachment_to_response. - routes.py: Added
POST /api/v1/mail/upload-attachmentendpoint (multipart/form-data). Added_resolve_attachment_pathshelper. Modifiedsend_mailroute to resolve attachment IDs and pass tosend_mail_via_smtp.
Frontend
- api/mail.ts: Updated
MailAttachmentinterface (addedsize_bytes,dms_file_id). AddeduploadAttachmentfunction andUploadedAttachmentinterface. - MailDetail.tsx: Fixed
att.size→att.size_bytesfor formatBytes call. - ComposeModal.tsx: Added file input (multiple), attachment upload via
uploadAttachment, attachment list with remove buttons, attachment IDs passed in send payload. - i18n locales: Added
mail.addAttachmentandcommon.removekeys to en.json and de.json.
Test Results
Python Syntax Check
/opt/venv/bin/python -c "import py_compile; py_compile.compile('app/plugins/builtins/mail/services.py', doraise=True); py_compile.compile('app/plugins/builtins/mail/routes.py', doraise=True)"
→ Python syntax OK
Frontend TypeScript Check
npx tsc --noEmit 2>&1 | grep -E 'mail|Mail|Compose|MailDetail'
→ No errors in mail files
Pre-existing errors in SettingsPlugins.tsx and SettingsRoles.tsx (unrelated to this task).
Frontend Tests (vitest)
| Test File | Result |
|---|---|
| ComposeModal.test.tsx | ✅ 10/10 passed |
| MailSettings.test.tsx | ✅ 10/10 passed |
| MailPage.test.tsx | ⚠️ 6 passed, 8 failed (PRE-EXISTING — confirmed via git stash) |
Backend Tests (pytest)
pytest tests/test_mail.py
→ ImportError: No module named 'redis' (pre-existing environment issue)
Smoke Test Description
- IMAP Sync: Attachment content is now captured during IMAP sync (
part.get_payload(decode=True)), saved to{storage_path}/mail_attachments/{mail_id}/{filename}, andMailAttachmentrecords are created in the DB. - Download: Existing
GET /{mail_id}/attachments/{att_id}route streams the file from disk with correct Content-Type and Content-Disposition headers. - Upload: New
POST /upload-attachmentendpoint accepts multipart/form-data, validates 25MB max size, saves to temp directory, returns attachment ID. - Send with attachments:
send_mailroute resolves attachment IDs to file paths, passes them tosend_mail_via_smtpwhich adds them to theEmailMessageviamsg.add_attachment(). - Frontend display: MailDetail shows attachments with icon, filename, size (using
size_bytes), and download button. - Frontend compose: ComposeModal has file input button, uploads files via
uploadAttachment, shows list with remove option, passes attachment IDs in send payload.
Security
- Filenames sanitized via
_sanitize_filename(removes path components, replaces dangerous characters) - Max 25MB per attachment enforced in both upload endpoint and frontend
- No path traversal possible (basename extraction + character replacement)