Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,9 @@ jobs:
name: E2E Tests
runs-on: ubuntu-latest
needs: [unit-tests, integration-tests]
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop')
if: >-
(github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'))
|| github.event_name == 'pull_request'
services:
mongodb:
image: mongo:8.0
Expand Down Expand Up @@ -201,19 +203,21 @@ jobs:
test-summary:
name: All Tests Passed
runs-on: ubuntu-latest
needs: [unit-tests, integration-tests]
needs: [unit-tests, integration-tests, e2e-tests]
if: always()

steps:
- name: Check test results
run: |
if [[ "${{ needs.unit-tests.result }}" == "success" && "${{ needs.integration-tests.result }}" == "success" ]]; then
E2E="${{ needs.e2e-tests.result }}"
if [[ "${{ needs.unit-tests.result }}" == "success" && "${{ needs.integration-tests.result }}" == "success" && ("$E2E" == "success" || "$E2E" == "skipped") ]]; then
echo "✅ All tests passed!"
exit 0
else
echo "❌ Some tests failed"
echo "Unit tests: ${{ needs.unit-tests.result }}"
echo "Integration tests: ${{ needs.integration-tests.result }}"
echo "E2E tests: $E2E"
exit 1
fi

Expand All @@ -226,3 +230,6 @@ jobs:
echo "|-------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Unit Tests | ${{ needs.unit-tests.result == 'success' && '✅' || '❌' }} ${{ needs.unit-tests.result }} |" >> $GITHUB_STEP_SUMMARY
echo "| Integration Tests | ${{ needs.integration-tests.result == 'success' && '✅' || '❌' }} ${{ needs.integration-tests.result }} |" >> $GITHUB_STEP_SUMMARY
E2E_RESULT="${{ needs.e2e-tests.result }}"
if [[ "$E2E_RESULT" == "success" ]]; then E2E_ICON="✅"; elif [[ "$E2E_RESULT" == "skipped" ]]; then E2E_ICON="⏭️"; else E2E_ICON="❌"; fi
echo "| E2E Tests | $E2E_ICON $E2E_RESULT |" >> $GITHUB_STEP_SUMMARY
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ help:
@echo " make test-cov - Tests con coverage report HTML"
@echo " make test-unit - Tests solo unitarios"
@echo " make test-integration - Tests solo de integración"
@echo " make test-e2e - Tests E2E (requiere MongoDB)"
@echo " make test-mark - Tests con marcador específico (ej: make test-mark MARK=slow)"
@echo " make coverage-report - Ver reporte de coverage"
@echo " make seed - Inicializar base de datos con datos de prueba"
Expand Down Expand Up @@ -77,6 +78,10 @@ test-unit:
test-integration:
cd deployments && docker compose exec backend pytest tests/integration -v

# Tests solo E2E (requiere MongoDB)
test-e2e:
cd deployments && docker compose exec backend pytest tests/e2e -v

# Tests con marcador específico
test-mark:
ifndef MARK
Expand Down
69 changes: 69 additions & 0 deletions tests/e2e/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""
Fixtures for E2E tests.

E2E tests exercise the FULL stack without mocks:
API → Use Cases → Repositories → MongoDB (real)

Requirements:
- MONGODB_URL environment variable must be set
- A running MongoDB instance (Docker or CI service)

Unlike integration tests, NO dependency_overrides are applied here.
Every request goes through the real DI chain.
"""

from collections.abc import AsyncGenerator
import os

from httpx import AsyncClient
import pytest
import pytest_asyncio

pytestmark = pytest.mark.e2e

# =====================================================================
# SKIP E2E IF MONGODB IS NOT AVAILABLE
# =====================================================================


def pytest_collection_modifyitems(items):
"""Auto-skip all E2E tests when MongoDB is not available."""
if os.getenv("MONGODB_URL"):
return
skip_marker = pytest.mark.skip(
reason="MONGODB_URL not set — E2E tests require a running MongoDB instance"
)
for item in items:
item.add_marker(skip_marker)


# =====================================================================
# DATABASE CLEANUP — USES THE APP'S OWN DB REFERENCE
# =====================================================================


async def _drop_all_collections(db) -> None:
"""Drop every collection in the database."""
for name in await db.list_collection_names():
await db.drop_collection(name)


@pytest_asyncio.fixture(autouse=True)
async def _ensure_clean_db(client: AsyncClient) -> AsyncGenerator[None, None]:
"""
Ensure a completely clean database for every E2E test.

Depends on ``client`` so it runs AFTER MongoDBClient.connect() has
been called by the global client fixture. Uses the app's own DB
reference (MongoDBClient.db) to guarantee we clean the exact same
database the app writes to.
"""
from app.infrastructure.database.mongo_client import MongoDBClient

if MongoDBClient.db is not None:
await _drop_all_collections(MongoDBClient.db)

yield

if MongoDBClient.db is not None:
await _drop_all_collections(MongoDBClient.db)
Loading
Loading