Files
brain-of-reese/tests/unit/test_brand_live.py
T
ducoterra c6ffa050a6
Build and Push Containers / build-and-push-app (push) Successful in 2m9s
Build and Push Containers / build-and-push-db (push) Successful in 15s
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`.
2026-10-18 02:59:19 -04:00

409 lines
19 KiB
Python

"""Unit: the live-brand-on-save marker pins (phase 190, tasks 01 + 03).
Defect (b) of the phase (``TODO.md`` L3, the stale half): a save in
either admin surface never re-applied the brand strings to the OPEN
document. ``settings.js`` re-GETed the catalogue after a save but never
touched the brand; ``theme.js``'s save ran ``applyServedTheme`` (the
phase-92 COLOR reconcile) and stopped there; ``brand.js`` applied the
strings ONCE at boot from ``/api/config`` — so the open page kept the old
name until a manual reload.
Task 01 pinned that as the ABSENT behavior; **task 03 inverted it**, and
this module now pins the fix:
* ``APPLY_BRAND`` (``applyBrandFromConfig``) is ONE function in
``brand.js`` — the single owner of the brand DOM — exported on the
layer's existing ``window`` surface and called by the BOOT
(``applyBrand``) and by both admin surfaces. The boot is the
EXTRACTION, not a duplication: the four DOM writes exist once;
* ``settings.js`` re-applies after a save / per-row Reset / Clear all
that touched one of the three brand keys, and after nothing else;
* ``theme.js`` re-applies after Save and after Reset (a reset can make a
legacy baked-in string fall back, so the page must re-read it);
* a FAILED re-apply leaves the DOM at its last applied state — the
phase-168 "never a blanked panel" precedent applied to the brand.
House JS convention (``test_frontend_brand.py`` /
``test_settings_frontend_view.py`` / ``test_save_chat_ui.py``): the
browser behavior is pinned at source-marker level here, E2E in the
phase's ``tests/e2e/test_app_name_settings_fix.py`` (task 04).
``BRAND_DOM_TOKENS`` (the four writes that constitute "applying the
brand strings": the document title, the ``.brand-text`` header, the
``#message-input`` composer placeholder, the ``.footer-text`` line) stays
the ownership pin it was in task 01 — those belong to the ONE shared pass
in ``brand.js`` and must NEVER be duplicated inside ``settings.js`` / the
``theme.js`` save paths. The live-apply is asserted by presence of the
shared CALL, never by a second copy of the DOM path.
"""
from __future__ import annotations
from pathlib import Path
ASSETS = Path(__file__).resolve().parents[2] / "frontend" / "assets"
BRAND_JS = ASSETS / "brand.js"
SETTINGS_JS = ASSETS / "settings.js"
THEME_JS = ASSETS / "theme.js"
#: The ONE shared brand pass task 03 extracted out of ``brand.js`` (the
#: phase-190 contract name) — boot and live-apply run this function.
APPLY_BRAND = "applyBrandFromConfig"
#: The four DOM writes that ARE "re-applying the brand strings" — the
#: shared pass owns them; a save surface that writes one of these
#: directly has duplicated the brand path (the defect the phase fixes
#: must not be re-introduced as a second copy).
BRAND_DOM_TOKENS = ("document.title", "brand-text", "message-input", "footer-text")
#: The three keys the Settings panel owns and whose save/reset therefore
#: re-applies the brand (phase 190 A1). A save of any other row must not.
BRAND_KEYS = ("app_name", "input_placeholder", "footer_text")
#: The two statements every live re-apply is made of, pinned verbatim so
#: the counts below count CALL SITES and not prose (the house
#: ``= fetch("/api/config"`` idiom in ``test_frontend_brand.py``). Both
#: surfaces write them identically — the shared semantics, one shape.
CONFIG_FETCH = 'const r = await fetch("/api/config", { cache: "no-store" });'
SHARED_CALL = "window.applyBrandFromConfig(await r.json());"
# The three settings.js settle paths (save / per-row Reset / clear-all) —
# each ends its success arm with the catalogue re-GET (phase 168,
# untouched) followed by the brand re-apply (phase 190 task 03, additive).
SETTINGS_SETTLE_PATHS = (
"async function saveSettings()",
"async function resetRow(",
"async function confirmClearAll(",
)
def _read(path: Path) -> str:
return path.read_text(encoding="utf-8")
def _body(js: str, header: str, closer: str = "\n }") -> str:
"""The source of the function whose declaration starts at ``header``
up to its closing brace — the house slice idiom
(``test_settings_frontend_view.py``): these view modules nest their
functions at two spaces inside ``mount``, so the first ``\\n }``
after the header closes it (nested blocks close at four).
``closer`` opens the same idiom to top-level functions (``brand.js``,
whose blocks close at column 0)."""
start = js.index(header)
end = js.find(closer, start)
assert end != -1, f"unterminated function body for {header!r}"
return js[start:end]
def _every_marker_preceded_by(body: str, marker: str, before: str) -> None:
"""Every ``marker`` occurrence sits after some ``before``
occurrence (the order pin for a body with more than one of each)."""
positions = []
start = 0
while (i := body.find(marker, start)) != -1:
positions.append(i)
start = i + 1
assert positions, f"marker {marker!r} missing from the body"
for pos in positions:
assert body.rfind(before, 0, pos) != -1, (
f"{marker!r} must never run before {before!r}"
)
# ---------------------------------------------------------------------------
# brand.js — the ONE shared pass, exported, and the boot runs IT
# ---------------------------------------------------------------------------
def test_brand_js_exports_the_shared_pass() -> None:
"""Task 03's extraction: the brand DOM lives in ONE named function
``applyBrandFromConfig(cfg)`` and reaches the page's MODULE scripts
through the layer's existing export surface — the ``window`` globals
it has always had (``window.BOR_BRAND``, ``window.BOR_CONFIG_PROMISE``).
``brand.js`` stays a classic script (no ``export`` statement —
``test_frontend_brand.py`` owns that pin), so the assignment IS the
export."""
js = _read(BRAND_JS)
assert "function applyBrandFromConfig(cfg) {" in js, (
"the shared pass is a named function taking the /api/config payload"
)
assert "window.applyBrandFromConfig = applyBrandFromConfig;" in js, (
"the shared pass is exported on the window surface settings.js / "
"theme.js already read (window.BOR_BRAND, window.BOR_CONFIG_PROMISE)"
)
def test_boot_call_site_is_the_extraction_not_a_second_copy() -> None:
"""The boot behavior is byte-identical (same request, same
assignments, same order) because the boot RUNS the shared pass:
``applyBrand`` awaits the parse-time promise and hands the settled
payload over — its own body contains NONE of the four DOM writes and
each of them occurs exactly ONCE in the file (the extraction, not a
duplication)."""
js = _read(BRAND_JS)
boot = _body(js, "function applyBrand() {", closer="\n}")
assert "applyBrandFromConfig(cfg)" in boot, "the boot calls the shared pass"
assert "BOR_CONFIG_PROMISE.then" in boot, "the boot still rides the parse-time fetch"
for token in BRAND_DOM_TOKENS:
assert token not in boot, (
f"the boot must not carry its own copy of the {token!r} write — "
"the shared pass is the ONE owner"
)
# Each of the four brand DOM writes exists exactly once in the file.
assert js.count('document.querySelectorAll(".brand-text")') == 1
assert js.count('document.querySelectorAll(".footer-text")') == 1
assert js.count('document.querySelector("#message-input")') == 1
# …and every `document.title` write sits inside the shared pass.
shared = _body(js, "function applyBrandFromConfig(cfg) {", closer="\n}")
assert js.count("document.title =") == shared.count("document.title =") >= 1
# The boot wiring is the phase-39 one (pinned in test_frontend_brand.py).
assert 'document.addEventListener("DOMContentLoaded", applyBrand)' in js
assert js.count('= fetch("/api/config"') == 1, (
"the boot fetch stays the file's only fetch — the SURFACES fetch, "
"brand.js never fetches twice"
)
def test_the_shared_pass_reapplies_over_the_name_already_shown() -> None:
"""A LIVE re-apply runs against a document whose literal passes
already rewrote the default literal into the previous name — so the
shared pass also replaces ``window.BOR_BRAND_APPLIED`` (the title,
the prose text nodes, the aria-label/placeholder/meta attributes:
the three places the literal passes touch). At boot the global is
``""`` so the clause is skipped and the boot path stays exactly the
phase-39 sequence (the byte-identical requirement)."""
js = _read(BRAND_JS)
shared = _body(js, "function applyBrandFromConfig(cfg) {", closer="\n}")
assert 'window.BOR_BRAND_APPLIED = "";' in js, (
"the previous-name global ships empty — at boot the clause never runs"
)
assert "window.BOR_BRAND_APPLIED" in shared, "the shared pass reads the last applied name"
assert "if (previous && previous !== name) {" in shared, (
"the re-apply clause is gated on a previous name that differs "
"(the boot no-op stays byte-identical)"
)
assert "document.title.replaceAll(previous, name)" in shared
assert "node.nodeValue.replaceAll(previous, name)" in shared
assert 'window.BOR_BRAND_APPLIED = name;' in shared, (
"the pass records what the document now shows, so the NEXT "
"re-apply replaces the right string"
)
# ---------------------------------------------------------------------------
# settings.js — the panel re-applies, gated on the three brand keys
# ---------------------------------------------------------------------------
def test_settings_js_settle_paths_reapply_the_brand() -> None:
"""The INVERTED task-01 pin: every settle path of the Settings panel
(Save, the per-row Reset, Clear all) re-applies the brand to the open
document through the shared pass — AFTER the phase-168 catalogue
re-GET, which stays the catalogue's own refresh (untouched). Clear
all passes all three keys (clearing hands every brand string back to
the environment); the Reset passes its own key; Save passes the keys
that actually landed — including the prefix of a batch that stopped
at a later failure (K4), because that value DID take effect."""
js = _read(SETTINGS_JS)
assert APPLY_BRAND in js, (
"task 03 inverted the task-01 absence pin: the panel now reaches "
"the shared brand pass"
)
for header in SETTINGS_SETTLE_PATHS:
body = _body(js, header)
assert "await reapplyBrand(" in body, (
f"{header} must re-apply the brand on settle"
)
assert "await loadSettings()" in body, (
f"the catalogue re-GET stays ({header}) — the phase-168 "
"refresh the re-apply EXTENDS, never replaces"
)
_every_marker_preceded_by(body, "await reapplyBrand(", "await loadSettings()")
save = _body(js, "async function saveSettings()")
assert "await reapplyBrand(saved)" in save, (
"Save re-applies the keys that landed (the K4 prefix included)"
)
assert "await reapplyBrand([key])" in _body(js, "async function resetRow("), (
"the per-row Reset re-applies the key it handed back to the env"
)
assert "await reapplyBrand(BRAND_KEYS)" in _body(
js, "async function confirmClearAll("
), "Clear all re-applies every brand key"
def _brand_keys_line(js: str) -> str:
"""The ``BRAND_KEYS`` declaration (the gate's data)."""
start = js.index("const BRAND_KEYS = [")
end = js.index("];", start)
return js[start:end]
def test_settings_js_gates_the_reapply_on_the_three_brand_keys() -> None:
"""The gate itself, in ONE helper: only ``app_name`` /
``input_placeholder`` / ``footer_text`` re-apply. A save of any of
the ~72 other rows costs no extra request and no DOM churn — and the
helper is the single place the panel reaches ``/api/config``, so the
surfaces cannot drift (the pin: exactly one shared-pass call and one
config fetch in the file)."""
js = _read(SETTINGS_JS)
helper = _body(js, "async function reapplyBrand(")
keys_line = _brand_keys_line(js)
assert tuple(BRAND_KEYS) == (
"app_name",
"input_placeholder",
"footer_text",
), "the phase-190 brand trio is the panel's own key set"
for key in BRAND_KEYS:
assert f'"{key}"' in keys_line, (
f"the gate must name {key!r} — otherwise its save never re-applies"
)
assert "if (!keys.some((key) => BRAND_KEYS.includes(key))) return;" in helper, (
"a settle that touched no brand key makes NO re-apply request"
)
assert js.count(SHARED_CALL) == 1, (
"ONE shared-pass call site in the panel — inside the helper"
)
assert js.count(CONFIG_FETCH) == 1, (
"ONE /api/config fetch in the panel module (the helper's)"
)
assert '{ cache: "no-store" }' in helper, (
"never a cached brand — the same no-store read the boot makes"
)
def test_settings_js_reapply_is_single_flight_under_the_existing_guard() -> None:
"""The §7.4 never-stale guard, EXTENDED rather than forked: the Save
button's own ``saveInFlight`` flag still owns the save (the re-apply
is awaited before it drops, so a second Save cannot start while the
config fetch is out), and the ONE flag inside the shared helper covers
the settle paths that reach it — a second Save / Reset / Clear all
never stacks a second ``/api/config`` fetch."""
js = _read(SETTINGS_JS)
helper = _body(js, "async function reapplyBrand(")
assert "let brandReapplyInFlight = false;" in js
assert "if (brandReapplyInFlight) return;" in helper, (
"one re-apply at a time — a second settle never stacks a fetch"
)
assert "brandReapplyInFlight = true;" in helper
finally_arm = helper[helper.index("} finally {") :]
assert "brandReapplyInFlight = false;" in finally_arm, (
"the flag clears in the finally — a failed re-apply can never "
"strand the guard on (the house stuck-button contract)"
)
save = _body(js, "async function saveSettings()")
guard_off = save.index("saveInFlight = false;")
assert save.index("await reapplyBrand(saved)") < guard_off, (
"the brand re-apply sits INSIDE the saveInFlight scope — the "
"existing guard's reach, not a parallel one"
)
# ---------------------------------------------------------------------------
# theme.js — the tab re-applies after Save and after Reset
# ---------------------------------------------------------------------------
def test_theme_js_save_and_reset_reapply_the_brand() -> None:
"""The Theme tab's two settle paths run the SAME shared pass after
the phase-92 palette reconcile: the tab no longer EDITS the strings
(A1) but a Reset nulls the stored row, and a legacy baked-in string
then FALLS BACK (panel row → env) — the open document must re-read
it without a reload. One code path for both surfaces' semantics
(``reapplyBrand``, called by Save and by Reset)."""
js = _read(THEME_JS)
save = _body(js, "async function saveTheme()")
reset = _body(js, "async function resetTheme()")
for name, body in (("saveTheme", save), ("resetTheme", reset)):
assert "await reapplyBrand()" in body, (
f"{name} re-applies the brand strings on success"
)
assert body.index("applyServedTheme(settings)") < body.index(
"await reapplyBrand()"
), "the palette reconcile keeps its place; the brand follows it"
assert js.count(SHARED_CALL) == 1, (
"ONE shared-pass call site in the tab module — inside its helper"
)
assert js.count(CONFIG_FETCH) == 1, (
"ONE /api/config fetch in the tab module, and the SAME statement "
"the panel uses (one shared semantics, one shape)"
)
helper = _body(js, "async function reapplyBrand()")
assert "if (brandReapplyInFlight) return;" in helper, (
"one re-apply at a time across Save and Reset (§7.4)"
)
# ---------------------------------------------------------------------------
# the failure arm — never a blanked brand (both surfaces)
# ---------------------------------------------------------------------------
def test_a_failed_reapply_leaves_the_dom_at_its_last_applied_state() -> None:
"""The phase-168 "never a blanked panel" precedent, applied to the
brand: in BOTH helpers a non-2xx returns BEFORE the shared pass runs,
and a network/JSON failure is swallowed with no DOM write at all —
the open page keeps the name, footer and placeholder it already
shows, and the save's own status / error line is the visibility."""
for path in (SETTINGS_JS, THEME_JS):
js = _read(path)
helper = _body(js, "async function reapplyBrand(")
assert "if (!r.ok) return;" in helper, (
f"{path.name}: a non-2xx /api/config must never reach the DOM"
)
assert helper.index("if (!r.ok) return;") < helper.index(
SHARED_CALL
), f"{path.name}: the shared pass runs only on a settled payload"
assert "} catch {" in helper, (
f"{path.name}: an unreachable app / non-JSON body is caught, "
"not thrown (the brand never blanks)"
)
catch_arm = helper[helper.index("} catch {") : helper.index("} finally {")]
for token in BRAND_DOM_TOKENS:
assert token not in catch_arm, (
f"{path.name}: the failure arm writes nothing ({token!r})"
)
# ---------------------------------------------------------------------------
# the ownership pins carried unchanged from task 01
# ---------------------------------------------------------------------------
def test_settings_js_never_writes_the_brand_dom_directly() -> None:
"""The shared-pass ownership pin — GREEN before, during AND after the
phase: the four brand DOM writes (title, ``.brand-text``, the composer
placeholder, ``.footer-text``) belong to the ONE brand path in
``brand.js``; the Settings panel must never duplicate one of them
inline. Task 03's live-apply goes through the shared
``applyBrandFromConfig`` call, so this file stays free of the tokens
even after the fix (no second copy of the DOM path)."""
js = _read(SETTINGS_JS)
for token in BRAND_DOM_TOKENS:
assert token not in js, (
f"settings.js must not own the brand DOM write {token!r} — "
"the shared brand.js pass is its ONE owner"
)
def test_theme_js_save_and_reset_reconcile_colors_only() -> None:
"""GREEN pre-phase AND through the phase: ``saveTheme``'s (and
``resetTheme``'s) success path calls ``applyServedTheme`` — the
phase-92 COLOR reconcile — and neither writes the brand DOM (no
title, no ``.brand-text``, no composer placeholder, no footer line).
Task 03's live brand reaches the document through the shared call,
which is exactly what this pin keeps true."""
js = _read(THEME_JS)
save = _body(js, "async function saveTheme()")
reset = _body(js, "async function resetTheme()")
assert "applyServedTheme(settings)" in save, (
"the success path keeps the phase-92 color reconcile"
)
assert "applyServedTheme(settings)" in reset, (
"the reset path keeps the phase-92 color reconcile"
)
for name, body in (("saveTheme", save), ("resetTheme", reset)):
for token in BRAND_DOM_TOKENS:
assert token not in body, (
f"{name} must stay colors-only at the DOM level — "
f"the brand write {token!r} belongs to the shared brand pass"
)