|
@@ -7,6 +7,8 @@ import pytest
|
|
|
from httpx import AsyncClient
|
|
from httpx import AsyncClient
|
|
|
from sqlalchemy import text
|
|
from sqlalchemy import text
|
|
|
|
|
|
|
|
|
|
+from backend.tests._fixtures.notification_toggles import EVENT_TOGGLE_COLUMNS, TOGGLE_TARGET
|
|
|
|
|
+
|
|
|
|
|
|
|
|
class TestNotificationsAPI:
|
|
class TestNotificationsAPI:
|
|
|
"""Integration tests for /api/v1/notifications/ endpoints."""
|
|
"""Integration tests for /api/v1/notifications/ endpoints."""
|
|
@@ -500,7 +502,7 @@ class TestNotificationsAPI:
|
|
|
response = await async_client.get(f"/api/v1/notifications/{provider.id}")
|
|
response = await async_client.get(f"/api/v1/notifications/{provider.id}")
|
|
|
assert response.json()["on_billing_charge_failed"] is False
|
|
assert response.json()["on_billing_charge_failed"] is False
|
|
|
|
|
|
|
|
- # Per-event toggles that live only in these hand-maintained field maps.
|
|
|
|
|
|
|
+ # Every per-event toggle, across the hand-maintained field maps.
|
|
|
#
|
|
#
|
|
|
# These have to be exercised through the route, not the ORM: both
|
|
# These have to be exercised through the route, not the ORM: both
|
|
|
# directions of notifications.py are hand-maintained field-by-field maps,
|
|
# directions of notifications.py are hand-maintained field-by-field maps,
|
|
@@ -518,52 +520,61 @@ class TestNotificationsAPI:
|
|
|
# the toggles could not be turned on at all.
|
|
# the toggles could not be turned on at all.
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.asyncio
|
|
|
@pytest.mark.integration
|
|
@pytest.mark.integration
|
|
|
- @pytest.mark.parametrize(
|
|
|
|
|
- "field",
|
|
|
|
|
- ["on_ha_sensor_alert", "on_location_ha_sensor_alert", "on_stock_reorder_alert", "on_stock_break_alert"],
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ @pytest.mark.parametrize("field", EVENT_TOGGLE_COLUMNS)
|
|
|
async def test_create_persists_and_returns_the_toggle(self, async_client: AsyncClient, field: str):
|
|
async def test_create_persists_and_returns_the_toggle(self, async_client: AsyncClient, field: str):
|
|
|
|
|
+ # Driven to whatever the column does not default to. Nine of these
|
|
|
|
|
+ # default to True on the model and on the response schema, so sending
|
|
|
|
|
+ # True and asserting True is answered by the default alone -- a field
|
|
|
|
|
+ # dropped by the create constructor still reads back True, and the
|
|
|
|
|
+ # mutation that would prove the constructor load-bearing survives.
|
|
|
|
|
+ target = TOGGLE_TARGET[field]
|
|
|
|
|
+
|
|
|
response = await async_client.post(
|
|
response = await async_client.post(
|
|
|
"/api/v1/notifications/",
|
|
"/api/v1/notifications/",
|
|
|
json={
|
|
json={
|
|
|
"name": "Sensor Alert Test",
|
|
"name": "Sensor Alert Test",
|
|
|
"provider_type": "ntfy",
|
|
"provider_type": "ntfy",
|
|
|
"config": {"server": "https://ntfy.sh", "topic": "test"},
|
|
"config": {"server": "https://ntfy.sh", "topic": "test"},
|
|
|
- field: True,
|
|
|
|
|
|
|
+ field: target,
|
|
|
},
|
|
},
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
|
assert response.status_code == 200
|
|
|
- assert response.json()[field] is True
|
|
|
|
|
|
|
+ assert response.json()[field] is target
|
|
|
|
|
|
|
|
# Re-read it: a value dropped by the create constructor but echoed
|
|
# Re-read it: a value dropped by the create constructor but echoed
|
|
|
# from the request body would still pass the assertion above.
|
|
# from the request body would still pass the assertion above.
|
|
|
provider_id = response.json()["id"]
|
|
provider_id = response.json()["id"]
|
|
|
response = await async_client.get(f"/api/v1/notifications/{provider_id}")
|
|
response = await async_client.get(f"/api/v1/notifications/{provider_id}")
|
|
|
- assert response.json()[field] is True
|
|
|
|
|
|
|
+ assert response.json()[field] is target
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
@pytest.mark.asyncio
|
|
|
@pytest.mark.integration
|
|
@pytest.mark.integration
|
|
|
- @pytest.mark.parametrize(
|
|
|
|
|
- "field",
|
|
|
|
|
- ["on_ha_sensor_alert", "on_location_ha_sensor_alert", "on_stock_reorder_alert", "on_stock_break_alert"],
|
|
|
|
|
- )
|
|
|
|
|
|
|
+ @pytest.mark.parametrize("field", EVENT_TOGGLE_COLUMNS)
|
|
|
async def test_patch_is_reflected_by_every_read_route(
|
|
async def test_patch_is_reflected_by_every_read_route(
|
|
|
self, async_client: AsyncClient, notification_provider_factory, field: str
|
|
self, async_client: AsyncClient, notification_provider_factory, field: str
|
|
|
):
|
|
):
|
|
|
- """PATCH already persisted (generic setattr loop) — the reads were the broken half."""
|
|
|
|
|
- provider = await notification_provider_factory(**{field: False})
|
|
|
|
|
|
|
+ """PATCH already persisted (generic setattr loop) — the reads were the broken half.
|
|
|
|
|
+
|
|
|
|
|
+ Seeded at the target's opposite and driven to the target, so the value
|
|
|
|
|
+ asserted is never the one the column would have supplied on its own.
|
|
|
|
|
+ With ``True`` on both sides the nine True-default columns could not see
|
|
|
|
|
+ the half this test exists for: deleting a column from the read map left
|
|
|
|
|
+ them passing.
|
|
|
|
|
+ """
|
|
|
|
|
+ target = TOGGLE_TARGET[field]
|
|
|
|
|
+ provider = await notification_provider_factory(**{field: not target})
|
|
|
|
|
|
|
|
- response = await async_client.patch(f"/api/v1/notifications/{provider.id}", json={field: True})
|
|
|
|
|
|
|
+ response = await async_client.patch(f"/api/v1/notifications/{provider.id}", json={field: target})
|
|
|
assert response.status_code == 200
|
|
assert response.status_code == 200
|
|
|
- assert response.json()[field] is True
|
|
|
|
|
|
|
+ assert response.json()[field] is target
|
|
|
|
|
|
|
|
response = await async_client.get(f"/api/v1/notifications/{provider.id}")
|
|
response = await async_client.get(f"/api/v1/notifications/{provider.id}")
|
|
|
- assert response.json()[field] is True
|
|
|
|
|
|
|
+ assert response.json()[field] is target
|
|
|
|
|
|
|
|
response = await async_client.get("/api/v1/notifications/")
|
|
response = await async_client.get("/api/v1/notifications/")
|
|
|
listed = next(p for p in response.json() if p["id"] == provider.id)
|
|
listed = next(p for p in response.json() if p["id"] == provider.id)
|
|
|
- assert listed[field] is True
|
|
|
|
|
|
|
+ assert listed[field] is target
|
|
|
|
|
|
|
|
|
|
|
|
|
class TestNotificationTemplatesAPI:
|
|
class TestNotificationTemplatesAPI:
|