phase: 104_chip_sizing_question_cap
All completion criteria verified. Final report: **Phase 104 — final verification pass: all green** - Verified (no code changes needed): single-line ellipsized `.suggestion-chip` CSS + deleted `.maybe-try` override, `renderChips` full-text `title` + `aria-label`-when-clipped, `maxlength="4000"` + `#char-count` counter + `handleSend` over-cap guard, unit pins file, schemas boundary pins, dedicated E2E suite. - E2E (isolation): `uv run pytest tests/e2e/test_chip_sizing_question_cap.py -v --no-cov` → **6 passed**; regressions: `test_suggestion_chips.py` 8 passed, `test_pinned_composer.py` 4 passed, `test_responsive_polish.py` 7 passed, `test_chat_history.py` 5 passed. - `uv run pytest` → **2102 passed**; `--cov=app` → **99%** (>90%); `uv run ruff check . && uv run pyright` → clean, 0 errors. - Criteria: chip E2E (single-line, clipped, title+aria-label full text) ✅; paste caps at exactly 4,000, send streams, counter hides ✅; programmatic 5,000-char fill → banner, no turn, text kept ✅; 4,000/4,001 boundary pinned + HTML maxlength == JS constant cross-file pin ✅. - Diff scope: `frontend/`, new unit file, `tests/unit/test_schemas.py`, new E2E file, phase files — **no `app/` diff, no migration, no `shared.js` diff**. - Deviations: 4 regression test files touched — 2 genuine DOM-pin conflicts from the new `#char-count` child (explicitly anticipated by the overview) + 3 documented **pre-existing E2E flake fixes** (smooth-scroll race, tab-walk heuristic, 10 ms timeout), each verified pre-existing on the pre-phase-104 tree. - No commit made (harness commits per the execution protocol override). - Next pending phase: `98_sync_summary_visibility`.
This commit is contained in:
@@ -15,6 +15,7 @@ from pydantic import ValidationError
|
||||
|
||||
from app.schemas import (
|
||||
ChatMessage,
|
||||
ChatRequest,
|
||||
SavedChatCreate,
|
||||
SavedChatUpdate,
|
||||
SourceRef,
|
||||
@@ -34,6 +35,7 @@ TITLE_CAP = 500 # documents.title String(500)
|
||||
NAME_CAP = 100 # ToolCall.name
|
||||
ARGUMENT_CAP = 2000 # ToolCall.argument
|
||||
MESSAGES_CAP = 200 # SavedChatCreate/Update.messages
|
||||
QUESTION_CAP = 4_000 # ChatRequest.message — the cap the composer mirrors (phase 104)
|
||||
|
||||
|
||||
def _source_ref() -> dict:
|
||||
@@ -174,6 +176,30 @@ def test_chat_message_tools_one_over_cap_rejects() -> None:
|
||||
_failed_loc(exc.value, "tools")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ChatRequest.message (the 4,000-char question cap — phase 104, A3)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_chat_request_message_at_cap_validates() -> None:
|
||||
"""EXACTLY 4,000 chars passes — the cap admits "a code example of a
|
||||
few dozen lines" (owner A3) and the composer's maxlength + counter
|
||||
mirror this exact boundary (the 4,000/4,000 E2E submit must not
|
||||
422)."""
|
||||
req = ChatRequest.model_validate({"message": "a" * QUESTION_CAP})
|
||||
assert len(req.message) == QUESTION_CAP
|
||||
|
||||
|
||||
def test_chat_request_message_one_over_cap_rejects() -> None:
|
||||
"""4,001 chars is the 422 the UI now prevents blind: the server cap
|
||||
stays the backstop (pre-existing, untouched — this phase only pins
|
||||
it at the boundary, phase-83 pattern), naming ``message`` in the
|
||||
error loc."""
|
||||
with pytest.raises(ValidationError) as exc:
|
||||
ChatRequest.model_validate({"message": "a" * (QUESTION_CAP + 1)})
|
||||
_failed_loc(exc.value, "message")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# SourceRef (documents column-length mirrors)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user