One process owns this database — the image has run a single uvicorn worker for that reason since the four-engines bug — so a file beside the flows is the honest shape for it, and it is what lets `fluksio serve` need no infrastructure at all. Live values, node execution and the work queue never came here anyway; what does is a rollup a minute at a time, a row per cascade and the run history, and WAL keeps the readers going while that one writer works. DATA_DIR is now the one setting that moves everything an installation keeps; the rest derive from it and the images still spell theirs out. The schema is prepared in-process at startup, so the prestart service is gone, and the ten Postgres-only revisions collapse into one portable baseline. Three things only worked because psycopg was casting for us: a token's subject arriving as a string where the column is a UUID, `greatest`, and `date_bin`. The timestamps needed a column type of their own — SQLite stores no offset, and a naive datetime read back either raises against an aware `now` or serialises as local time. Postgres stays in the stack only for Umami, behind the analytics profile.
34 lines
1.3 KiB
Python
34 lines
1.3 KiB
Python
"""What the database has to keep true whatever dialect is under it."""
|
|
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
from sqlmodel import Session, select
|
|
|
|
from fluksio.core.db import engine
|
|
from fluksio.models import EngineEvent
|
|
|
|
# The `db` fixture is session-scoped and autouse, so the schema is already there.
|
|
|
|
|
|
def test_a_stored_instant_comes_back_aware_and_in_utc() -> None:
|
|
"""SQLite stores no offset, so the column type has to put one back.
|
|
|
|
Without it a timestamp read from the database is naive: comparing it to
|
|
`datetime.now(timezone.utc)` raises, and serialising it hands the frontend
|
|
a time with no zone, which it reads as local.
|
|
"""
|
|
stamp = datetime(2026, 8, 21, 15, 30, tzinfo=timezone(timedelta(hours=2)))
|
|
with Session(engine) as session:
|
|
session.add(
|
|
EngineEvent(ts=stamp, type="node_error", flow="tz", node="n", detail="")
|
|
)
|
|
session.commit()
|
|
|
|
with Session(engine) as session:
|
|
stored = session.exec(select(EngineEvent).where(EngineEvent.flow == "tz")).one()
|
|
assert stored.ts.tzinfo is not None
|
|
assert stored.ts.utcoffset() == timedelta(0)
|
|
assert stored.ts == stamp
|
|
# And it still compares against an aware "now" rather than raising.
|
|
assert stored.ts < datetime.now(timezone.utc)
|