Files
brain-of-reese/tests/integration/test_admin_settings_api.py
T
ducoterra a86caec821
Build and Push Containers / build-and-push-app (push) Successful in 1m41s
Build and Push Containers / build-and-push-db (push) Successful in 11s
phase: 173_docs_push_creds_split
All gates green. Final report:

**Phase 173 — final verification pass (all tasks already complete in `complete/`)**

- Verified the full implementation is in the working tree: `docs_user`/`docs_token` settings + catalogue rows (task 01), `docs_repo` PUT 422 guard + `docs_token` write-only pin (task 02), `run_git(env)` + `push_document(user, token)` credential env (task 03), `settings_migrate.py` wired into `create_app()` (task 04), E2E suite (task 05).
- **Completion criteria:**
  1. GET `/api/admin/settings` lists `docs_user` (plain) + `docs_token` (secret, `value=null`), token never in any body — ✅ `test_docs_token_is_write_only_masked_end_to_end` + E2E sentinel-absent-from-DOM
  2. PUT `docs_repo` with `://user:pass@` → 422 naming the fields; clean URL passes — ✅ integration + E2E UI-side
  3. Token set → `GIT_CURL_USERNAME`/`GIT_CURL_PASSWORD`/`GIT_TERMINAL_PROMPT=0` on every git step; empty → `env=None` — ✅ mocked-git unit tests
  4. Legacy env split once at startup, idempotent, one INFO line, `.env` untouched — ✅ `test_settings_migrate.py` (4 shapes) + `test_docs_push_migrate.py`
  5. Full suite green, coverage >90%, lint clean — ✅
  6. E2E isolated: `test_docs_push_creds.py` 4/4; regression set (panel + layout + response_to_docs) 21/21 — ✅
  7. Commit + phase move — harness's job (per instructions, not mine).
  8. README/AGENTS consistent — ✅ (below)
