60 lines
3.4 KiB
Markdown
60 lines
3.4 KiB
Markdown
# 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_response` helpers. Modified IMAP sync to save attachment content to disk + create `MailAttachment` DB records. Modified `send_mail_via_smtp` to accept `attachment_paths` and attach files to `EmailMessage`. Updated `mail_to_response` to use `attachment_to_response`.
|
|
- **routes.py**: Added `POST /api/v1/mail/upload-attachment` endpoint (multipart/form-data). Added `_resolve_attachment_paths` helper. Modified `send_mail` route to resolve attachment IDs and pass to `send_mail_via_smtp`.
|
|
|
|
### Frontend
|
|
- **api/mail.ts**: Updated `MailAttachment` interface (added `size_bytes`, `dms_file_id`). Added `uploadAttachment` function and `UploadedAttachment` interface.
|
|
- **MailDetail.tsx**: Fixed `att.size` → `att.size_bytes` for 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.addAttachment` and `common.remove` keys 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
|
|
|
|
1. **IMAP Sync**: Attachment content is now captured during IMAP sync (`part.get_payload(decode=True)`), saved to `{storage_path}/mail_attachments/{mail_id}/{filename}`, and `MailAttachment` records are created in the DB.
|
|
2. **Download**: Existing `GET /{mail_id}/attachments/{att_id}` route streams the file from disk with correct Content-Type and Content-Disposition headers.
|
|
3. **Upload**: New `POST /upload-attachment` endpoint accepts multipart/form-data, validates 25MB max size, saves to temp directory, returns attachment ID.
|
|
4. **Send with attachments**: `send_mail` route resolves attachment IDs to file paths, passes them to `send_mail_via_smtp` which adds them to the `EmailMessage` via `msg.add_attachment()`.
|
|
5. **Frontend display**: MailDetail shows attachments with icon, filename, size (using `size_bytes`), and download button.
|
|
6. **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)
|