Model/backend/export/tests/conftest.py
Khalim Conn-Kowlessar c1c7b06f09 refactor(modelling): consolidate plan/recommendation models into infrastructure
Move the live plan, recommendation, recommendation_materials and (retiring)
plan_recommendations tables into a new infrastructure/postgres/modelling/
subpackage as single SQLModel definitions (the epc_property pattern), absorbing
the rebuild's partial PlanRow/RecommendationRow mirrors and carrying full
legacy column parity plus recommendation.plan_id. Out-of-cluster references are
plain indexed ints (mirror convention); the live FKs are owned by the Drizzle
schema. backend/app/db/models/recommendations.py becomes a re-export shim
(ScenarioModel/InstalledMeasure stay for a later slice).

Fix the export conftest to create SQLModel-first (so Base funding_package's FK
to the now-SQLModel plan resolves) and skip the redundant drop_all on its
function-scoped throwaway DB (the epc enum type is now shared across both
metadatas). Resolves the pre-existing dual-definition collision: the rebuild
and legacy export suites are now co-runnable. No behaviour change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 21:00:14 +00:00

65 lines
2.2 KiB
Python

import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from backend.app.db.base import Base
from sqlmodel import SQLModel
import backend.app.db.models.organisation # noqa: F401 — registers Organisation with SQLModel.metadata
@pytest.fixture(scope="function")
def engine(postgresql):
"""
Create a SQLAlchemy engine bound to the ephemeral
pytest-postgresql database.
"""
# Build SQLAlchemy URL from psycopg connection info
connection_string = (
f"postgresql+psycopg://"
f"{postgresql.info.user}:"
f"{postgresql.info.password}@"
f"{postgresql.info.host}:"
f"{postgresql.info.port}/"
f"{postgresql.info.dbname}"
)
engine = create_engine(connection_string)
# Create tables once per test session. SQLModel first: the Modelling tables
# (`plan` / `recommendation` / …) are SQLModel definitions, and Base tables
# FK them (`funding_package` → `plan`), so they must exist before Base's
# create_all runs (ADR-0017 amendment — single model per table).
SQLModel.metadata.create_all(engine)
Base.metadata.create_all(engine)
# Yeild will split this function into two phase. 1) setup and 2) teardown, the latter of which will run after all
# tests have completed
yield engine
# The `postgresql` fixture is function-scoped — a fresh, throwaway database
# per test — so an explicit drop_all is redundant. We skip it: the `epc`
# Postgres enum type is now shared across both metadatas (Base `portfolio`
# tables and the SQLModel `plan`), and a two-phase metadata drop cannot drop
# a cross-metadata type cleanly (ADR-0017 amendment). Disposing the engine
# and letting the fixture discard the database is correct and conflict-free.
engine.dispose()
@pytest.fixture(scope="function")
def db_session(engine):
"""
Provides a clean transactional session per test.
Rolls back after each test to keep isolation.
"""
connection = engine.connect()
transaction = connection.begin()
session = sessionmaker(bind=connection)()
yield session
session.close()
transaction.rollback()
connection.close()