Files
brain-of-reese/tests/unit/test_settings_catalog.py
ducoterra a45f744604
Build and Push Containers / build-and-push-app (push) Successful in 2m5s
Build and Push Containers / build-and-push-db (push) Successful in 13s
phase: 194_sync_pending_count
**Phase 194 — final verification pass: all green.**

**Verified (all 5 tasks already complete; nothing re-implemented needed)**
- `app/rag/importer.py`: `_prescan_pending` + `_pending_hash_stmt` (scalar `documents.file_hash`, own session, ONE `asyncio.to_thread` span, REPLACES the stats pre-walk), `on_scan_progress`, `progress` pair = pending position, `0` branch = byte-identical phase-193 walk.
- `app/config.py`/`settings_catalog.py`/`.env.example`: `sync_pending_count=1` + validator; `app/api/sync.py`: `"scan"` phase, `scan_done`/`scan_total` (additive after `files_total`, kept at every terminal), forced/throttled RSS split; `frontend/assets/sources.js`: `fmtSyncPhaseLabel` scan branch first-arm, `fmtSyncLabel` untouched.

**Tests / lint / coverage (exact commands)**
- `uv run pytest --cov=app --cov-report=term-missing` → **4835 passed**, `app/` 99% (7240 stmts / 16 miss), exit 0
- `uv run pytest tests/e2e/test_sync_pending_count.py -v --no-cov` → **8 passed in 150.81s** (phase suite, isolated)
- 14 sync-lineage E2E suites, one pytest process each (`test_sync_remaining_count`, `…_chunk_progress`, `…_progress_box`, `…_peak_memory_visible`, `…_label_mobile`, `…_upload_progress`, `test_archive_upload_sources`, `…_image_progress`, `…_summary_visibility`, `…_status_freshness`, `test_smoke`, `test_fast_path_deferred_content`, `test_sync_session_reset`, `test_sync_git_freeze`) → **all exit 0**
- `uv run ruff check .` → All checks passed; `uv run pyright` → **0 errors**

**Completion criteria** — all met: `N remaining` = predicted change set (E2E arms 1/2/5, first import counts the walk, no-change → no count part); `scan` phase visible + flips to `import`, scan pair reset/kept at terminals (`test_sync_scan_phase.py`); `BOR_SYNC_PENDING_COUNT=0` walk semantics ("7 remaining" arm) + **negative refuses startup naming the field (verified live by importing `app.main`)**; pre-scan off-loop + cancel lands `cancelled` (integration pins); CLI/upload byte-identical (unedited pins green); payload additive (`_PRE_187_STATUS_KEYS | _MEMORY_KEYS | _SCAN_KEYS`); suite green, coverage >90%, lint/types clean.

**Documentation** — 3 fixes: README sync "States" bullet had an **unclosed parenthesis** (pre-existing, 193/194) and the scan-label "total is 0" clause was ambiguous about a `=0` run (it still counts the walk once the import starts); **AGENTS.md rule 17 had a stray `)`** (pre-existing from phase 191). Also verified consistency both directions for all 87 settings ↔ README ↔ `.env.example` ↔ settings catalog, the pages table ↔ `app/main.py` routes, no stale `BOR_` names, and README/AGENTS claims through phase 194.

**Notes/deviations** — no live-infra changes (prod sync knob ships default-on, no deployment touched); no git index/history commands run (all changes left in the working tree for the harness); commit criterion left to the harness as instructed.

**Next pending phase:** none — `.agents/phases/todo/` holds only `194_sync_pending_count`, which is now ready to move to `complete/`.
2026-10-20 06:36:21 -04:00

307 lines
14 KiB
Python

