phase: 121_git_source_tokens
**Phase 121 final verification pass — all green** (all 4 tasks already in `complete/`; verified, no defects found, no changes needed) - Verified implementation vs phase design: migration `0021` (reversible, round-tripped via `alembic downgrade base` + `upgrade head` → head `0021`), `GitSource.token` column, `normalize_credential`/`clone_url_for`/`sanitize_url`, clone callers switched (`sync.py`, `import_docs.py`), masked token fields in add form + editor, `extra="forbid"` output shapes - Tests: `uv run pytest` → 2662 passed, 0 failed (exit 0); `uv run pytest --cov=app --cov-report=term-missing` → TOTAL **99%** (≥90% gate) - Lint/types: `uv run ruff check .` → All checks passed; `uv run pyright` → 0 errors, 0 warnings - E2E in isolation: `uv run pytest tests/e2e/test_git_source_tokens.py -v --no-cov` → **4 passed** Completion criteria: 1. Private repo (UI add or pasted embedded-token URL) clones with injected token; token absent from every API response, page text, title attr, and full HTML — **PASS** (integration raw-JSON assertions + E2E `_assert_token_nowhere`) 2. Legacy embedded-token rows still clone from stored URL; output sanitized — **PASS** (`test_sync_legacy_row_clones_with_original_stored_url`, `test_get_masks_legacy_embedded_token_row`, env-fallback masking) 3. Public/local sources byte-identical — **PASS** (verbatim-URL + no-userinfo-unchanged tests) 4. pytest / coverage / ruff / pyright — **PASS** (see above) 5. Commit + phase move — harness responsibility; task files already in `complete/`, changes left in working tree (no commit made, per protocol) Notable: no deviations; DB left at head, functional. Next pending phase: **122_image_documents** (then 123_chat_image_questions).
This commit is contained in:
@@ -68,6 +68,7 @@ DIALOG_IDS = (
|
||||
"ignore-editor-source",
|
||||
"ignore-editor-copy",
|
||||
"ignore-editor-textarea",
|
||||
"ignore-editor-token", # phase 121 (task 03): the masked token field
|
||||
"ignore-editor-error",
|
||||
"ignore-editor-cancel",
|
||||
"ignore-editor-save",
|
||||
@@ -359,7 +360,10 @@ def test_save_runs_the_inflight_never_stale_lifecycle() -> None:
|
||||
A4). The §7.4 in-flight state precedes the PATCH: both buttons
|
||||
disable + the save relabels "Saving…" — one
|
||||
``PATCH /api/git-sources/{id}`` with the lines as the whole
|
||||
body list (A5 replace). 200 → close (focus return) →
|
||||
body list (A5 replace). Phase 121 (task 03): the masked token is
|
||||
read AFTER the line parse and included in the body ONLY when
|
||||
non-blank (the tri-state: blank → key omitted → no change, the
|
||||
row's stored credential is kept). 200 → close (focus return) →
|
||||
loadSources (the count tag lands) → announce (the update
|
||||
confirmation is the LAST announcement). Non-2xx: the in-dialog
|
||||
role=alert line (apiDetail, 422 shape-aware), the dialog STAYS
|
||||
@@ -371,18 +375,23 @@ def test_save_runs_the_inflight_never_stale_lifecycle() -> None:
|
||||
parse_i = body.find('.split("\\n")')
|
||||
trim_i = body.find(".map((l) => l.trim())", parse_i)
|
||||
drop_i = body.find(".filter(Boolean)", trim_i)
|
||||
inflight_i = body.find("ignoreInFlight = true", drop_i)
|
||||
# Phase 121 (task 03): the token is read between the line parse
|
||||
# and the in-flight flag (blank = the key is omitted).
|
||||
token_i = body.find('ignoreTokenInput ? ignoreTokenInput.value.trim() : ""', drop_i)
|
||||
inflight_i = body.find("ignoreInFlight = true", token_i)
|
||||
dis_c = body.find("ignoreCancelBtn.disabled = true", inflight_i)
|
||||
dis_s = body.find("ignoreSaveBtn.disabled = true", inflight_i)
|
||||
label_i = body.find(f'"{SAVING_LABEL}"', dis_s)
|
||||
fetch_i = body.find("`/api/git-sources/${", label_i)
|
||||
method_i = body.find('method: "PATCH"', fetch_i)
|
||||
body_i = body.find("JSON.stringify({ ignore_paths: lines })", method_i)
|
||||
assert -1 < guard_i < parse_i < trim_i < drop_i < inflight_i, (
|
||||
"guard → split + trim + drop blank lines → in-flight"
|
||||
body_i = body.find(
|
||||
"JSON.stringify({ ignore_paths: lines, ...(token ? { token } : {}) })", method_i
|
||||
)
|
||||
assert -1 < guard_i < parse_i < trim_i < drop_i < token_i < inflight_i, (
|
||||
"guard → split + trim + drop blank lines → read the token → in-flight"
|
||||
)
|
||||
assert -1 < dis_c < dis_s < label_i < fetch_i < method_i < body_i, (
|
||||
"disable both + 'Saving…' → the PATCH with the lines"
|
||||
"disable both + 'Saving…' → the PATCH (lines + token only when non-blank)"
|
||||
)
|
||||
# Success: close → reload → announce (the exact order) — the
|
||||
# update confirmation is the LAST announcement: the reload's
|
||||
|
||||
Reference in New Issue
Block a user