Files
plantillas-proyectos/backend/tests

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_URL o CORE_DATABASE_URL o settings.core_database_url. Cada test corre dentro de una transacción; al terminar no persiste nada (rollback).
  • Celery: sin Redis; task_always_eager para que el endpoint dispare lógica síncrona.
  • Seguridad en tests: validate_access_to_resource se reemplaza por un stub que devuelve tenant_id del usuario fake (alineado con el fixture test_tenant).

Builders: qué se construye antes de llamar a la API

Orden típico en casi todos los tests que tocan proceso:

  1. ensure_reference_data: tipos de factura (TEM, DEF, MEX, DONAC) y régimen A1 si no existen.
  2. Fixture test_tenant: asigna IDs efímeros altos y crea tenant + company; el app usa ese tenant_id en el usuario fake.
  3. 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.
  4. create_import_invoice_with_line / create_export_invoice_with_line segú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 especialtest_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 TEM qty 10 → POST .../process → comprueba BalanceMovement tipo ENTRY → factura export ligada a import/línea qty 4 → POST process → comprueba CONSUMPTION, DischargeHeader, DischargeDetail con movement_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_lot con 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_balances con dos AvailableLot (órdenes 1 y 2), demanda 6; parche _resolve_import_uom → PZA; comprueba consumo 3+3 y quantity_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) con SimpleNamespace (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: TEM genera saldo; DEF no.
  • 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 literal host de los ejemplos genéricos)

Run commands

  • Fast subset (CI gate):
    • pytest -q tests/unit tests/integration
  • Full suite:
    • pytest -q tests
    • pytest tests -v -ra
    • pytest 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=True
    • task_eager_propagates=True