"""Phase 168 (task 02): the settings catalogue is complete and consistent.
The Settings panel (task 05) is GENERATED from
``app/core/settings_catalog.py`` — never hand-written — so these pins
are load-bearing: ``set(CATALOG) == set(Settings.model_fields)`` fails
the build the moment a new ``Settings`` field lands without its
metadata (it can never silently disappear from the panel), the group
table is closed both directions, and the capability sets are exactly
the LOCKED decisions (A3/A4).
"""
from __future__ import annotations
from app.config import Settings
from app.core.settings_catalog import (
CATALOG,
GROUPS,
NON_OVERRIDABLE,
RESTART_REQUIRED,
SECRETS,
SettingMeta,
env_var,
)
def test_catalog_covers_exactly_model_fields() -> None:
"""The completeness pin (both directions, the diff in the message)."""
catalog_keys = set(CATALOG)
field_names = set(Settings.model_fields)
missing = sorted(field_names - catalog_keys)
extra = sorted(catalog_keys - field_names)
assert not missing, (
f"Settings fields missing from the catalogue (invisible in the "
f"panel): {missing}"
)
assert not extra, (
f"Catalogue entries with no Settings field (stale): {extra}"
)
assert set(CATALOG) == set(Settings.model_fields)
def test_groups_closed_both_directions() -> None:
catalog_groups = {meta.group for meta in CATALOG.values()}
only_in_groups = sorted(set(GROUPS) - catalog_groups)
only_in_catalog = sorted(catalog_groups - set(GROUPS))
assert not only_in_groups, f"GROUPS entries with no catalogue member: {only_in_groups}"
assert not only_in_catalog, f"Catalogue groups missing from GROUPS: {only_in_catalog}"
def test_every_meta_carries_its_own_key() -> None:
for key, meta in CATALOG.items():
assert isinstance(meta, SettingMeta)
assert meta.key == key
def test_secrets_are_exactly_the_masked_set() -> None:
# LOCKED A4: write-only, masked — the 3 named secrets +
# database_url (read-only, but its URL carries credentials) +
# docs_token (phase 173, C1: the docs-push remote token reuses the
# write-only/masked mechanism, llm_api_key-style).
assert set(SECRETS) == {
"llm_api_key",
"admin_password",
"session_secret",
"database_url",
"docs_token",
}
def test_non_overridable_is_exactly_locked_a3() -> None:
# LOCKED A3: database_url builds the engine that would read the
# override (chicken-and-egg brick); embedding_dim is fixed in the
# chunks table by A6 (a dim override would corrupt retrieval).
assert set(NON_OVERRIDABLE) == {"database_url", "embedding_dim"}
def test_ef_search_row_is_a_plain_live_overridable_row() -> None:
"""Phase 188 (task 02, D3): the HNSW ``ef_search`` row sits in
``Retrieval & honesty`` (beside ``hybrid_vector_candidates`` — the two
numbers the clamp rule reads together) with the ``Vector ef_search``
label and is plain — not ``secret`` (an int), not ``restart_required``
(``_vector_candidates`` reads ``get_settings()`` per turn, so a saved
override applies on the next question), overridable (the overlay is
generic over ``int`` fields). ``0``/negative fails startup, not the
panel.
The default this row ships with is 400, not the A7 candidate count it
launched at: task 04's live gate measured the graph recovering the exact
top-100 for 0.9814 of rows on average (0.94 at worst) at 100 against 1.0000
at 400, on the restored live KB with the vectors pinned
(``benchmarks/phase188_live_gate/``). It stays live-overridable in either
direction, which is what makes that a tuning decision and not a redeploy.
"""
meta = CATALOG["retrieval_ef_search"]
assert meta.group == "Retrieval & honesty"
assert meta.label == "Vector ef_search"
assert meta.secret is False
assert meta.restart_required is False
assert meta.overridable is True
assert "retrieval_ef_search" not in SECRETS
assert "retrieval_ef_search" not in RESTART_REQUIRED
assert "retrieval_ef_search" not in NON_OVERRIDABLE
assert Settings.model_fields["retrieval_ef_search"].annotation is int
assert Settings.model_fields["retrieval_ef_search"].default == 400
def test_restart_required_is_the_boot_wired_set() -> None:
# LOCKED A4: logging, the gzip middleware (compress_min_bytes), the
# static mount (static_dir), create_engine (db_pool_*), and
# SessionMiddleware (session_*).
assert set(RESTART_REQUIRED) == {
"log_level",
"compress_min_bytes",
"static_dir",
"db_pool_size",
"db_pool_max_overflow",
"db_pool_recycle",
"session_secret",
"session_max_age",
"session_cookie",
}
def test_env_var_is_bare_key_upper_prefixed() -> None:
assert env_var("relevance_threshold") == "BOR_RELEVANCE_THRESHOLD"
for key in CATALOG:
assert env_var(key).startswith("BOR_")
def test_llm_thinking_levels_are_plain_live_overridable_rows() -> None:
"""Phase 178 (task 01): the two thinking-level rows (the phase-172
knob, renamed + the new summary level) sit in ``LLM (aipi)`` with
the ``Chat thinking level`` / ``Summary thinking level`` labels and
are plain — not ``secret`` (free-form text values, like
``llm_chat_model``), not ``restart_required`` (the LLM client
reads the settings per request via ``get_settings()`` — live),
overridable (the overlay is generic over ``str`` fields)."""
for key, label in (
("llm_chat_thinking_level", "Chat thinking level"),
("llm_summary_thinking_level", "Summary thinking level"),
):
meta = CATALOG[key]
assert meta.group == "LLM (aipi)"
assert meta.label == label
assert meta.secret is False
assert key not in SECRETS
assert key not in RESTART_REQUIRED
assert key not in NON_OVERRIDABLE
def test_docs_user_is_a_plain_live_overridable_row() -> None:
"""Phase 173 (task 01, C1/C5): ``docs_user`` sits in ``Docs push``
with the ``Docs user`` label and is plain — not ``secret`` (a
plain username), not ``restart_required`` (the push service reads
the setting per push via ``get_settings()`` — live), overridable
(the overlay is generic over ``str`` fields). An empty value is a
valid state (C5: the anonymous push)."""
meta = CATALOG["docs_user"]
assert meta.group == "Docs push"
assert meta.label == "Docs user"
assert meta.secret is False
assert "docs_user" not in SECRETS
assert "docs_user" not in RESTART_REQUIRED
assert "docs_user" not in NON_OVERRIDABLE
def test_docs_token_is_a_secret_live_overridable_row() -> None:
"""Phase 173 (task 01, C1): ``docs_token`` has the ``Docs token``
label and ``secret=True`` — write-only end-to-end via the EXISTING
derived mechanism (``SECRETS`` picks it up automatically; the API
reports ``is_set`` + ``value=null``, a blank PUT 422s, the panel
renders it masked, zero ``settings.js`` code). Live (the push
service reads it per push), overridable (generic ``str`` overlay)."""
meta = CATALOG["docs_token"]
assert meta.group == "Docs push"
assert meta.label == "Docs token"
assert meta.secret is True
assert "docs_token" in SECRETS
assert "docs_token" not in RESTART_REQUIRED
assert "docs_token" not in NON_OVERRIDABLE
def test_verbatim_display_rows_are_plain_live_overridable() -> None:
"""Phase 182 (task 01, LOCKED A8): the three verbatim-display rows sit
in ``Agent tools`` (the ``read_max_chars`` group, beside it) with the
``Answer text`` / ``Show text max chars`` / ``Show text max blocks``
labels and are plain — not ``secret`` (a bool and two ints), not
``restart_required`` (the chat request path reads them per turn via
``get_settings()`` — live), overridable (the overlay is generic over
``bool``/``int`` fields). ``answer_text`` is the master switch and the
two budgets accept ``0`` (inert) — a negative value fails startup, not
the panel."""
for key, label in (
("answer_text", "Answer text"),
("show_text_max_chars", "Show text max chars"),
("show_text_max_blocks", "Show text max blocks"),
):
meta = CATALOG[key]
assert meta.group == "Agent tools"
assert meta.label == label
assert meta.secret is False
assert meta.restart_required is False
assert meta.overridable is True
assert key not in SECRETS
assert key not in RESTART_REQUIRED
assert key not in NON_OVERRIDABLE
def test_verbatim_display_field_types() -> None:
"""Phase 182 (task 01): the master switch is a ``bool`` and the two
budgets are ``int``s with the LOCKED A8 defaults (``True`` /
``12000`` / ``5``) — the types the panel renders its input for and the
overlay coerces from the stored raw string."""
assert Settings.model_fields["answer_text"].annotation is bool
assert Settings.model_fields["show_text_max_chars"].annotation is int
assert Settings.model_fields["show_text_max_blocks"].annotation is int
assert Settings.model_fields["answer_text"].default is True
assert Settings.model_fields["show_text_max_chars"].default == 12_000
assert Settings.model_fields["show_text_max_blocks"].default == 5
def test_sync_session_bound_rows_are_plain_live_overridable() -> None:
"""Phase 184 (task 01, LOCKED A3/D2): the two sync-run knobs sit in
``Import & sources`` (beside ``upload_max_mb`` — they tune the sync, not
the walk's content) with the ``Sync session reset files`` / ``Sync prune
batch`` labels and are plain — not ``secret`` (two ints), not
``restart_required`` (the importer reads ``llm.settings`` per run, so a
saved override applies to the NEXT sync), overridable (the overlay is
generic over ``int`` fields). Both accept ``0`` = the exact pre-phase
behaviour (the no-redeploy rollback); a negative value fails startup,
not the panel."""
for key, label in (
("sync_session_reset_files", "Sync session reset files"),
("sync_prune_batch", "Sync prune batch"),
):
meta = CATALOG[key]
assert meta.group == "Import & sources"
assert meta.label == label
assert meta.secret is False
assert meta.restart_required is False
assert meta.overridable is True
assert key not in SECRETS
assert key not in RESTART_REQUIRED
assert key not in NON_OVERRIDABLE
def test_sync_session_bound_field_types() -> None:
"""Phase 184 (task 01, D2): both knobs are ``int``s with the LOCKED A3
defaults (``200`` files / ``500`` documents) — the type the panel renders
its input for and the overlay coerces from the stored raw string."""
assert Settings.model_fields["sync_session_reset_files"].annotation is int
assert Settings.model_fields["sync_prune_batch"].annotation is int
assert Settings.model_fields["sync_session_reset_files"].default == 200
assert Settings.model_fields["sync_prune_batch"].default == 500
def test_sync_pending_count_row_is_a_plain_live_overridable_row() -> None:
"""Phase 194 (task 01, D2): the pre-scan switch sits in ``Import &
sources`` beside the other two sync-run knobs with the ``Sync pending
count`` label and is plain — not ``secret`` (an int), not
``restart_required`` (the importer reads ``llm.settings`` once per run, so
a saved override applies to the NEXT sync), overridable (the overlay is
generic over ``int`` fields). ``1`` is the shipped default — the pending
count is the FIX the owner asked for, so it ships on and the phase-193
walk position is the ``0`` rollback; a negative value fails startup, not
the panel."""
meta = CATALOG["sync_pending_count"]
assert meta.group == "Import & sources"
assert meta.label == "Sync pending count"
assert meta.secret is False
assert meta.restart_required is False
assert meta.overridable is True
assert "sync_pending_count" not in SECRETS
assert "sync_pending_count" not in RESTART_REQUIRED
assert "sync_pending_count" not in NON_OVERRIDABLE
assert Settings.model_fields["sync_pending_count"].annotation is int
assert Settings.model_fields["sync_pending_count"].default == 1
def test_source_name_hit_bonus_row_is_a_plain_live_overridable_row() -> None:
"""Phase 191 (task 04, D3): the second selection-bonus row sits in
``Retrieval & honesty`` (right beside ``name_hit_bonus`` — the two
independently kill-able knobs) with the ``Source name hit bonus``
label and mirrors the ``name_hit_bonus`` entry's flags exactly:
plain — not ``secret`` (a float), not ``restart_required`` (the
selection walk reads ``get_settings()`` per turn, so a saved
override applies on the next question), overridable (the overlay is
generic over ``float`` fields). ``0`` is the byte-identical kill
switch; a negative value fails startup, not the panel."""
meta = CATALOG["source_name_hit_bonus"]
assert meta.group == "Retrieval & honesty"
assert meta.label == "Source name hit bonus"
assert meta.secret is False
assert meta.restart_required is False
assert meta.overridable is True
assert "source_name_hit_bonus" not in SECRETS
assert "source_name_hit_bonus" not in RESTART_REQUIRED
assert "source_name_hit_bonus" not in NON_OVERRIDABLE
assert Settings.model_fields["source_name_hit_bonus"].annotation is float
assert Settings.model_fields["source_name_hit_bonus"].default == 0.005
def test_labels_nonempty_and_unique() -> None:
labels = [meta.label for meta in CATALOG.values()]
assert all(label.strip() for label in labels), "empty label in the catalogue"
duplicates = sorted({label for label in labels if labels.count(label) > 1})
assert not duplicates, f"duplicate labels: {duplicates}"