- Tests/lint: `uv run pytest` exit 0 (4112 passed) · `uv run pytest --cov=app --cov-report=term-missing` TOTAL 99% · `uv run ruff check .` clean · `uv run pyright` 0 errors
- Docs: README.md (incl. Docs-push config section, guard/migration paragraph, `.env.example` block) and AGENTS.md verified consistent in full; fixed two stale docstrings in `app/config.py` + `app/core/settings_catalog.py` (`is_set` → the API's actual `has_value` field).
- No live-infrastructure changes (no live step in this phase). No next pending phase — `todo/` held only 173.
2026-10-10 16:47:39 -04:00

768 lines
35 KiB
Python

"""Integration: the admin settings panel's read/write surface (phase 168,
task 04) — ``/api/admin/settings`` GET / PUT ``/{key}`` / DELETE
``/{key}`` / DELETE all, driven through the real app (TestClient keeps
the cookie jar — the house ``test_ui_settings_api`` /
``test_tokens_api`` pattern).
The pins (00_phase, Testing & Quality):
* the auth matrix — anonymous AND a signed-in token USER get 403 on ALL
four routes (the router-wide ``require_admin`` closes the surface on
every method), the admin 200s on all four;
* the shape — ``items`` length == ``len(Settings.model_fields)`` (78),
groups in ``GROUPS`` order (the stable order the E2E pins against),
``totals.count`` / ``overridden`` correct, ``overlay_enabled`` true;
on an empty table a field whose ``BOR_*`` var the test env sets
reports ``source="env"``, an untouched one ``source="default"`` and
none reports ``"db"``;
* write → live — a saved ``relevance_threshold`` is in
``get_settings()`` with NO restart (the cache clear + task-03 hooks
are the whole job), the row is in the table with a non-null
``updated_at``, and the DB value reaches the ANONYMOUS
``/api/config`` endpoint through the same overlay;
* errors — unknown key 404; the two LOCKED A3 keys 422 naming the env
var; a bad shape 422 naming the field with NOTHING stored; a
cross-field pair that only breaks in combination (the second PUT 422s
and the first row is unchanged);
* masking (LOCKED A4) — a stored secret reports ``masked``/``has_value``
only, the plaintext appears NOWHERE in the raw response text, a blank
secret PUT 422s, and a DELETE clears it;
* reset — a DELETE ``/{key}`` restores the env value (idempotent);
DELETE ``""`` clears everything (``{"cleared": 3}``);
* the kill switch (D6) — ``BOR_SETTINGS_DB=0``: ``overlay_enabled``
false, a PUT still stores the row, ``get_settings()`` still reports
the env value — the switch disables the mechanism, not the panel;
* the byte budget — the whole catalogue stays under the phase-167 INFO
bar (64 KB), so it never floods the log;
* fail-soft boot — a malformed row inserted DIRECTLY into the table
(bypassing the API's 422 gate) never bricks: a fresh ``create_app()``
runs with it in the table, the row is skipped with a logged reason,
the rest of the table still applies, and ``/api/health`` is 200.
The module's teardown leaves NO row behind (the same discipline
``test_admin_theme_tab.py`` applies to ``ui_settings`` — a surviving
row would retune every later suite through the overlay).
Real Postgres (``podman compose up -d db``); no LLM involved.
Requires: podman compose up -d db
"""
from __future__ import annotations
import logging
from collections.abc import Iterator
import pytest
from fastapi.testclient import TestClient
from sqlalchemy import text
from sqlalchemy.orm import Session
from app.api import chat as chat_api
from app.config import Settings, get_settings, invalidate_settings
from app.core import settings_overlay
from app.core.settings_catalog import CATALOG, GROUPS
from app.core.settings_overlay import register_invalidator
from app.main import app as fastapi_app
from app.main import create_app
from tests.conftest import ADMIN_PASSWORD
API = "/api/admin/settings"
@pytest.fixture(autouse=True)
def clean_state(
db: Session, monkeypatch: pytest.MonkeyPatch
) -> Iterator[None]:
"""A clean ``app_settings`` table + the overlay switch ON + a fresh
overlay/hook slate (the ``test_admin_settings_live`` idiom: reset
the process-wide hook list and re-wire what ``create_app()``
wires, so a save in this module runs the chat runtime reset).
Teardown deletes the rows + invalidates + clears the cache — NO row
may survive this module (a surviving row would retune every later
suite through the overlay).
"""
settings_overlay.reset_state_for_tests()
register_invalidator(chat_api.reset_runtime_caches)
db.execute(text("DELETE FROM app_settings"))
db.commit()
monkeypatch.setenv("BOR_SETTINGS_DB", "1")
invalidate_settings()
yield
db.execute(text("DELETE FROM app_settings"))
db.commit()
invalidate_settings()
get_settings.cache_clear()
def _admin_client() -> TestClient:
"""A fresh client signed in as the admin (the ``_admin_client``
pattern from ``test_ui_settings_api``)."""
c = TestClient(fastapi_app)
r = c.post("/api/login", json={"password": ADMIN_PASSWORD})
assert r.status_code == 204, f"admin login failed: {r.status_code} {r.text}"
return c
def _token_user_client(admin: TestClient, anon: TestClient) -> TestClient:
"""A signed-in token USER (role ``"user"`` — issue the token the way
``test_tokens_api`` does): the only non-admin identity, 403 on this
surface on every method (the phase-79 token matrix contract)."""
r = admin.post("/api/tokens", json={"label": "alice"})
assert r.status_code == 201, r.text
token = r.json()["token"]
assert anon.post("/api/token-auth", json={"token": token}).status_code == 204
return anon
def _item(body: dict, key: str) -> dict:
"""One catalogue item by key (the GET body's ``items`` list)."""
for item in body["items"]:
if item["key"] == key:
return item
raise AssertionError(f"catalogue item missing: {key}")
def test_auth_matrix_all_four_routes(client: TestClient, db: Session) -> None:
"""Anonymous AND token user: 403 ``admin only`` on ALL four routes
(the router-wide dependency closes the surface on every method);
the admin passes on all four."""
admin = _admin_client()
token_user = _token_user_client(admin, client)
for caller in (client, token_user):
r = caller.get(API)
assert r.status_code == 403 and r.json() == {"detail": "admin only"}
r = caller.put(f"{API}/relevance_threshold", json={"value": "0.5"})
assert r.status_code == 403 and r.json() == {"detail": "admin only"}
r = caller.delete(f"{API}/relevance_threshold")
assert r.status_code == 403 and r.json() == {"detail": "admin only"}
r = caller.delete(API)
assert r.status_code == 403 and r.json() == {"detail": "admin only"}
r = admin.get(API)
assert r.status_code == 200
r = admin.put(f"{API}/top_n_docs", json={"value": "3"})
assert r.status_code == 200, r.text
r = admin.delete(f"{API}/top_n_docs")
assert r.status_code == 200, r.text
r = admin.delete(API)
assert r.status_code == 200, r.text
assert r.json() == {"cleared": 0}
assert db.execute(text("SELECT count(*) FROM app_settings")).scalar() == 0
def test_catalogue_shape_and_sources_on_empty_table(admin_client) -> None:
"""The whole catalogue, grouped and stable: 78 items (exactly
``Settings.model_fields``), groups in ``GROUPS`` order (each group a
contiguous block, the block order = ``GROUPS``), ``totals`` correct,
``overlay_enabled`` true. On an empty table: the env-var-set field
reports ``"env"``, the untouched one ``"default"`` and NOTHING
reports ``"db"``."""
r = admin_client.get(API)
assert r.status_code == 200, r.text
body = r.json()
items = body["items"]
assert len(items) == len(Settings.model_fields)
assert body["totals"] == {
"count": len(Settings.model_fields),
"overridden": 0,
}
assert body["overlay_enabled"] is True
# The stable order: each group a contiguous block, block order GROUPS.
groups_seen: list[str] = []
for item in items:
assert item["group"] in GROUPS
if not groups_seen or groups_seen[-1] != item["group"]:
groups_seen.append(item["group"])
assert groups_seen == [g for g in GROUPS if g in groups_seen]
assert len(groups_seen) == len(set(groups_seen))
# Every item carries the full contract (the panel generates from it).
for item in items:
assert set(item) == {
"key", "env_var", "group", "label", "type", "secret",
"restart_required", "overridable", "source", "value",
"masked", "has_value", "has_override", "db_value", "default",
}
assert item["env_var"] == "BOR_" + item["key"].upper()
assert item["source"] in ("db", "env", "default")
# Empty table: env / default / never db.
sources = {item["key"]: item["source"] for item in items}
assert "db" not in sources.values()
assert sources["input_placeholder"] == "env" # BOR_… set in tests/conftest.py
assert sources["docs_work_dir"] == "default" # no BOR_… in the test env
assert sources["relevance_threshold"] == "env" # pinned 0.30 in conftest
def test_write_is_live_without_restart(admin_client, db: Session) -> None:
"""Write → live (no restart): the saved value is in ``get_settings()``
in the SAME process, the row is in the table with a non-null
``updated_at``, the item reports ``source="db"`` and
``totals.overridden == 1`` — and the DB value reaches the ANONYMOUS
``/api/config`` endpoint through the same overlay (the DB-over-env
rule is deployment-wide, not panel-local)."""
assert get_settings().relevance_threshold == 0.30 # the conftest env value
r = admin_client.put(f"{API}/relevance_threshold", json={"value": "0.5"})
assert r.status_code == 200, r.text
body = r.json()
assert body["key"] == "relevance_threshold"
assert body["source"] == "db"
assert body["has_override"] is True
assert body["db_value"] == "0.5"
assert get_settings().relevance_threshold == 0.5 # NO restart (task 03)
row = db.execute(
text("SELECT value, updated_at FROM app_settings WHERE key = 'relevance_threshold'")
).first()
assert row is not None
assert row[0] == "0.5"
assert row[1] is not None # the server-side now()
r = admin_client.get(API)
assert r.json()["totals"]["overridden"] == 1
# Visible through the public config endpoint (no auth) — the same
# overlay retunes every consumer at once (D2).
anon = TestClient(fastapi_app)
r = anon.get("/api/config")
assert r.status_code == 200
assert r.json()["input_placeholder"] == "Ask me anything…"
r = admin_client.put(f"{API}/input_placeholder", json={"value": "Type your question…"})
assert r.status_code == 200, r.text
r = anon.get("/api/config")
assert r.status_code == 200
assert r.json()["input_placeholder"] == "Type your question…"
def test_put_errors_404_and_422(admin_client, db: Session) -> None:
"""The write-path guards (LOCKED A5, A3, A4): unknown key 404; the
two A3 keys 422 naming the env var; a bad shape 422 naming the FIELD
with NOTHING stored; a cross-field pair that only breaks in
combination — the second PUT 422s and the first row is unchanged."""
r = admin_client.put(f"{API}/bogus_key", json={"value": "1"})
assert r.status_code == 404
assert r.json() == {"detail": "Unknown setting bogus_key"}
r = admin_client.put(f"{API}/database_url", json={"value": "postgresql://x"})
assert r.status_code == 422
assert "BOR_DATABASE_URL is not settable from the database" in r.json()["detail"]
r = admin_client.put(f"{API}/embedding_dim", json={"value": "384"})
assert r.status_code == 422
assert "BOR_EMBEDDING_DIM is not settable from the database" in r.json()["detail"]
# A bad shape: names the field, stores nothing.
r = admin_client.put(f"{API}/relevance_threshold", json={"value": "abc"})
assert r.status_code == 422
assert "relevance_threshold" in r.json()["detail"]
assert db.execute(text("SELECT count(*) FROM app_settings")).scalar() == 0
# The one list[str] field (K2): a malformed JSON array is a 422
# naming the field (the _coerce ValueError branch), nothing stored.
r = admin_client.put(f"{API}/suggestions", json={"value": "[not json"})
assert r.status_code == 422
assert "suggestions" in r.json()["detail"]
assert db.execute(text("SELECT count(*) FROM app_settings")).scalar() == 0
# A well-formed JSON array stores fine (the JSON round-trip works).
r = admin_client.put(
f"{API}/suggestions", json={"value": '["How do I deploy gitea?"]'}
)
assert r.status_code == 200, r.text
assert r.json()["db_value"] == '["How do I deploy gitea?"]'
assert db.execute(
text("SELECT value FROM app_settings WHERE key = 'suggestions'")
).scalar() == '["How do I deploy gitea?"]'
# (Clean up the row — the cross-field pin below counts rows.)
assert admin_client.delete(f"{API}/suggestions").status_code == 200
# The cross-field pair (only breaks in combination): store
# relevance_threshold=0.2 (valid alone), then lexical_support_floor=0.9
# — above the DB-overridden threshold: the SECOND PUT 422s and the
# first row is unchanged.
r = admin_client.put(f"{API}/relevance_threshold", json={"value": "0.2"})
assert r.status_code == 200, r.text
r = admin_client.put(f"{API}/lexical_support_floor", json={"value": "0.9"})
assert r.status_code == 422
assert "lexical_support_floor" in r.json()["detail"]
value = db.execute(
text("SELECT value FROM app_settings WHERE key = 'relevance_threshold'")
).scalar()
assert value == "0.2" # the first row is unchanged
assert db.execute(text("SELECT count(*) FROM app_settings")).scalar() == 1
# The failed write did not disturb the live view.
assert get_settings().relevance_threshold == 0.2
def test_secrets_are_masked_and_write_only(admin_client, db: Session) -> None:
"""LOCKED A4: a stored secret reports ``masked``/``has_value`` only —
``value``/``db_value`` are ``None`` and the plaintext appears NOWHERE
in the raw response text (asserted on ``response.text``, not the
parsed model); a blank secret PUT 422s (a self-lockout — DELETE is
the explicit clear); a DELETE clears it."""
db.execute(
text("INSERT INTO app_settings (key, value) VALUES ('llm_api_key', :v)"),
{"v": "sk-secret-12345"},
)
db.commit()
invalidate_settings()
r = admin_client.get(API)
assert r.status_code == 200
item = _item(r.json(), "llm_api_key")
assert item["masked"] is True
assert item["value"] is None
assert item["db_value"] is None
assert item["has_value"] is True # a non-empty value is in effect
assert item["has_override"] is True
assert item["source"] == "db"
assert item["default"] == "" # masked: the code default is never shown
assert "sk-secret-12345" not in r.text # NOWHERE in the raw body
# The other write-only fields are masked too (set in the test env).
for key in ("admin_password", "session_secret", "database_url"):
other = _item(r.json(), key)
assert other["masked"] is True and other["value"] is None
# A blank secret PUT is a 422 (an empty API key / session secret
# would self-lockout — DELETE is the explicit clear).
r = admin_client.put(f"{API}/llm_api_key", json={"value": " "})
assert r.status_code == 422
assert "BOR_LLM_API_KEY must not be blank" in r.json()["detail"]
assert db.execute(
text("SELECT value FROM app_settings WHERE key = 'llm_api_key'")
).scalar() == "sk-secret-12345" # unchanged
# A DELETE clears the row — the effective value falls back to the
# env value (whatever this deployment's env/.env provides — the
# test only pins that it is NOT the stored secret and that the
# item reports the fallback state).
r = admin_client.delete(f"{API}/llm_api_key")
assert r.status_code == 200
assert r.json()["source"] == "default" # no BOR_LLM_API_KEY in the PROCESS env
assert r.json()["has_override"] is False
assert r.json()["value"] is None
assert r.json()["has_value"] == bool(get_settings().llm_api_key)
assert get_settings().llm_api_key != "sk-secret-12345" # the override is gone
assert db.execute(text("SELECT count(*) FROM app_settings")).scalar() == 0
def test_reset_to_env_and_clear_all(admin_client, db: Session) -> None:
"""Reset semantics: DELETE ``/{key}`` deletes the row (idempotent — a
double Reset is a success, not a 404) and the env value wins again;
DELETE ``""`` clears every row (``{"cleared": 3}``) and
``totals.overridden`` is back to 0."""
r = admin_client.put(f"{API}/relevance_threshold", json={"value": "0.5"})
assert r.status_code == 200, r.text
assert get_settings().relevance_threshold == 0.5
r = admin_client.delete(f"{API}/relevance_threshold")
assert r.status_code == 200, r.text
assert r.json()["source"] == "env" # BOR_RELEVANCE_THRESHOLD is set (0.30)
assert r.json()["has_override"] is False
assert get_settings().relevance_threshold == 0.30 # the env value wins again
# Idempotent: the double Reset is a success, not a 404.
r = admin_client.delete(f"{API}/relevance_threshold")
assert r.status_code == 200, r.text
# Three rows, then "Clear all overrides".
for key, value in (("relevance_threshold", "0.5"), ("llm_retries", "7"), ("top_n_docs", "3")):
r = admin_client.put(f"{API}/{key}", json={"value": value})
assert r.status_code == 200, r.text
assert db.execute(text("SELECT count(*) FROM app_settings")).scalar() == 3
r = admin_client.delete(API)
assert r.status_code == 200
assert r.json() == {"cleared": 3}
assert db.execute(text("SELECT count(*) FROM app_settings")).scalar() == 0
assert get_settings().relevance_threshold == 0.30 # env values win again
r = admin_client.get(API)
assert r.json()["totals"]["overridden"] == 0
def test_kill_switch_disables_the_mechanism_not_the_panel(
admin_client, db: Session, monkeypatch: pytest.MonkeyPatch
) -> None:
"""D6: with ``BOR_SETTINGS_DB=0`` the catalogue reports
``overlay_enabled`` false (the panel shows a banner instead of
pretending a save took effect), a PUT STILL stores the row, and
``get_settings()`` still reports the ENV value — the switch disables
the mechanism, not the panel."""
r = admin_client.put(f"{API}/relevance_threshold", json={"value": "0.5"})
assert r.status_code == 200, r.text
assert get_settings().relevance_threshold == 0.5 # the switch was ON
# Deterministic env base for the pin (machine-independent):
monkeypatch.setenv("BOR_LLM_RETRIES", "3")
invalidate_settings()
monkeypatch.setenv("BOR_SETTINGS_DB", "0")
invalidate_settings()
r = admin_client.get(API)
assert r.status_code == 200
assert r.json()["overlay_enabled"] is False
assert r.json()["totals"]["overridden"] == 0 # no row is IN EFFECT
item = _item(r.json(), "relevance_threshold")
assert item["source"] == "env" # the row exists but is not in effect
assert item["has_override"] is True
assert item["db_value"] == "0.5" # the stored string is visible (non-secret)
assert get_settings().relevance_threshold == 0.30 # the env value stands
# The panel stays writable: the PUT stores the row …
r = admin_client.put(f"{API}/llm_retries", json={"value": "7"})
assert r.status_code == 200, r.text
assert db.execute(text("SELECT count(*) FROM app_settings")).scalar() == 2
# … but the env value stays in effect while the switch is off.
assert get_settings().llm_retries == 3 # the env value (pinned 3 above)
def test_catalogue_stays_under_the_byte_budget(admin_client) -> None:
"""The whole catalogue stays under the phase-167 INFO bar (64 KB —
``BOR_LOG_LARGE_RESPONSE_BYTES``), so ``GET /api/admin/settings``
never floods the log (78 items ≈ 20 KB; the audit script re-verifies
the 256 KB budget in task 06)."""
r = admin_client.get(API)
assert r.status_code == 200
assert len(r.content) < 65_536
def test_unknown_delete_is_a_404(admin_client) -> None:
"""A DELETE of an UNKNOWN SETTING is a 404 like the PUT (a typo in
the key is a client bug; the idempotency pin is about a known key
whose row is already gone — the double Reset in
``test_reset_to_env_and_clear_all``)."""
r = admin_client.delete(f"{API}/bogus_key")
assert r.status_code == 404
assert r.json() == {"detail": "Unknown setting bogus_key"}
def test_write_log_lines_carry_never_the_value(
admin_client, caplog: pytest.LogCaptureFixture
) -> None:
"""AGENTS rule 10 / A4: one line per write (``settings action=…`` on
the ``app.api.admin_settings`` logger) carrying the key and the
action — NEVER the value (a secret write must not leak the
plaintext into the log)."""
caplog.set_level(logging.INFO, logger="app.api.admin_settings")
r = admin_client.put(f"{API}/llm_api_key", json={"value": "sk-log-secret-xyz"})
assert r.status_code == 200, r.text
set_lines = [rec.message for rec in caplog.records
if rec.name == "app.api.admin_settings"]
assert "settings action=set key=llm_api_key status=200" in set_lines
assert "sk-log-secret-xyz" not in "\n".join(set_lines)
caplog.clear()
r = admin_client.delete(f"{API}/llm_api_key")
assert r.status_code == 200
assert "settings action=clear key=llm_api_key" in [
rec.message for rec in caplog.records
if rec.name == "app.api.admin_settings"
]
caplog.clear()
admin_client.put(f"{API}/top_n_docs", json={"value": "3"})
r = admin_client.delete(API)
assert r.status_code == 200 and r.json() == {"cleared": 1}
assert "settings action=clear_all count=1" in [
rec.message for rec in caplog.records
if rec.name == "app.api.admin_settings"
]
def test_bad_row_inserted_directly_never_bricks_boot(
db: Session, caplog: pytest.LogCaptureFixture
) -> None:
"""Completion criterion (00_phase, item 5): a malformed row written
straight into ``app_settings`` (bypassing the API's 422 gate — the
operator SQL, the failed migration) must never brick boot. A FRESH
``create_app()`` — the full boot path: fail-loud auth gate, resolver
prime, middleware wiring — runs with the bad row present, the row is
SKIPPED with a logged reason, the rest of the table still applies,
and ``/api/health`` is 200 (A5)."""
# One bad row (the API's 422 gate would refuse it) + one good row:
# the bad one is skipped, the good one stands.
db.execute(text(
"INSERT INTO app_settings (key, value)"
" VALUES ('relevance_threshold', 'not-a-number')"
))
db.execute(text(
"INSERT INTO app_settings (key, value) VALUES ('top_n_docs', '3')"
))
db.commit()
caplog.set_level(logging.ERROR, logger="app.settings")
# The boot path with the bad row in the table — must not raise.
app = create_app()
s = get_settings()
env_only = Settings() # the env value the skipped key falls back to
assert s.relevance_threshold == env_only.relevance_threshold
assert s.top_n_docs == 3 # the rest of the table still applies
assert any(
"settings override ignored" in rec.message
and "relevance_threshold" in rec.message
for rec in caplog.records
)
r = TestClient(app).get("/api/health")
assert r.status_code == 200
def test_catalog_covers_every_settings_field(admin_client) -> None:
"""The catalogue's keys are exactly ``Settings.model_fields`` (the
API-level face of the unit completeness pin — a new setting cannot
be missing from the panel's data)."""
r = admin_client.get(API)
assert r.status_code == 200
assert {item["key"] for item in r.json()["items"]} == set(Settings.model_fields)
assert set(CATALOG) == set(Settings.model_fields)
def test_llm_thinking_level_row_contract(
admin_client: TestClient, db: Session, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Phase 172 (task 02): the new ``llm_thinking_level`` row behaves
exactly like ``llm_chat_model`` — the machinery is generic over
the catalogue (D4 / A5), so the per-key contract is: (a) present in
the GET body (group ``LLM (aipi)``, ``str``, effective ``low``
with no row in ``app_settings``, ``env_var`` named); (b) PUT stores
the RAW string in ``app_settings`` (the overlay's env-native
string form) and the response reports the new effective value;
(c) the EMPTY value is storable (the off switch, A2/A4 — the API
stores as-is, no allowlist); (d) DELETE resets to the env value;
(e) the non-admin 403 gate applies (asserted once here; the full
four-route matrix is ``test_auth_matrix_all_four_routes``).
``test_catalogue_shape_and_sources_on_empty_table`` +
``test_catalog_covers_every_settings_field`` already pin the row's
PRESENCE generically — these cases pin the per-key behavior.
"""
# A deterministic base (machine-independent, the kill-switch test's
# idiom): the code default, no row in the table.
monkeypatch.delenv("BOR_LLM_THINKING_LEVEL", raising=False)
invalidate_settings()
assert get_settings().llm_thinking_level == "low"
# (a) the row: group, type, env var, effective ``low`` with NO row
# in app_settings, not secret, not restart-required (the LLM layer
# reads the setting per request — live).
r = admin_client.get(API)
assert r.status_code == 200, r.text
item = _item(r.json(), "llm_thinking_level")
assert item["group"] == "LLM (aipi)"
assert item["label"] == "Thinking level"
assert item["type"] == "str"
assert item["env_var"] == "BOR_LLM_THINKING_LEVEL"
assert item["secret"] is False
assert item["restart_required"] is False
assert item["value"] == "low"
assert item["source"] in ("default", "env") # no row in app_settings
assert item["has_override"] is False
assert item["db_value"] is None
assert item["default"] == "low"
# (b) PUT stores the RAW string (the overlay's D1 — env-native
# string form) and the response reports the new effective value.
r = admin_client.put(f"{API}/llm_thinking_level", json={"value": "high"})
assert r.status_code == 200, r.text
assert r.json()["value"] == "high"
assert r.json()["source"] == "db"
assert r.json()["has_override"] is True
assert r.json()["db_value"] == "high"
assert db.execute(
text("SELECT value FROM app_settings WHERE key = 'llm_thinking_level'")
).scalar() == "high" # stored raw, exactly as sent
assert get_settings().llm_thinking_level == "high" # live, no restart
# (c) the EMPTY value is storable (the off switch, A2/A4 — the
# API stores as-is; no allowlist, no value validation): the row
# holds the empty string and the effective value is ``""``.
r = admin_client.put(f"{API}/llm_thinking_level", json={"value": ""})
assert r.status_code == 200, r.text
assert r.json()["value"] == ""
assert r.json()["has_value"] is False # the effective value is empty
assert db.execute(
text("SELECT value FROM app_settings WHERE key = 'llm_thinking_level'")
).scalar() == "" # stored as-is (the off switch)
assert get_settings().llm_thinking_level == ""
# (d) DELETE: the row is gone and the effective value is back to
# the env value (``low`` — the base pinned above).
r = admin_client.delete(f"{API}/llm_thinking_level")
assert r.status_code == 200, r.text
assert r.json()["value"] == "low"
assert r.json()["has_override"] is False
assert db.execute(
text("SELECT count(*) FROM app_settings WHERE key = 'llm_thinking_level'")
).scalar() == 0 # the row is gone
assert get_settings().llm_thinking_level == "low"
# (e) the non-admin 403 gate still applies (asserted once for this
# key — the router-wide dependency is pinned on ALL four routes in
# ``test_auth_matrix_all_four_routes``).
anon = TestClient(fastapi_app)
r = anon.put(f"{API}/llm_thinking_level", json={"value": "high"})
assert r.status_code == 403 and r.json() == {"detail": "admin only"}
def test_docs_token_is_write_only_masked_end_to_end(
admin_client: TestClient, db: Session, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Phase 173 (task 02, C1): ``docs_token`` is a write-only
``secret=True`` row — the EXISTING generic secret contract pinned
for the new key (no new code): (a) before a save the GET reports
the row in the ``Docs push`` group, ``secret=true``,
``value=null`` (masked) and ``has_value=false`` — with
``docs_user`` a plain ``str`` row at ``""``; (b) a PUT stores it
(200) and the GET reports ``has_value=true`` with the plaintext
NOWHERE in the raw body — the core pin: the token text is absent
from the response JSON, the leak class this phase kills; (c) a
blank PUT 422s (the self-lockout guard — ``DELETE`` is the
explicit clear) and the stored value is unchanged; (d) a DELETE
unsets it (reset to the env value — ``""`` by default).
"""
# A deterministic base (machine-independent, the phase-172 test's
# idiom): an explicit empty process value for both docs
# credentials — the process env RANKS ABOVE the operator's local
# ``.env`` (the conftest pin's reasoning), so this pins the code
# defaults no matter what the machine's ``.env`` carries.
monkeypatch.setenv("BOR_DOCS_USER", "")
monkeypatch.setenv("BOR_DOCS_TOKEN", "")
invalidate_settings()
# (a) the rows: docs_token masked + unset, docs_user plain + empty.
r = admin_client.get(API)
assert r.status_code == 200, r.text
item = _item(r.json(), "docs_token")
assert item["group"] == "Docs push"
assert item["label"] == "Docs token"
assert item["type"] == "str"
assert item["env_var"] == "BOR_DOCS_TOKEN"
assert item["secret"] is True
assert item["restart_required"] is False
assert item["overridable"] is True
assert item["masked"] is True
assert item["value"] is None
assert item["db_value"] is None
assert item["has_value"] is False
assert item["has_override"] is False
assert item["default"] == "" # masked: the code default is never shown
user = _item(r.json(), "docs_user")
assert user["group"] == "Docs push"
assert user["label"] == "Docs user"
assert user["secret"] is False
assert user["masked"] is False
assert user["value"] == ""
assert user["has_value"] is False
# (b) a PUT stores it (200, the RAW string in app_settings) and the
# GET reports has_value=true with the plaintext NOWHERE in the raw
# body (asserted on response.text, not the parsed model).
r = admin_client.put(f"{API}/docs_token", json={"value": "bor-docs-tok-123"})
assert r.status_code == 200, r.text
assert r.json()["masked"] is True
assert r.json()["value"] is None
assert r.json()["has_value"] is True
assert r.json()["source"] == "db"
assert db.execute(
text("SELECT value FROM app_settings WHERE key = 'docs_token'")
).scalar() == "bor-docs-tok-123" # stored, exactly as sent
assert get_settings().docs_token == "bor-docs-tok-123" # live, no restart
r = admin_client.get(API)
assert r.status_code == 200
item = _item(r.json(), "docs_token")
assert item["has_value"] is True
assert item["value"] is None
assert item["db_value"] is None
assert "bor-docs-tok-123" not in r.text # NOWHERE in the raw body
# (c) a blank PUT is a 422 (a blank token would self-lockout —
# DELETE is the explicit clear) and the stored value is unchanged.
r = admin_client.put(f"{API}/docs_token", json={"value": " "})
assert r.status_code == 422
assert "BOR_DOCS_TOKEN must not be blank" in r.json()["detail"]
assert db.execute(
text("SELECT value FROM app_settings WHERE key = 'docs_token'")
).scalar() == "bor-docs-tok-123" # unchanged
# (d) a DELETE unsets it: the row is gone and the effective value is
# back to the env value (the pinned "" — the anonymous state, C5).
r = admin_client.delete(f"{API}/docs_token")
assert r.status_code == 200, r.text
assert r.json()["source"] == "env" # the pinned empty BOR_DOCS_TOKEN
assert r.json()["has_override"] is False
assert r.json()["has_value"] is False
assert get_settings().docs_token == "" # the override is gone
assert db.execute(
text("SELECT count(*) FROM app_settings WHERE key = 'docs_token'")
).scalar() == 0 # the row is gone
def test_docs_repo_refuses_embedded_credentials(
admin_client: TestClient, db: Session, monkeypatch: pytest.MonkeyPatch
) -> None:
"""Phase 173 (task 02, C4): a ``docs_repo`` PUT with a
``://<user>:<pass>@`` segment is a 422 that NAMES the Docs user /
Docs token fields (asserted in the detail, not just present) and
stores NOTHING — the leak cannot be re-created from the UI. Clean
``https://`` URLs, local paths and ``ssh://`` remotes pass
untouched; a ``user:``-looking subdomain (no trailing ``@``) does
NOT false-positive. A legacy ``.env`` value with embedded
credentials is NOT a PUT — the task-04 startup migration handles
that path."""
# A deterministic base: explicit empty/valid process values for the
# docs settings — the process env ranks above the operator's local
# ``.env`` (the conftest pin's reasoning), so the base is the code
# defaults no matter what the machine's ``.env`` carries (a local
# ``BOR_DOCS_REPO`` with embedded credentials is EXACTLY the leak
# class under test — it must not leak into this pin).
monkeypatch.setenv("BOR_DOCS_REPO", "")
monkeypatch.setenv("BOR_DOCS_BRANCH", "bor-docs")
invalidate_settings()
# The 422: names the replacement fields, stores nothing.
r = admin_client.put(
f"{API}/docs_repo", json={"value": "https://u:pass@gitea.example.com/o/r.git"}
)
assert r.status_code == 422
detail = r.json()["detail"]
assert "Docs user" in detail
assert "Docs token" in detail
assert db.execute(text("SELECT count(*) FROM app_settings")).scalar() == 0
# The failed write did not disturb the live view.
assert get_settings().docs_repo == ""
# Clean https:// URL: passes (the branch validator is satisfied by
# the non-empty docs_branch default — the repo is set, the token
# shape is valid).
r = admin_client.put(f"{API}/docs_repo", json={"value": "https://gitea.example.com/o/r.git"})
assert r.status_code == 200, r.text
assert r.json()["db_value"] == "https://gitea.example.com/o/r.git"
assert get_settings().docs_repo == "https://gitea.example.com/o/r.git"
assert admin_client.delete(f"{API}/docs_repo").status_code == 200
# A local path: untouched.
r = admin_client.put(f"{API}/docs_repo", json={"value": "/srv/docs-repo"})
assert r.status_code == 200, r.text
assert r.json()["db_value"] == "/srv/docs-repo"
assert admin_client.delete(f"{API}/docs_repo").status_code == 200
# A ``user:``-looking subdomain: the trailing ``@`` requirement keeps
# the regex from false-positiving — passes untouched.
r = admin_client.put(
f"{API}/docs_repo", json={"value": "https://sub.domain.example.com/r.git"}
)
assert r.status_code == 200, r.text
assert r.json()["db_value"] == "https://sub.domain.example.com/r.git"
assert admin_client.delete(f"{API}/docs_repo").status_code == 200
# An ssh:// remote (credentials via SSH keys, never in the URL):
# untouched.
r = admin_client.put(
f"{API}/docs_repo", json={"value": "ssh://git@gitea.example.com/o/r.git"}
)
assert r.status_code == 200, r.text
assert r.json()["db_value"] == "ssh://git@gitea.example.com/o/r.git"
assert admin_client.delete(f"{API}/docs_repo").status_code == 200
assert db.execute(text("SELECT count(*) FROM app_settings")).scalar() == 0