Merge pull request #446 from christianlouis/copilot/optimize-test-timeouts
fix(ci): split test pipeline into quick/integration stages, fix hanging test
This commit is contained in:
@@ -361,12 +361,28 @@ docker volume prune -f
|
||||
|
||||
### GitHub Actions - Current Configuration
|
||||
|
||||
The DocuElevate CI workflow (`.github/workflows/tests.yaml`) **excludes E2E tests** by default because they require Docker-in-Docker (testcontainers), which requires additional configuration in GitHub Actions.
|
||||
The DocuElevate CI pipeline (`.github/workflows/ci.yml`) uses a **two-stage test strategy** for fast feedback:
|
||||
|
||||
1. **Quick Tests** (`test-quick`) — Runs ~2,790 unit and basic integration tests in ~2 minutes. Excludes tests marked `e2e`, `requires_docker`, `requires_external`, or `slow`. This stage gates the integration tests.
|
||||
|
||||
2. **Integration Tests** (`test-integration`) — Runs ~28 Docker-based and external service tests. Only starts after quick tests pass. Uses testcontainers for WebDAV, OAuth, etc.
|
||||
|
||||
Both stages use `pytest-timeout` to prevent individual tests from hanging:
|
||||
- Quick tests: 120 seconds per test, 15-minute job timeout
|
||||
- Integration tests: 300 seconds per test, 20-minute job timeout
|
||||
|
||||
```yaml
|
||||
- name: Run Tests
|
||||
# Exclude E2E tests - they require Docker-in-Docker (testcontainers)
|
||||
run: pytest tests/ -v --cov=app -m "not e2e"
|
||||
# Quick tests (Stage 2b)
|
||||
- name: Run Quick Tests
|
||||
run: >
|
||||
pytest tests/ -v --timeout=120
|
||||
-m "not e2e and not requires_docker and not requires_external and not slow"
|
||||
|
||||
# Integration tests (Stage 2c) — only after quick tests pass
|
||||
- name: Run Integration Tests
|
||||
run: >
|
||||
pytest tests/ -v --timeout=300
|
||||
-m "(requires_docker or requires_external or slow) and not e2e"
|
||||
```
|
||||
|
||||
**To run E2E tests locally:**
|
||||
|
||||
@@ -580,10 +580,36 @@ class TestSendToAllDestinations:
|
||||
# Error should be recorded in results
|
||||
assert "dropbox_error" in result.result["tasks"]
|
||||
|
||||
@patch("app.tasks.send_to_all._should_upload_to_s3")
|
||||
@patch("app.tasks.send_to_all._should_upload_to_onedrive")
|
||||
@patch("app.tasks.send_to_all._should_upload_to_email")
|
||||
@patch("app.tasks.send_to_all._should_upload_to_sftp")
|
||||
@patch("app.tasks.send_to_all._should_upload_to_ftp")
|
||||
@patch("app.tasks.send_to_all._should_upload_to_webdav")
|
||||
@patch("app.tasks.send_to_all._should_upload_to_google_drive")
|
||||
@patch("app.tasks.send_to_all._should_upload_to_paperless")
|
||||
@patch("app.tasks.send_to_all._should_upload_to_nextcloud")
|
||||
@patch("app.tasks.send_to_all._should_upload_to_dropbox")
|
||||
@patch("app.tasks.send_to_all.log_task_progress")
|
||||
@patch("app.tasks.send_to_all.SessionLocal")
|
||||
@patch("app.tasks.send_to_all.settings")
|
||||
def test_fallback_file_id_lookup(self, mock_settings, mock_session_local, mock_log, tmp_path):
|
||||
def test_fallback_file_id_lookup(
|
||||
self,
|
||||
mock_settings,
|
||||
mock_session_local,
|
||||
mock_log,
|
||||
mock_dropbox,
|
||||
mock_nextcloud,
|
||||
mock_paperless,
|
||||
mock_google,
|
||||
mock_webdav,
|
||||
mock_ftp,
|
||||
mock_sftp,
|
||||
mock_email,
|
||||
mock_onedrive,
|
||||
mock_s3,
|
||||
tmp_path,
|
||||
):
|
||||
"""Test file_id lookup fallback when not provided."""
|
||||
from app.models import FileRecord
|
||||
|
||||
@@ -592,6 +618,18 @@ class TestSendToAllDestinations:
|
||||
|
||||
mock_settings.workdir = str(tmp_path)
|
||||
|
||||
# Disable all upload services — this test only checks the DB lookup fallback
|
||||
mock_dropbox.return_value = False
|
||||
mock_nextcloud.return_value = False
|
||||
mock_paperless.return_value = False
|
||||
mock_google.return_value = False
|
||||
mock_webdav.return_value = False
|
||||
mock_ftp.return_value = False
|
||||
mock_sftp.return_value = False
|
||||
mock_email.return_value = False
|
||||
mock_onedrive.return_value = False
|
||||
mock_s3.return_value = False
|
||||
|
||||
# Mock database session
|
||||
mock_db = MagicMock()
|
||||
mock_query = MagicMock()
|
||||
|
||||
Reference in New Issue
Block a user