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.
This commit is contained in:
@@ -54,12 +54,15 @@ def test_every_meta_carries_its_own_key() -> None:
|
||||
|
||||
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).
|
||||
# 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",
|
||||
}
|
||||
|
||||
|
||||
@@ -108,6 +111,38 @@ def test_llm_thinking_level_is_a_plain_live_overridable_row() -> None:
|
||||
assert "llm_thinking_level" 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_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"
|
||||
|
||||
Reference in New Issue
Block a user