phase: 190_app_name_settings_fix
All gates re-verified green. Report: - **Verified (all 4 tasks already complete; this was the final pass):** `effective_settings` resolves the 3 brand strings panel-row → tab-row → env (`applied_override_keys`, presence-not-value); `PUT /api/ui-settings` ignores string body fields; Theme-tab form is colors-only (17 pickers, no `#theme-app-name`/`#theme-placeholder`/`#theme-footer`); the ONE shared `applyBrandFromConfig` re-applies title/`.brand-text`/footer/placeholder after a panel save-reset and a tab save-reset; boot path byte-identical (single `/api/config`). - **Completion criteria:** - strings order panel→tab→env, both branches — ✅ `tests/unit/test_theming.py::test_effective_app_name_prefers_settings_panel_row_over_ui_settings_row`, `..._fall_back_to_ui_settings_row_without_panel_row`, `..._per_field_panel_then_tab_then_env`, `..._ignore_panel_rows_when_the_overlay_is_off` - PUT ignores string fields + no 3 inputs in the tab — ✅ `tests/integration/test_ui_settings_api.py::test_put_ignores_string_fields_in_body` (+2) and the E2E `test_theme_tab_dom_is_colors_only` - live apply without reload + incident regression — ✅ `tests/e2e/test_app_name_settings_fix.py` tests 1–4 - byte-identical boot, no double-apply — ✅ `tests/unit/test_brand_live.py` markers + E2E "exactly one /api/config" - suite green, coverage >90%, ruff+pyright clean — ✅ - no behavior change in prior phases — ✅ (palette, phase-168 panel, env fallback suites all green) - atomic commit — **left to the harness** (pipeline rule overrides per-phase committing; no git write run) - **Gates:** `uv run pytest --cov=app --cov-report=term-missing` → **4630 passed, TOTAL 6941 16 99%**, exit 0 · `uv run pytest tests/e2e/test_app_name_settings_fix.py -v --no-cov` → **6 passed** in isolation · regression E2E (isolated, one process each, all exit 0): `test_smoke` `test_configurable_brand` `test_admin_theme_tab` `test_theme_save_and_coverage` `test_theme_semantic_completion` `test_themed_favicon` `test_dark_tech_theme` `test_admin_settings_panel` `test_admin_settings_layout` `test_settings_mobile_value_width` · `uv run ruff check .` → All checks passed · `uv run pyright` → 0 errors. - **Documentation:** README — fixed "saving one repaints *every* open page" (only the open document repaints; other pages on next load) and a broken paragraph wrap in *Customizing the look*; AGENTS.md rule 16 — `test_chunks_hnsw_recall.py` now described accurately (index paths forced with `enable_seqscan`/`indexonlyscan`/`bitmapscan` off, unforced plan asserted only on a pristine relation); `tests/unit/test_theme_frontend.py` — two stale section headers ("20 form fields"/"20 inputs" → 17 color). No AGENTS.md settings-overlay/resolution-order clause exists, so per the phase none was added. Full env-var cross-check (`BOR_*` in `app/config.py` vs README) → 0 gaps; 0 missing file references. - **Notable:** the working tree also carries this phase's flake hardening of the phase-188 `tests/integration/test_chunks_hnsw_recall.py` (verified legit and green). Live infra untouched; shared DB left clean (0 `app_settings`, 0 `ui_settings`). **Flagged, not changed:** `settings_overlay.applied_override_keys` lets a `ProgrammingError` (missing `app_settings`) propagate from public `GET /api/config`, where `load_overrides` deliberately fails soft — only reachable on an un-migrated (0027) DB, outside this phase's pinned A1 semantics, so left for the owner to rule on rather than silently deviating. - **Next pending phase:** `191_source_name_labels_tags`.
This commit is contained in:
@@ -221,6 +221,71 @@ def test_get_engine_creates_once_and_caches() -> None:
|
||||
assert first is second
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# applied_override_keys — "which keys does the panel own?" (phase 190)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class _KeyResult:
|
||||
"""The ``session.execute(select(AppSetting.key))`` shape — the one
|
||||
call :func:`applied_override_keys` makes on the caller's session."""
|
||||
|
||||
def __init__(self, keys: list[str]) -> None:
|
||||
self._keys = keys
|
||||
|
||||
def scalars(self) -> _KeyResult:
|
||||
return self
|
||||
|
||||
def all(self) -> list[str]:
|
||||
return list(self._keys)
|
||||
|
||||
|
||||
class _RecordingSession:
|
||||
"""A stand-in session: it answers the key read and counts the calls
|
||||
(the resolver's "one read for all three string fields" contract)."""
|
||||
|
||||
def __init__(self, keys: list[str]) -> None:
|
||||
self.keys = keys
|
||||
self.calls = 0
|
||||
|
||||
def execute(self, _stmt: object) -> _KeyResult:
|
||||
self.calls += 1
|
||||
return _KeyResult(self.keys)
|
||||
|
||||
|
||||
def test_applied_override_keys_filters_orphans_and_locked_rows(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
from typing import Any, cast
|
||||
"""Only a row the overlay would ACTUALLY apply counts: an orphan row
|
||||
(a field a later release removed) and a LOCKED A3 row (a
|
||||
hand-inserted ``database_url`` — read-only, never in effect) are
|
||||
excluded, so a row that changes nothing can never win a brand
|
||||
string from the Theme-tab fallback (phase 190 A1)."""
|
||||
monkeypatch.setenv("BOR_SETTINGS_DB", "1")
|
||||
session = _RecordingSession(
|
||||
["app_name", "database_url", "embedding_dim", "no_such_setting"]
|
||||
)
|
||||
assert settings_overlay.applied_override_keys(
|
||||
cast(Any, session)
|
||||
) == frozenset({"app_name"})
|
||||
assert session.calls == 1
|
||||
|
||||
|
||||
def test_applied_override_keys_kill_switch_reads_nothing(
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
from typing import Any, cast
|
||||
"""``BOR_SETTINGS_DB=0`` means NO row is in effect (D6) — the helper
|
||||
says so WITHOUT touching the database, so ``effective_settings``
|
||||
falls through to the Theme-tab row instead of shadowing it with a
|
||||
row the resolver ignores."""
|
||||
monkeypatch.setenv("BOR_SETTINGS_DB", "0")
|
||||
session = _RecordingSession(["app_name"])
|
||||
assert settings_overlay.applied_override_keys(cast(Any, session)) == frozenset()
|
||||
assert session.calls == 0 # short-circuited before any statement
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# merge — the defensive fallback (a validator change in a later release
|
||||
# blames a field no override owns and no single drop heals the set: the
|
||||
|
||||
Reference in New Issue
Block a user