smriti/backend/tests/integration/conftest.py
Himanshu Dongre f22056418d Add optional local-first SQLite mode (Phase A core)
Smriti can now run against a file-backed SQLite database with no Docker
and no Postgres, removing the operate-a-backend burden for solo builders.
Postgres remains the stronger shared/team mode, unchanged.

- Portable column types (app/db/types.py): JSON renders as JSONB on
  PostgreSQL and generic JSON on SQLite; the pgvector embedding column
  renders as JSON on SQLite. Same models and create_all on both backends.
- Mode resolution (config.py): SMRITI_DB_MODE=local|postgres, defaulting
  to local when unconfigured. An explicitly-set Postgres DATABASE_URL
  preserves Postgres behavior, so existing setups are unaffected. Local
  DB defaults to ~/.smriti/smriti.db; SMRITI_LOCAL_DB_PATH overrides.
- SQLite engine setup (database.py): check_same_thread plus foreign_keys
  / WAL / busy_timeout pragmas; a lazy first-run create_all bootstrap on
  first DB use, which keeps the integration test suite insulated.
- Removed the integration-test JSONB/VECTOR DDL substitution hack — the
  models are genuinely portable now, so conftest needs no type patching.
- Alembic resolves its URL through the same logic (Postgres mode only).
- New persistent file-backed SQLite smoke test, plus mode-resolution and
  per-dialect type-rendering tests.

Local mode uses create_all, not Alembic. No schema changes.
2026-05-16 22:26:05 +05:30

63 lines
1.8 KiB
Python

"""Integration test configuration — uses an in-memory SQLite database.
Overrides the FastAPI app's `get_db` dependency to use a fresh in-memory
SQLite database for each test, enabling full API integration tests
without requiring PostgreSQL.
The ORM models use portable column types (see `app/db/types.py`), so
`create_all` works against SQLite natively — no JSONB/VECTOR DDL
substitution is needed here.
"""
import pytest
from fastapi.testclient import TestClient
from sqlalchemy import create_engine, event
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool
from app.db.database import Base, get_db
from app.main import app
@pytest.fixture(scope="function")
def db_engine():
engine = create_engine(
"sqlite://",
connect_args={"check_same_thread": False},
poolclass=StaticPool,
)
# Enable foreign keys in SQLite (off by default).
@event.listens_for(engine, "connect")
def set_sqlite_pragma(dbapi_connection, connection_record):
cursor = dbapi_connection.cursor()
cursor.execute("PRAGMA foreign_keys=ON")
cursor.close()
Base.metadata.create_all(bind=engine)
yield engine
Base.metadata.drop_all(bind=engine)
@pytest.fixture(scope="function")
def db_session(db_engine):
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=db_engine)
session = SessionLocal()
yield session
session.close()
@pytest.fixture(scope="function")
def client(db_session):
"""FastAPI TestClient with overridden DB dependency."""
def override_get_db():
try:
yield db_session
finally:
pass
app.dependency_overrides[get_db] = override_get_db
with TestClient(app) as c:
yield c
app.dependency_overrides.clear()