158 lines
5.9 KiB
Python
158 lines
5.9 KiB
Python
from collections.abc import Generator
|
|
|
|
import pytest
|
|
from fastapi import FastAPI
|
|
from fastapi.testclient import TestClient
|
|
from sqlalchemy import create_engine
|
|
from sqlalchemy.orm import Session, sessionmaker
|
|
|
|
# Importar modelos con relaciones string para registrar mappers antes de tests.
|
|
# Evita errores tipo: expression 'Pedimentos' failed to locate a name.
|
|
from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos # noqa: F401
|
|
from api.v1.modules.a76.invoices.models import InvoiceHeader
|
|
from api.v1.modules.a76.transportation.trailers.models import Trailer # noqa: F401
|
|
from api.v1.modules.a76.transportation.transporters.models import Transporter # noqa: F401
|
|
from api.v1.modules.a76.transportation.vehicles.models import Vehicle # noqa: F401
|
|
from api.v1.modules.a76.invoices.imports.process import routes as process_routes
|
|
from api.v1.modules.a76.invoices.imports.process import main_process as import_main_process
|
|
from api.v1.modules.a76.invoices.exports.process import main_process as export_main_process
|
|
from core.celery_app import celery_app
|
|
from core.config import settings
|
|
from core.database import get_core_db
|
|
from core.security import get_current_user
|
|
from tests.fixtures.builders import (
|
|
EphemeralOrgIds,
|
|
allocate_ephemeral_tenant_company_ids,
|
|
ensure_tenant_company,
|
|
)
|
|
|
|
|
|
TEST_DB_URL = (
|
|
__import__("os").environ.get("TEST_DATABASE_URL")
|
|
or __import__("os").environ.get("CORE_DATABASE_URL")
|
|
or settings.core_database_url
|
|
)
|
|
|
|
engine = create_engine(TEST_DB_URL, future=True)
|
|
TestingSessionLocal = sessionmaker(
|
|
bind=engine,
|
|
autoflush=False,
|
|
autocommit=False,
|
|
expire_on_commit=False,
|
|
class_=Session,
|
|
join_transaction_mode="create_savepoint",
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def db_session() -> Generator[Session, None, None]:
|
|
connection = engine.connect()
|
|
transaction = connection.begin()
|
|
session = TestingSessionLocal(bind=connection)
|
|
try:
|
|
yield session
|
|
finally:
|
|
session.close()
|
|
transaction.rollback()
|
|
connection.close()
|
|
|
|
|
|
@pytest.fixture
|
|
def test_tenant(db_session: Session) -> EphemeralOrgIds:
|
|
"""Tenant y company dedicados por test (IDs efímeros en rango alto)."""
|
|
tid, cid = allocate_ephemeral_tenant_company_ids(db_session)
|
|
ensure_tenant_company(db_session, tenant_id=tid, company_id=cid)
|
|
return EphemeralOrgIds(tenant_id=tid, company_id=cid)
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def celery_eager() -> Generator[None, None, None]:
|
|
prev_broker = celery_app.conf.broker_url
|
|
prev_backend = celery_app.conf.result_backend
|
|
prev_always_eager = celery_app.conf.task_always_eager
|
|
prev_propagates = celery_app.conf.task_eager_propagates
|
|
prev_store_result = celery_app.conf.task_store_eager_result
|
|
prev_ignore_result = celery_app.conf.task_ignore_result
|
|
|
|
# Aislar Celery de infraestructura externa en tests (sin Redis/Valkey).
|
|
celery_app.conf.broker_url = "memory://"
|
|
celery_app.conf.result_backend = "cache+memory://"
|
|
celery_app.conf.task_always_eager = True
|
|
celery_app.conf.task_eager_propagates = True
|
|
celery_app.conf.task_store_eager_result = False
|
|
celery_app.conf.task_ignore_result = True
|
|
try:
|
|
yield
|
|
finally:
|
|
celery_app.conf.broker_url = prev_broker
|
|
celery_app.conf.result_backend = prev_backend
|
|
celery_app.conf.task_always_eager = prev_always_eager
|
|
celery_app.conf.task_eager_propagates = prev_propagates
|
|
celery_app.conf.task_store_eager_result = prev_store_result
|
|
celery_app.conf.task_ignore_result = prev_ignore_result
|
|
|
|
|
|
@pytest.fixture
|
|
def app(
|
|
db_session: Session,
|
|
test_tenant: EphemeralOrgIds,
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> FastAPI:
|
|
test_app = FastAPI()
|
|
test_app.include_router(process_routes.router, prefix="/api/v1/a76")
|
|
|
|
def _override_get_db() -> Generator[Session, None, None]:
|
|
yield db_session
|
|
|
|
async def _override_current_user():
|
|
return {"sub": "test-user", "tenant_id": test_tenant.tenant_id}
|
|
|
|
# validate_access_to_resource is imported directly in the routes module.
|
|
def _override_validate_access_to_resource(db, company_id, current_user, required_permissions=None, **kwargs):
|
|
return int(current_user["tenant_id"])
|
|
|
|
monkeypatch.setattr(process_routes, "validate_access_to_resource", _override_validate_access_to_resource)
|
|
|
|
class _InlineResult:
|
|
def __init__(self, task_id: str):
|
|
self.id = task_id
|
|
|
|
def _run_import_inline(*, args=None, **kwargs):
|
|
if args is None:
|
|
args = []
|
|
invoice_id, tenant_id, company_id = args
|
|
invoice = db_session.get(InvoiceHeader, int(invoice_id))
|
|
if invoice is None:
|
|
return _InlineResult("missing-import-invoice")
|
|
import_main_process.main_process(db_session, invoice, str(tenant_id), str(company_id))
|
|
db_session.flush()
|
|
return _InlineResult(f"inline-import-{invoice_id}")
|
|
|
|
def _run_export_inline(*, args=None, **kwargs):
|
|
if args is None:
|
|
args = []
|
|
invoice_id, tenant_id, company_id = args
|
|
invoice = db_session.get(InvoiceHeader, int(invoice_id))
|
|
if invoice is None:
|
|
return _InlineResult("missing-export-invoice")
|
|
export_main_process.main_process(db_session, invoice, str(tenant_id), str(company_id))
|
|
db_session.flush()
|
|
return _InlineResult(f"inline-export-{invoice_id}")
|
|
|
|
class _InlineTask:
|
|
def __init__(self, runner):
|
|
self.apply_async = runner
|
|
|
|
# Evita worker/redis y obliga ejecución inline con la misma sesión.
|
|
monkeypatch.setattr(process_routes, "process_invoice_task", _InlineTask(_run_import_inline))
|
|
monkeypatch.setattr(process_routes, "process_export_invoice_task", _InlineTask(_run_export_inline))
|
|
|
|
test_app.dependency_overrides[get_core_db] = _override_get_db
|
|
test_app.dependency_overrides[get_current_user] = _override_current_user
|
|
return test_app
|
|
|
|
|
|
@pytest.fixture
|
|
def client(app: FastAPI) -> TestClient:
|
|
return TestClient(app)
|