"""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_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}"