Files
home-automation/tests/test_poo.py
T
tliu93 0d898e09f2 M1-T04: converge startup chain onto the single app DB
run_all_migrations() now adopts/initializes only the app DB and returns
{'app': ...}. app/main.py drops the location/poo readiness checks
(ensure_location_db_ready / ensure_poo_db_ready) and their imports;
ensure_runtime_dirs only provisions the app DB path; lifespan still
fail-closes on a missing/unmanaged app DB. Delete the retired
location/poo adopt scripts and the alembic_location / alembic_poo
chains. Update tests to single-DB expectations and drop the obsolete
location/poo adoption + readiness tests.

pytest 95 passed; ruff clean (pre-existing only); a fresh app DB
initialized via scripts.run_migrations contains location + poo_records.
2026-06-12 16:50:05 +02:00

147 lines
4.3 KiB
Python

import pytest
from sqlalchemy import text
from app.config import Settings, get_settings
from app.dependencies import get_app_settings, get_homeassistant_client
class _FakeHomeAssistantClient:
def __init__(self) -> None:
self.sensor_calls: list[dict] = []
self.webhook_calls: list[dict] = []
def publish_sensor(self, *, entity_id: str, state: str, attributes: dict | None = None) -> None:
self.sensor_calls.append(
{"entity_id": entity_id, "state": state, "attributes": attributes or {}}
)
def trigger_webhook(self, *, webhook_id: str, body) -> None:
self.webhook_calls.append({"webhook_id": webhook_id, "body": body})
@pytest.fixture
def poo_client_with_overrides(poo_client):
client, engine = poo_client
fake_ha = _FakeHomeAssistantClient()
settings = Settings(
poo_webhook_id="poo-hook",
poo_sensor_entity_name="sensor.test_poo_status",
poo_sensor_friendly_name="Poo Status",
)
client.app.dependency_overrides[get_homeassistant_client] = lambda: fake_ha
client.app.dependency_overrides[get_app_settings] = lambda: settings
try:
yield client, engine, fake_ha
finally:
client.app.dependency_overrides.clear()
get_settings.cache_clear()
def test_poo_record_endpoint_writes_row_and_notifies_homeassistant(
poo_client_with_overrides,
) -> None:
client, engine, fake_ha = poo_client_with_overrides
response = client.post(
"/poo/record",
json={
"status": "done",
"latitude": "1.23",
"longitude": "4.56",
},
)
assert response.status_code == 200
assert response.text == ""
with engine.connect() as conn:
row = conn.execute(
text(
"SELECT status, latitude, longitude FROM poo_records "
"ORDER BY timestamp DESC LIMIT 1"
)
).one()
assert row.status == "done"
assert row.latitude == pytest.approx(1.23)
assert row.longitude == pytest.approx(4.56)
assert len(fake_ha.sensor_calls) == 1
assert fake_ha.sensor_calls[0]["entity_id"] == "sensor.test_poo_status"
assert fake_ha.sensor_calls[0]["state"] == "done"
assert fake_ha.sensor_calls[0]["attributes"]["friendly_name"] == "Poo Status"
assert len(fake_ha.webhook_calls) == 1
assert fake_ha.webhook_calls[0] == {
"webhook_id": "poo-hook",
"body": {"status": "done"},
}
def test_poo_latest_endpoint_publishes_latest_status(poo_client_with_overrides) -> None:
client, engine, fake_ha = poo_client_with_overrides
with engine.begin() as conn:
conn.execute(
text(
"INSERT INTO poo_records (timestamp, status, latitude, longitude) "
"VALUES (:timestamp, :status, :latitude, :longitude)"
),
{
"timestamp": "2026-04-20T10:05Z",
"status": "urgent",
"latitude": 3.21,
"longitude": 6.54,
},
)
response = client.get("/poo/latest")
assert response.status_code == 200
assert response.text == ""
assert len(fake_ha.sensor_calls) == 1
assert fake_ha.sensor_calls[0]["state"] == "urgent"
assert fake_ha.sensor_calls[0]["attributes"]["last_poo"]
def test_poo_record_endpoint_rejects_unknown_fields(poo_client_with_overrides) -> None:
client, _, _ = poo_client_with_overrides
response = client.post(
"/poo/record",
json={
"status": "done",
"latitude": "1.23",
"longitude": "4.56",
"extra": "nope",
},
)
assert response.status_code == 400
assert response.text == "bad request"
def test_poo_record_endpoint_rejects_invalid_latitude(poo_client_with_overrides) -> None:
client, _, _ = poo_client_with_overrides
response = client.post(
"/poo/record",
json={
"status": "done",
"latitude": "oops",
"longitude": "4.56",
},
)
assert response.status_code == 400
assert response.text == "bad request"
def test_poo_latest_endpoint_returns_ok_when_no_record_exists(poo_client_with_overrides) -> None:
client, _, _ = poo_client_with_overrides
response = client.get("/poo/latest")
assert response.status_code == 200
assert response.text == ""