Postgres real en contenedor. Sin solutions.md hasta entregar.
Ejercicio 1 — El entorno de test
- Levanta el
docker-compose.test.yml(Postgres 16 en 5433 + Redis en 6380) y corre la suite de integración conDATABASE_URLapuntando ahí. Pega el comando completo (env var + pytest). - Miente a propósito: corre el test del índice parcial del outbox (25) contra SQLite (
override_settingso BD de test SQLite). ¿Qué pasa y por qué? Documenta la mentira concretamente (¿el test pasa y miente, o falla honesto?). - Añade el check de versión al conftest: si la BD de test no es Postgres 16,
pytest.failal arrancar con mensaje claro (la 27: fail-fast).
Ejercicio 2 — Un asiento, un ganador
- Implementa el test de concurrencia (ThreadPoolExecutor +
transaction=True) con N=2, y luego N=10 threads sobre el MISMO asiento: exactamente 1 ganador, 9SeatUnavailable. - Mide: ¿cuánto tarda el test con N=10? Anota el número como línea base (la 36 lo comparará bajo carga).
- La variante con
nowait(10): misma prueba conselect_for_update(nowait=True)— los perdedores fallan INMEDIATAMENTE con LockNotAvailable. ¿Qué UX de checkout prefieres y por qué? (2 líneas: espera-block vs rechazo-reintento).
Ejercicio 3 — Las costuras del outbox y del ledger
- El test de atomicidad del outbox con TX REAL: fuerza el fallo en la mitad de la transacción de
reservar()(trigger/constraint que explote) y verifica cero eventos publicados. Sin mocks de transaction: la BD decide. - El test de la constraint del ledger (31): inserta duplicado
(org, mes)y verificaIntegrityError— y que el código deliquidar_meslo convierte en skip, no en crash (elget_or_create). - El test de la saga reanudable (32): crea saga PENDING con intent_id, "mata" el proceso (simula con excepción no capturada), reanuda, y verifica UN solo cargo en la pasarela fake con Idempotency-Key.
Ejercicio 4 — Aislamiento
- Rompe el aislamiento a propósito: un test que escribe en Redis DB 0 (la del dev) y otro que depende de esa clave. Corre solo el segundo: ¿rojo? Corre la suite completa: ¿verde por orden? Documenta el pestilencia y arréglalo (prefijo + DB 15).
- Corre la suite de integración con
pytest -p no:randomlyy luego con randomización (pytest-randomly): ¿algún test dependiente afloró? El test que solo pasa en orden: arréglalo o elícitalo. - La regla final del conftest:
autousefixture que verifica que NINGÚN test escribe en la BD de desarrollo (la URL debe contenertf_test). ¿Cómo lo implementarías con un guard en el fixture?
Ejercicio 5 — La tabla de garantías
- Escribe la tabla de garantías (§5) para TU proyecto con el estado real: cada fila con su test (o "falta"). ¿Cuántas garantías vendidas al negocio no tienen póliza?
- Implementa la que falta más crítica (elige: advisory lock, UniqueConstraint del ledger, o saga reanudable) y pégala.
- Corre la suite completa y cronometra por capa: unitarias X s, integración Y s, e2e Z s. ¿Cumple el presupuesto (10s / 2min / 10min)? Si no: ¿qué test está en la capa equivocada?
Entrega
Pega el compose de test, el test N=10 con sus ms, el test de atomicidad del outbox y la tabla de garantías con huecos. Después: Lección 35 — Pruebas E2E y de contrato.