| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 |
- """The per-event notification toggles, derived from the model's own columns (#2945).
- A hardcoded list only covers the toggles someone remembered to add to it, so it
- carries the same hazard as the field maps it is meant to police, one layer up.
- Derived, a toggle added tomorrow is exercised the day its column lands.
- This lives here rather than in either test module because the unit parity checks
- and the integration round-trips need the same two answers, and a derivation
- duplicated across files is a list with extra steps.
- """
- from __future__ import annotations
- from sqlalchemy import Boolean
- from sqlalchemy.sql.schema import Column
- from backend.app.models.notification import NotificationProvider
- # The Boolean check is not decoration. An on_* column of some other type would
- # turn `{field: True}` into a 422 and break every test parametrised off this
- # list, in whichever unrelated PR happened to add that column -- and a spurious
- # failure is how a derivation gets edited until it passes.
- _EVENT_TOGGLES: list[Column] = [
- column
- for column in NotificationProvider.__table__.columns
- if column.name.startswith("on_") and isinstance(column.type, Boolean)
- ]
- EVENT_TOGGLE_COLUMNS: list[str] = sorted(column.name for column in _EVENT_TOGGLES)
- # Every column a client is meant to be able to set. The hazard #2945 describes is
- # per-column -- a field that exists everywhere except one of the maps it has to
- # cross -- and `enabled`, the quiet-hours and digest fields cross the same two
- # maps as the on_* toggles. The prefix is a naming convention, not the boundary.
- # What is left out is what the server owns: the key, the timestamps, and the
- # delivery status it records itself.
- _SERVER_OWNED = {"id", "created_at", "updated_at", "last_success", "last_error", "last_error_at"}
- SETTABLE_COLUMNS: list[str] = sorted(
- column.name for column in NotificationProvider.__table__.columns if column.name not in _SERVER_OWNED
- )
- def _model_default(column: Column) -> bool:
- """The value a row gets for this column when the caller sends nothing.
- Reads the Python-side default only. That is exact while every toggle
- declares one, which ``test_every_event_column_has_a_python_default`` holds
- in place: a column added with only a ``server_default`` would read as False
- here, get a target of True, and for a server-default-true column the round
- trip would send True and assert True -- vacuous again, and silently.
- """
- if column.default is None:
- return False
- return bool(column.default.arg)
- # The value each toggle has to be driven to for the assertion to mean anything.
- #
- # Nine of these columns default to True on the model and on the response schema,
- # so a test that sends True and asserts True is answered by the default alone:
- # a field dropped by a hand-maintained map still reads back True, and the
- # mutation that would prove the map is load-bearing survives. Driving each
- # toggle to whatever its default is not makes the round trip the only thing that
- # can supply the answer.
- TOGGLE_TARGET: dict[str, bool] = {column.name: not _model_default(column) for column in _EVENT_TOGGLES}
|