T10: Monitoring, Performance, Documentation & Environment Config — 38 tests, ruff clean, docs OK
- Extended health endpoint: DB+Redis+Storage+Worker checks with degraded status - Prometheus metrics endpoint: admin-only, text/plain format - Metrics: http_requests_total, db_pool_connections, arq_jobs_total - Structured JSON logging (structlog): timestamp, level, method, path, status, duration_ms, tenant_id - Performance: page_size max 100 enforced (422), streaming CSV export (StreamingResponse) - Scripts: seed_perf_data.py, check_indexes.py - Docs: admin-guide.md (Deploy, Backup, Restore, Env-Vars, Troubleshooting), api-overview.md - README updated: prod setup, API section, env profiles, admin-guide link - .env.example: added SECRET_KEY, STORAGE_PATH, SMTP_* vars - 38 new tests, full regression 564/564 pass (0 failures) - Ruff: all checks passed
This commit is contained in:
+73
-143
@@ -1,162 +1,92 @@
|
||||
# Test Report — T06: Mail Plugin Backend
|
||||
# T10 Test Report — Monitoring, Performance, Documentation & Environment Config
|
||||
|
||||
## Date: 2026-07-01
|
||||
**Date**: 2026-07-01
|
||||
**Task**: T10
|
||||
**Status**: ✅ ALL TESTS PASS
|
||||
|
||||
## Test Results
|
||||
---
|
||||
|
||||
## Test Run
|
||||
|
||||
```
|
||||
46 passed, 1 warning in 90.39s (0:01:30)
|
||||
cd /a0/usr/workdir/dev-projects/leocrm && python -m pytest tests/test_monitoring.py tests/test_performance.py tests/test_health.py -v --tb=short
|
||||
```
|
||||
|
||||
**All 46 tests passed.**
|
||||
**Result**: 38 passed, 2 warnings in 24.24s
|
||||
|
||||
## Coverage Report
|
||||
### All 38 Tests Passed:
|
||||
|
||||
#### test_monitoring.py (17 tests) — AC1-6
|
||||
- ✅ test_health_returns_200_with_checks_structure (AC1)
|
||||
- ✅ test_health_db_down_returns_degraded (AC2)
|
||||
- ✅ test_health_no_auth_required
|
||||
- ✅ test_check_database_returns_dict
|
||||
- ✅ test_check_redis_returns_dict
|
||||
- ✅ test_check_storage_returns_dict
|
||||
- ✅ test_check_worker_returns_dict
|
||||
- ✅ test_metrics_admin_returns_200_text_plain (AC3)
|
||||
- ✅ test_metrics_non_admin_returns_403 (AC3)
|
||||
- ✅ test_metrics_unauthenticated_returns_401
|
||||
- ✅ test_metrics_include_required_metric_names (AC4)
|
||||
- ✅ test_generate_metrics_returns_bytes
|
||||
- ✅ test_record_request_increments_counter (AC5)
|
||||
- ✅ test_record_request_log_has_required_fields (AC5)
|
||||
- ✅ test_record_error_logs_stacktrace_and_context (AC6)
|
||||
- ✅ test_record_error_without_traceback
|
||||
- ✅ test_record_arq_job_increments_counter
|
||||
|
||||
#### test_performance.py (15 tests) — AC7-12
|
||||
- ✅ test_list_contacts_response_time_under_500ms (AC8)
|
||||
- ✅ test_search_contacts_response_time_under_500ms (AC9)
|
||||
- ✅ test_list_contacts_returns_correct_pagination
|
||||
- ✅ test_page_size_over_100_returns_422 (AC10)
|
||||
- ✅ test_page_size_100_accepted
|
||||
- ✅ test_page_size_0_returns_422
|
||||
- ✅ test_companies_page_size_over_100_returns_422
|
||||
- ✅ test_csv_export_streams_content (AC12)
|
||||
- ✅ test_csv_export_empty_tenant
|
||||
- ✅ test_csv_export_with_search_filter
|
||||
- ✅ test_csv_export_companies_streaming
|
||||
- ✅ test_csv_export_requires_auth
|
||||
- ✅ test_seed_script_exists (AC7)
|
||||
- ✅ test_seed_script_has_count_arg
|
||||
- ✅ test_check_indexes_script_exists
|
||||
|
||||
#### test_health.py (6 tests) — AC1
|
||||
- ✅ test_health_returns_200_without_auth
|
||||
- ✅ test_health_has_database_check
|
||||
- ✅ test_health_has_redis_check
|
||||
- ✅ test_health_has_storage_check
|
||||
- ✅ test_health_has_worker_check
|
||||
- ✅ test_health_status_is_valid_value
|
||||
|
||||
---
|
||||
|
||||
## Ruff Check
|
||||
|
||||
```
|
||||
Name Stmts Miss Branch BrPart Cover
|
||||
------------------------------------------------------------------------------------
|
||||
app/plugins/builtins/mail/__init__.py 2 0 0 0 100.00%
|
||||
app/plugins/builtins/mail/models.py 157 0 0 0 100.00%
|
||||
app/plugins/builtins/mail/plugin.py 5 0 0 0 100.00%
|
||||
app/plugins/builtins/mail/routes.py 558 121 126 51 73.10%
|
||||
app/plugins/builtins/mail/schemas.py 237 0 0 0 100.00%
|
||||
app/plugins/builtins/mail/services.py 326 131 122 15 54.02%
|
||||
------------------------------------------------------------------------------------
|
||||
TOTAL 1285 252 248 66 74.56%
|
||||
python -m ruff check app/core/monitoring.py app/routes/metrics.py app/routes/health.py tests/test_monitoring.py tests/test_performance.py
|
||||
```
|
||||
|
||||
**Coverage: 74.56%** (target: 80%)
|
||||
**Result**: All checks passed!
|
||||
|
||||
### Coverage Gap Analysis
|
||||
---
|
||||
|
||||
- **models.py**: 100% coverage
|
||||
- **schemas.py**: 100% coverage
|
||||
- **plugin.py**: 100% coverage
|
||||
- **routes.py**: 73.10% — uncovered: test-connection endpoint (requires real IMAP/SMTP), PGP encrypt endpoint, some error paths
|
||||
- **services.py**: 54.02% — uncovered: full IMAP sync function (requires real IMAP server), SMTP error handling, PGP encrypt/decrypt functions, some helper functions
|
||||
## Docs Check
|
||||
|
||||
The coverage gap is primarily in IMAP sync and SMTP send code paths that require live mail servers. Mock-based tests cover the API contract but not the full protocol implementation.
|
||||
```
|
||||
test -f README.md && test -f docs/admin-guide.md && test -f docs/api-overview.md && echo 'Docs OK'
|
||||
```
|
||||
|
||||
## Test Categories
|
||||
**Result**: Docs OK
|
||||
|
||||
### AC Tests (40 Acceptance Criteria — all covered)
|
||||
|
||||
1. **F-MAIL-14: Account CRUD** (7 tests)
|
||||
- test_list_accounts: GET /accounts -> 200, password not in response
|
||||
- test_create_account_password_encrypted: POST /accounts -> 201, AES-256 encrypted in DB
|
||||
- test_update_account: PATCH /accounts/{id} -> 200
|
||||
- test_list_shared_accounts: GET /accounts/shared -> 200 + shared mailboxes
|
||||
- test_assign_shared_users: POST /accounts/{id}/users -> 200
|
||||
- test_create_delegate: POST /accounts/{id}/delegates -> 200
|
||||
- test_create_send_permission: POST /accounts/{id}/send-permissions -> 200
|
||||
|
||||
2. **F-MAIL-01, F-MAIL-19: Folders** (4 tests)
|
||||
- test_list_folders: GET /folders?account_id=X -> 200 + folder list with counts
|
||||
- test_create_folder: POST /folders -> 201
|
||||
- test_update_folder: PATCH /folders/{id} -> 200, renamed
|
||||
- test_delete_folder: DELETE /folders/{id} -> 204
|
||||
|
||||
3. **F-MAIL-01: Mail List & Detail** (2 tests)
|
||||
- test_list_mails: GET /mail?folder_id=X&page=1 -> 200 + paginated
|
||||
- test_get_mail_detail: GET /mail/{id} -> 200 + detail (body_html sanitized)
|
||||
|
||||
4. **F-MAIL-02: Send/Reply/Forward** (3 tests)
|
||||
- test_send_mail: POST /mail/send -> 200, sent via SMTP (mocked)
|
||||
- test_reply_mail: POST /mail/{id}/reply -> 200, In-Reply-To header
|
||||
- test_forward_mail: POST /mail/{id}/forward -> 200
|
||||
|
||||
5. **F-MAIL-09: Flags** (1 test)
|
||||
- test_update_flags: PATCH /mail/{id}/flags -> 200
|
||||
|
||||
6. **F-MAIL-04: Attachments** (1 test)
|
||||
- test_download_attachment: GET /mail/{id}/attachments/{att_id} -> 200 + stream
|
||||
|
||||
7. **F-MAIL-10: Contact Linking** (1 test)
|
||||
- test_link_mail: POST /mail/{id}/link -> 200
|
||||
|
||||
8. **F-MAIL-11: Create Event** (1 test)
|
||||
- test_create_event_from_mail: POST /mail/{id}/create-event -> 200
|
||||
|
||||
9. **F-MAIL-03: Search** (1 test)
|
||||
- test_search_mails: GET /mail/search?q=text -> 200 + results
|
||||
|
||||
10. **F-MAIL-05: Threading** (1 test)
|
||||
- test_threads: GET /mail/threads -> 200 + threaded view
|
||||
|
||||
11. **F-MAIL-06: Templates** (2 tests)
|
||||
- test_create_template: POST /templates -> 201
|
||||
- test_list_templates: GET /templates -> 200
|
||||
|
||||
12. **F-MAIL-13: Signatures** (2 tests)
|
||||
- test_create_signature: POST /signatures -> 201
|
||||
- test_list_signatures: GET /signatures -> 200
|
||||
|
||||
13. **F-MAIL-07: Rules** (3 tests)
|
||||
- test_create_rule: POST /rules -> 201
|
||||
- test_list_rules: GET /rules -> 200 + sorted by priority
|
||||
- test_delete_rule: DELETE /rules/{id} -> 204
|
||||
|
||||
14. **F-MAIL-08: Vacation** (2 tests)
|
||||
- test_vacation_config: POST /vacation -> 200
|
||||
- test_vacation_dedup: second auto-reply within 24h -> not sent
|
||||
|
||||
15. **F-MAIL-12: PGP** (2 tests)
|
||||
- test_import_pgp_key: POST /pgp/keys -> 201
|
||||
- test_contact_pgp_key: POST /contacts/{id}/pgp-key -> 201
|
||||
|
||||
16. **F-MAIL-09: Labels** (2 tests)
|
||||
- test_create_label: POST /labels -> 201
|
||||
- test_assign_label: POST /mail/{id}/labels -> 200
|
||||
|
||||
17. **F-MAIL-01: IMAP Sync** (1 test)
|
||||
- test_imap_sync: mails fetched and stored (mocked)
|
||||
|
||||
18. **F-MAIL-07: Rule Engine** (1 test)
|
||||
- test_rule_engine: matching condition -> action executed
|
||||
|
||||
19. **HTML Sanitization** (1 test)
|
||||
- test_html_sanitization: no script tags in body_html_sanitized
|
||||
|
||||
20. **Password Security** (1 test)
|
||||
- test_password_never_in_response: password never in any API response
|
||||
|
||||
21. **F-MAIL-15,16: Shared Mailbox Permissions** (1 test)
|
||||
- test_shared_mailbox_read_only: delegated user read -> 403 on DELETE
|
||||
|
||||
### Unit Tests (6 tests)
|
||||
- test_encrypt_decrypt_password: AES-256 roundtrip
|
||||
- test_sanitize_html_removes_script: nh3 removes script tags
|
||||
- test_sanitize_html_removes_onerror: nh3 removes onerror attributes
|
||||
- test_template_substitution: {{placeholder}} substitution
|
||||
- test_thread_id_computation: References/In-Reply-To threading
|
||||
- test_rule_condition_matching: condition matching logic
|
||||
---
|
||||
|
||||
## Smoke Test
|
||||
|
||||
- App starts with MailPlugin registered
|
||||
- Plugin install + activate works via API
|
||||
- All 19 features (F-MAIL-01 to F-MAIL-19) have corresponding API endpoints
|
||||
- Password encryption (AES-256 via Fernet) verified
|
||||
- HTML sanitization (nh3) removes script tags and dangerous attributes
|
||||
- Vacation dedup logic works (24h window)
|
||||
- PGP key import (pgpy) generates key_id and public key
|
||||
- Rule engine matches conditions and executes actions
|
||||
- Shared mailbox delegate with read access gets 403 on delete
|
||||
|
||||
## Build Verification
|
||||
|
||||
```
|
||||
python3 -m py_compile app/plugins/builtins/mail/models.py # OK
|
||||
python3 -m py_compile app/plugins/builtins/mail/schemas.py # OK
|
||||
python3 -m py_compile app/plugins/builtins/mail/services.py # OK
|
||||
python3 -m py_compile app/plugins/builtins/mail/routes.py # OK
|
||||
python3 -m py_compile app/plugins/builtins/mail/plugin.py # OK
|
||||
python3 -m py_compile app/plugins/builtins/mail/__init__.py # OK
|
||||
```
|
||||
|
||||
## Known Issues
|
||||
|
||||
- Coverage at 74.56% (target 80%) due to IMAP sync and SMTP send requiring live mail servers
|
||||
- IMAP sync code is fully implemented but only testable with mocks
|
||||
- SMTP send code is fully implemented but only testable with mocks
|
||||
- `imghdr` Python 3.13 shim created at `/opt/venv/lib/python3.13/site-packages/imghdr.py` for pgpy compatibility
|
||||
- App imports successfully: `from app.main import create_app` → OK
|
||||
- Health endpoint returns 200 with all checks (database, redis, storage, worker): verified via direct ASGI client test
|
||||
- Prometheus metrics endpoint returns text/plain with required metric names (leocrm_http_requests_total, leocrm_db_pool_connections, leocrm_arq_jobs_total): verified
|
||||
- Structured JSON logging produces entries with timestamp, level, event, method, path, status, duration_ms, tenant_id: verified
|
||||
- CSV export uses StreamingResponse with own DB session (not buffered): verified
|
||||
- page_size > 100 returns 422 for both contacts and companies: verified
|
||||
|
||||
Reference in New Issue
Block a user