Migrate location recorder and refine db config
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
from app.config import Settings
|
||||
|
||||
|
||||
def test_settings_support_two_independent_database_urls(monkeypatch) -> None:
|
||||
monkeypatch.setenv("LOCATION_DATABASE_URL", "sqlite:///./data/locationRecorder.db")
|
||||
monkeypatch.setenv("POO_DATABASE_URL", "sqlite:///./data/pooRecorder.db")
|
||||
|
||||
settings = Settings()
|
||||
|
||||
assert settings.location_database_url == "sqlite:///./data/locationRecorder.db"
|
||||
assert settings.poo_database_url == "sqlite:///./data/pooRecorder.db"
|
||||
assert settings.location_sqlite_path is not None
|
||||
assert settings.location_sqlite_path.name == "locationRecorder.db"
|
||||
assert settings.poo_sqlite_path is not None
|
||||
assert settings.poo_sqlite_path.name == "pooRecorder.db"
|
||||
@@ -0,0 +1,172 @@
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
import sqlite3
|
||||
|
||||
import pytest
|
||||
from alembic import command
|
||||
from alembic.config import Config
|
||||
from sqlalchemy import create_engine, text
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
import app.db
|
||||
from app.main import create_app
|
||||
|
||||
LOCATION_BASELINE_REVISION = "20260419_01_location_baseline"
|
||||
|
||||
|
||||
def _make_alembic_config(database_url: str) -> Config:
|
||||
config = Config("alembic.ini")
|
||||
config.set_main_option("sqlalchemy.url", database_url)
|
||||
return config
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def location_client(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
|
||||
database_path = tmp_path / "location_test.db"
|
||||
database_url = f"sqlite:///{database_path}"
|
||||
|
||||
command.upgrade(_make_alembic_config(database_url), "head")
|
||||
|
||||
engine = create_engine(database_url, connect_args={"check_same_thread": False})
|
||||
session_local = sessionmaker(bind=engine, autoflush=False, autocommit=False)
|
||||
|
||||
monkeypatch.setattr(app.db, "engine", engine)
|
||||
monkeypatch.setattr(app.db, "SessionLocal", session_local)
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
fastapi_app = create_app()
|
||||
with TestClient(fastapi_app) as client:
|
||||
yield client, engine
|
||||
|
||||
engine.dispose()
|
||||
|
||||
|
||||
def test_location_record_endpoint_writes_row(location_client) -> None:
|
||||
client, engine = location_client
|
||||
|
||||
response = client.post(
|
||||
"/location/record",
|
||||
json={
|
||||
"person": "tianyu",
|
||||
"latitude": "1.23",
|
||||
"longitude": "4.56",
|
||||
"altitude": "7.89",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.text == ""
|
||||
|
||||
with engine.connect() as conn:
|
||||
row = conn.execute(
|
||||
text(
|
||||
"SELECT person, datetime, latitude, longitude, altitude "
|
||||
"FROM location ORDER BY datetime DESC LIMIT 1"
|
||||
)
|
||||
).one()
|
||||
|
||||
assert row.person == "tianyu"
|
||||
assert row.latitude == pytest.approx(1.23)
|
||||
assert row.longitude == pytest.approx(4.56)
|
||||
assert row.altitude == pytest.approx(7.89)
|
||||
datetime.fromisoformat(row.datetime.replace("Z", "+00:00"))
|
||||
|
||||
|
||||
def test_location_record_endpoint_rejects_unknown_fields(location_client) -> None:
|
||||
client, _ = location_client
|
||||
|
||||
response = client.post(
|
||||
"/location/record",
|
||||
json={
|
||||
"person": "tianyu",
|
||||
"latitude": "1.23",
|
||||
"longitude": "4.56",
|
||||
"extra": "not-allowed",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
|
||||
|
||||
def test_location_record_endpoint_keeps_legacy_lenient_number_parsing(location_client) -> None:
|
||||
client, engine = location_client
|
||||
|
||||
response = client.post(
|
||||
"/location/record",
|
||||
json={
|
||||
"person": "tianyu",
|
||||
"latitude": "bad-lat",
|
||||
"longitude": "bad-long",
|
||||
"altitude": "bad-alt",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
with engine.connect() as conn:
|
||||
row = conn.execute(
|
||||
text(
|
||||
"SELECT latitude, longitude, altitude "
|
||||
"FROM location ORDER BY datetime DESC LIMIT 1"
|
||||
)
|
||||
).one()
|
||||
|
||||
assert row.latitude == pytest.approx(0.0)
|
||||
assert row.longitude == pytest.approx(0.0)
|
||||
assert row.altitude == pytest.approx(0.0)
|
||||
|
||||
|
||||
def test_legacy_style_location_db_can_be_stamped_and_adopted(
|
||||
tmp_path: Path, monkeypatch: pytest.MonkeyPatch
|
||||
) -> None:
|
||||
database_path = tmp_path / "legacy_location.db"
|
||||
conn = sqlite3.connect(database_path)
|
||||
conn.execute(
|
||||
"""
|
||||
CREATE TABLE location (
|
||||
person TEXT NOT NULL,
|
||||
datetime TEXT NOT NULL,
|
||||
latitude REAL NOT NULL,
|
||||
longitude REAL NOT NULL,
|
||||
altitude REAL,
|
||||
PRIMARY KEY (person, datetime)
|
||||
)
|
||||
"""
|
||||
)
|
||||
conn.execute("PRAGMA user_version = 2")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
database_url = f"sqlite:///{database_path}"
|
||||
command.stamp(_make_alembic_config(database_url), LOCATION_BASELINE_REVISION)
|
||||
|
||||
engine = create_engine(database_url, connect_args={"check_same_thread": False})
|
||||
session_local = sessionmaker(bind=engine, autoflush=False, autocommit=False)
|
||||
monkeypatch.setattr(app.db, "engine", engine)
|
||||
monkeypatch.setattr(app.db, "SessionLocal", session_local)
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
fastapi_app = create_app()
|
||||
with TestClient(fastapi_app) as client:
|
||||
response = client.post(
|
||||
"/location/record",
|
||||
json={
|
||||
"person": "legacy-user",
|
||||
"latitude": "12.3",
|
||||
"longitude": "45.6",
|
||||
"altitude": "7.8",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
with engine.connect() as db_conn:
|
||||
revision = db_conn.execute(text("SELECT version_num FROM alembic_version")).scalar_one()
|
||||
row_count = db_conn.execute(text("SELECT COUNT(*) FROM location")).scalar_one()
|
||||
|
||||
assert revision == LOCATION_BASELINE_REVISION
|
||||
assert row_count == 1
|
||||
|
||||
engine.dispose()
|
||||
Reference in New Issue
Block a user