test(T05): contact router, rental router, email service, rate limiting tests + retry queue
This commit is contained in:
+52
-77
@@ -1,89 +1,64 @@
|
||||
# Test Report – T04: Rentman Integration + Admin Auth
|
||||
# Test Report – T05: Email Service + Contact & Rental Request Backend
|
||||
|
||||
**Datum:** 2026-07-09
|
||||
**Task:** T04 (Rentman Integration: Import + Request + Admin Auth)
|
||||
**Projekt:** hms-licht-ton
|
||||
|
||||
## Test Commands Run
|
||||
## Test Commands
|
||||
|
||||
### Targeted T05 Tests
|
||||
```bash
|
||||
cd backend && python -m pytest tests/ -v --cov=app --cov-report=term-missing
|
||||
python -m pytest tests/test_contact_router.py tests/test_rental_router.py tests/test_email_service.py tests/test_rate_limiting.py -v
|
||||
```
|
||||
**Result: 23 passed in 0.81s**
|
||||
|
||||
## Results
|
||||
### Full Suite with Coverage
|
||||
```bash
|
||||
python -m pytest tests/ -v --cov=app --cov-report=term-missing
|
||||
```
|
||||
**Result: 57 passed, 4 warnings in 7.31s**
|
||||
**Coverage: 78% overall**
|
||||
|
||||
- **Total tests:** 34
|
||||
- **Passed:** 34
|
||||
- **Failed:** 0
|
||||
- **Coverage:** 71% overall
|
||||
## Test Files Created
|
||||
|
||||
## Test Breakdown
|
||||
### tests/test_contact_router.py (6 tests)
|
||||
- test_contact_valid_returns_200 – POST /api/contact valid → 200, success=True
|
||||
- test_contact_invalid_email_returns_422 – invalid email → 422
|
||||
- test_contact_privacy_consent_false_returns_422 – privacy_consent=false → 422
|
||||
- test_contact_triggers_email – mock EmailService.send_contact_email called with correct data
|
||||
- test_contact_saved_to_db – Contact record saved with email_sent=True
|
||||
- test_contact_email_failure_does_not_break_response – email failure still returns 200
|
||||
|
||||
### test_admin_auth.py (9 tests) – T04
|
||||
- test_password_hash_and_verify ✅
|
||||
- test_create_and_verify_token ✅
|
||||
- test_verify_invalid_token ✅
|
||||
- test_verify_expired_token ✅
|
||||
- test_login_success (valid creds → 200 + token) ✅
|
||||
- test_login_failure_wrong_password (→ 401) ✅
|
||||
- test_login_failure_unknown_user (→ 401) ✅
|
||||
- test_me_without_token (→ 401) ✅
|
||||
- test_me_with_valid_token (→ 200 + username) ✅
|
||||
### tests/test_rental_router.py (7 tests)
|
||||
- test_rental_valid_returns_201 – valid POST → 201 with HMS-YYYY-NNNNN reference
|
||||
- test_rental_date_end_before_start_returns_422 – date_end < date_start → 422
|
||||
- test_rental_empty_items_returns_422 – empty items array → 422
|
||||
- test_rental_saved_to_db – RentalRequest + RentalRequestItem saved, rentman_sync_status=success
|
||||
- test_rental_triggers_rentman – RentmanService.create_project_request + add_equipment_to_request called
|
||||
- test_rental_triggers_email – EmailService.send_rental_confirmation called with ref + items
|
||||
- test_rental_rentman_failure_still_returns_201 – Rentman failure still returns 201
|
||||
|
||||
### test_admin_router.py (6 tests) – T04
|
||||
- test_sync_without_token (→ 401) ✅
|
||||
- test_sync_status_without_token (→ 401) ✅
|
||||
- test_sync_log_without_token (→ 401) ✅
|
||||
- test_sync_with_valid_token (→ 200 + sync_id) ✅
|
||||
- test_sync_status_with_valid_token (→ 200 + status) ✅
|
||||
- test_sync_log_with_valid_token (→ 200 + paginated) ✅
|
||||
### tests/test_email_service.py (7 tests)
|
||||
- test_send_contact_email_success – aiosmtplib.send called, MIMEMultipart contains name + message
|
||||
- test_send_rental_confirmation_success – email contains reference_number + items
|
||||
- test_send_email_smtp_failure_returns_false – SMTP exception → returns False, no crash
|
||||
- test_send_contact_email_smtp_failure_returns_false – contact email SMTP failure → False
|
||||
- test_send_rental_confirmation_smtp_failure_returns_false – rental email SMTP failure → False
|
||||
- test_send_email_failure_saves_to_queue – failed email saved to email_queue table
|
||||
- test_retry_failed_emails_resends_pending – retry sends queued email, marks as sent
|
||||
|
||||
### test_rentman_import.py (5 tests) – T04
|
||||
- test_transform_equipment (field mapping) ✅
|
||||
- test_paginated_import (250 items, 3 pages, upsert verified) ✅
|
||||
- test_sync_upsert_existing (update not duplicate) ✅
|
||||
- test_sync_failure_logs_error (error logged in sync_log) ✅
|
||||
- test_get_all_equipment_paginates (iterates until data empty) ✅
|
||||
### tests/test_rate_limiting.py (3 tests)
|
||||
- test_contact_rate_limit_allows_5_blocks_6th – 5 requests OK, 6th → 429
|
||||
- test_rental_rate_limit_allows_5_blocks_6th – 5 requests OK, 6th → 429
|
||||
- test_contact_rate_window_reset – rate window reset allows new requests after TTL
|
||||
|
||||
### test_rentman_request.py (5 tests) – T04
|
||||
- test_build_project_request_payload (full field mapping) ✅
|
||||
- test_build_project_request_payload_no_company (fallback to contact_name) ✅
|
||||
- test_build_equipment_payload (equipment mapping with linked_equipment) ✅
|
||||
- test_projectrequest_success (mock POST returns ID) ✅
|
||||
- test_equipment_retry (3 retries with exponential backoff) ✅
|
||||
## New Feature: Email Retry Queue
|
||||
- `app/models/email_queue.py` – EmailQueue model (id, to_addr, subject, html_body, text_body, reply_to, status, attempts, created_at, updated_at)
|
||||
- `app/services/email_service.py` – send_email now accepts optional `db` param; on SMTP failure saves to queue
|
||||
- `EmailService.retry_failed_emails(db)` – static method to retry pending emails (max 5 attempts)
|
||||
- `app/main.py` – APScheduler job `run_email_retry` every 15 minutes
|
||||
- Routers updated to pass `db` to email service methods
|
||||
|
||||
### test_equipment_router.py (8 tests) – T03
|
||||
- test_list_equipment (paginated response) ✅
|
||||
- test_search_equipment (filter by name) ✅
|
||||
- test_filter_category (filter by category) ✅
|
||||
- test_sort_name_asc ✅
|
||||
- test_sort_name_desc ✅
|
||||
- test_categories (distinct categories) ✅
|
||||
- test_equipment_detail (full detail) ✅
|
||||
- test_equipment_not_found (404) ✅
|
||||
|
||||
### test_health.py (1 test) – T03
|
||||
- test_health_endpoint (status, db, redis) ✅
|
||||
|
||||
## Coverage by Module
|
||||
|
||||
| Module | Coverage |
|
||||
|--------|---------|
|
||||
| app/auth.py | 86% |
|
||||
| app/routers/admin.py | 84% |
|
||||
| app/services/sync_service.py | 82% |
|
||||
| app/services/rentman_service.py | 75% |
|
||||
| app/routers/equipment.py | 66% |
|
||||
| app/main.py | 64% |
|
||||
| app/routers/health.py | 60% |
|
||||
| app/models/* | 100% |
|
||||
| app/schemas/* | 76-100% |
|
||||
| app/config.py | 100% |
|
||||
|
||||
## Notes
|
||||
|
||||
- Rentman API fully mocked in tests (no live calls)
|
||||
- Redis cache fully mocked in tests
|
||||
- SQLite in-memory database used for tests
|
||||
- APScheduler startup tested via lifespan context
|
||||
- Rate limiting tested via mock counter
|
||||
## Smoke Test
|
||||
- All 57 tests pass (23 new T05 + 34 existing)
|
||||
- No existing tests broken
|
||||
- Coverage: 78% overall, email_service 90%, contact.py 67%, rental_requests.py 46%
|
||||
- Rate limiting works via mock cache.incr_rate with sequential return values
|
||||
- Email queue persistence verified with in-memory SQLite
|
||||
- Retry mechanism verified: pending → sent on success
|
||||
|
||||
Reference in New Issue
Block a user