Backend test strategy (Anexo24)
Test suites
tests/e2e/: full business flow (catalogs -> import -> export -> balances/discharges).tests/integration/: process API behavior and edge cases.tests/unit/: critical algorithms only (no trivial CRUD tests).
Vista del flujo de tests (backend)
Dónde vive todo
| Capa | Ruta | Rol |
|---|---|---|
| Infra | conftest.py | DB en transacción + rollback, Celery eager, app mínima con router de process, overrides de DB/usuario, tareas import/export inline en la misma sesión |
| Datos | fixtures/builders.py | Catálogos, tenant/company, facturas import/export con líneas enlazadas |
| E2E | e2e/test_inventory_flow_anexo24.py | Flujo negocio completo import → export → saldos/descargas |
| Integración | integration/test_import_process_api.py, integration/test_export_process_api.py | API HTTP + reglas por tipo de factura, saldo, doble proceso |
| Unit | unit/invoices/test_balance_algorithm.py, unit/invoices/test_currency_conversion_imports.py | Algoritmos puros / subprocesos sin HTTP |
Infraestructura (flujo común)
flowchart TB
subgraph fixtures [Fixtures conftest]
db[db_session: conexion + begin + Session]
roll[teardown: rollback + close]
celery[celery_eager autouse: memory broker]
app[app: FastAPI + process router]
client[TestClient]
end
db --> roll
celery --> app
db --> app
app --> client
subgraph inline [Monkeypatch rutas]
imp[process_invoice_task -> main_process import en db_session]
exp[process_export_invoice_task -> main_process export en db_session]
end
app --> inline
- BD:
TEST_DATABASE_URLoCORE_DATABASE_URLosettings.core_database_url. Cada test corre dentro de una transacción; al terminar no persiste nada (rollback). - Celery: sin Redis;
task_always_eagerpara que el endpoint dispare lógica síncrona. - Seguridad en tests:
validate_access_to_resourcese reemplaza por un stub que devuelvetenant_iddel usuario fake (alineado con el fixturetest_tenant).
Builders: qué se construye antes de llamar a la API
Orden típico en casi todos los tests que tocan proceso:
ensure_reference_data: tipos de factura (TEM,DEF,MEX,DONAC) y régimenA1si no existen.- Fixture
test_tenant: asigna IDs efímeros altos y crea tenant + company; elappusa esetenant_iden el usuario fake. create_business_catalogs: proveedor/cliente/destinatario (con dirección idempotente vía_ensure_client_provider_address), agente aduanal, UoM, part number FA, tipo cambio, etc.create_import_invoice_with_line/create_export_invoice_with_linesegún el caso.
Parches al pipeline (import / export)
Los tests no ejecutan validadores pesados del proceso real; sustituyen funciones en main_process para aislar el comportamiento de saldos y API:
Import (varios tests): pre_validators devuelve la línea creada; se anulan reviews de clase, tipo de cambio, pesos, SISIMP, _validate_lines.
Export: análogo con pre_validators + reviews de clase, TC, asignación, cantidad vs peso, costo, series.
Caso especial — test_process_endpoint_prevents_double_processing_import en integration/test_export_process_api.py: usa _patch_import_pipeline_without_prevalidators (sin parchear pre_validators) para que el segundo POST /process recorra el flujo real y dispare la validación de “ya procesado”.
Mapa por archivo y edge cases
E2E — e2e/test_inventory_flow_anexo24.py
- Flujo: datos ref + tenant/company + catálogos → factura import
TEMqty 10 →POST .../process→ compruebaBalanceMovementtipoENTRY→ factura export ligada a import/línea qty 4 →POST process→ compruebaCONSUMPTION,DischargeHeader,DischargeDetailconmovement_id. - Edge implícito: saldo neto por línea de import ≥ 0 y < 10 tras consumo parcial (no agota todo el inventario del escenario).
Integración import — integration/test_import_process_api.py
| Test | Qué valida |
|---|---|
test_process_import_endpoint_creates_balance_entries |
TEM + process → al menos un ENTRY por factura/línea |
test_process_import_def_does_not_create_balance_entries |
DEF + process → ningún movimiento de saldo (edge: tipo operación no genera entradas de balance) |
Integración export + doble proceso — integration/test_export_process_api.py
| Test | Qué valida |
|---|---|
_prepare_inventory + test_process_export_endpoint_consumes_existing_balances |
Tras import TEM qty 10, export qty 3 → existen CONSUMPTION |
test_process_export_prevents_negative_balance |
Export qty 99 vs saldo 10 → ValidationException y cero consumos para esa factura export |
test_process_endpoint_prevents_double_processing_import |
Segundo POST misma factura import → ValidationException; exactamente un ENTRY (no duplicar PEPS) |
Unit saldos / FIFO — unit/invoices/test_balance_algorithm.py
test_net_balance_accounts_for_returns_and_entry_void:_net_balance_for_lotcon ENTRY, CONSUMPTION, RETURN, ENTRY_VOID (fórmula documentada en el docstring: 10 - 4 + 1 - 2 = 5).test_fifo_consumption_algorithm_uses_oldest_lots_first:compare_balancescon dosAvailableLot(órdenes 1 y 2), demanda 6; parche_resolve_import_uom→ PZA; comprueba consumo 3+3 yquantity_used == 6.
Unit IVA / moneda — unit/invoices/test_currency_conversion_imports.py
- Tres monedas en
assign_values_iva_lines: FOREIGN (ME), LOCAL (MN), MANUAL (MC) conSimpleNamespace(sin BD).
Diagrama de flujo de negocio (E2E + integración export)
sequenceDiagram
participant T as Test
participant API as POST process
participant DB as Session PostgreSQL
T->>DB: seed ref + catalogs + import invoice
T->>API: process import
API->>DB: ENTRY movements
T->>DB: export invoice linked
T->>API: process export
alt qty OK
API->>DB: CONSUMPTION + discharges
else qty over balance
API-->>T: ValidationException
end
Resumen de edge cases explícitos en la suite
- Tipo factura import:
TEMgenera saldo;DEFno. - Saldo insuficiente en export: error y sin consumos.
- Doble procesamiento import: error en segundo POST y una sola entrada.
- Neteo de movimientos: retornos y anulaciones de entrada en el cálculo de saldo.
- FIFO: reparto entre lotes por
order_peps. - Moneda / IVA: tres modos de captura y conversión esperada.
BD compartida / CI
Si TEST_DATABASE_URL apunta a una base con datos previos, los builders reutilizan filas existentes donde aplica (por ejemplo direcciones de clientes vía _ensure_client_provider_address). Para máximo aislamiento, usar una BD o esquema dedicado a tests.
Prerequisites
- PostgreSQL test database available.
- Environment variable:
TEST_DATABASE_URL=postgresql://user:pass@127.0.0.1:5432/db_name(sustituye por el host real alcanzable desde el runner; no uses el literalhostde los ejemplos genéricos)
Run commands
- Fast subset (CI gate):
pytest -q tests/unit tests/integration
- Full suite:
pytest -q testspytest tests -v -rapytest tests -v -ra -s
- E2E only:
pytest -q tests/e2e
CI recommendations
- Pull request gate:
- Run unit + integration on every PR.
- Nightly / main branch:
- Run full suite including E2E.
- Keep Celery eager mode for deterministic process endpoint tests:
task_always_eager=Truetask_eager_propagates=True