Introduces a Redis-backed sliding-window rate limiter for upload
endpoints (/api/ui-upload, /api/process-url) that:
- Enforces per-user limits (default: 20 uploads / 60 s)
- Dynamically reduces limits under system stress (queue depth, CPU load)
- Returns 429 with Retry-After header when exceeded
- Fails open when Redis is unavailable
- Works with the existing client-side adaptive back-off
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
SQLite engines now use NullPool instead of QueuePool, eliminating the
"QueuePool limit of size 5 overflow 10 reached" TimeoutError under
concurrent load. PostgreSQL/MySQL engines use a configurable QueuePool
with sensible defaults (pool_size=10, max_overflow=20) exposed via
DB_POOL_SIZE, DB_MAX_OVERFLOW, DB_POOL_TIMEOUT, DB_POOL_RECYCLE env
vars. pool_pre_ping is enabled on all backends.
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
- Add path traversal guard in reimport file copy loop
- Improve error log message context for table wipe failures
- Use conditional role=alert/status on result banner for accessibility
- Make test assertions more specific (exact status codes)
- Rename ambiguous view test
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
- 21 tests covering unit, integration, API, and view layers
- Update ConfigurationGuide.md with System Reset section
- Update API.md with system reset endpoint docs
- All tests pass, ruff clean
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
The per-user notification functions (notify_user_document_processed /
notify_user_document_failed) were defined but never called from the
document processing pipeline.
- Call notify_user_document_processed in finalize_document_storage
when owner_id is available (creates in-app + email/webhook notifications)
- Add _dispatch_user_failure_notification helper to celery_app.py that
extracts file_id from failed task args and dispatches
notify_user_document_failed for document pipeline tasks
- Add comprehensive tests for both success and failure notification paths
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
The tests were patching `app.api.url_upload.requests.get` but the module
uses `httpx.AsyncClient`. Updated 4 tests across 2 files to use the
correct `httpx.AsyncClient.stream` mock pattern with `AsyncMock`,
matching the existing working tests in test_url_upload.py.
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
get_current_owner_id() only checked the session for authenticated users.
When the mobile app sends Authorization: Bearer <token>, there is no
session cookie, so the Depends(_get_owner_id) dependency raised HTTP 401
before the @require_login wrapper could resolve the Bearer token.
The function now checks three sources in order:
1. Session user dict (existing behavior)
2. request.state.api_token_user (cached by require_login or prior call)
3. Direct Bearer token resolution via _resolve_bearer_user (new)
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Added a new test file `tests/test_api_saved_searches.py` containing a comprehensive test suite for the CRUD operations provided in `app/api/saved_searches.py`. The suite validates happy paths, error conditions (like missing filters, name limits, duplicates), and user isolation using an in-memory SQLite database.
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Replaces the synchronous `requests.post` and `requests.get` calls in
`app/api/onedrive.py:test_onedrive_token` with an asynchronous
`httpx.AsyncClient` implementation. This unblocks the FastAPI event loop
when this endpoint is hit.
Fixed unused requests import in `app/api/onedrive.py` and sorted imports in the testing files updated previously to adhere to the repository formatting (`ruff check --fix`). Tests in `test_api_onedrive_extended.py` were also migrated to use AsyncMock properly.
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
- Add LOG_FORMAT setting (text/json) for structured JSON log output
- Add LOG_SYSLOG_* settings for direct syslog forwarding of app logs
- JSON format compatible with Grafana Loki, Splunk, ELK, Datadog
- Syslog forwarding uses Python's SysLogHandler (UDP/TCP)
- Update .env.demo and ConfigurationGuide.md with all new settings
- Add tests for JSON formatter and syslog config fields
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Replaced synchronous `requests.get` and `open().write` in the `process_url` endpoint with `httpx.AsyncClient` and `aiofiles.open`. This prevents the FastAPI event loop from blocking during large file downloads.
Updated test suite in `tests/test_url_upload.py` to use `AsyncMock` to mock `httpx.AsyncClient.stream` contexts and async generators properly, covering all original conditions and HTTP error handling paths.
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit improves test coverage for the `register_settings_reload_signal` function in `app/utils/settings_sync.py`.
🎯 **What:** The testing gap addressed was that the `_reload_if_stale` inner Celery `task_prerun` signal handler was entirely untested, specifically around exception handling (e.g. Redis timeouts or OCR manager errors) and the code branch where Redis returns no version key.
📊 **Coverage:** The following scenarios are now tested:
- Redis returning `None` for the version.
- Redis throwing an exception (handled gracefully).
- `ensure_ocr_languages_async` throwing an exception (caught and logged without failing the task).
✨ **Result:** Test coverage for `register_settings_reload_signal` is now 100%. Total coverage for `app/utils/settings_sync.py` has been substantially improved.
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>