API: состояние агента и синхронизация вики неверной формы — 422, а не 500 (#129, #42 T2.1, #94 T8.1)
mirror.check_issues проверяет форму issues до записи; /wiki/sync отвергает project не строку. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
4a6855d70b
commit
f1dae5a811
@ -24,6 +24,7 @@ def h_agent_state(hub, conn, m, q, body):
|
||||
issues = body.get("issues") or []
|
||||
boards = body.get("boards")
|
||||
try:
|
||||
mirror.check_issues(issues)
|
||||
mirror.check_comments(issues) # до записи: неверная пачка не применяется целиком
|
||||
mirror.check_attachments(issues)
|
||||
mirror.check_agile_fields(issues)
|
||||
|
||||
@ -32,6 +32,15 @@ def check_comments(issues) -> None:
|
||||
if c.get(f) is not None and not isinstance(c[f], str):
|
||||
raise ValueError(f"{where}.{f} — строка или null")
|
||||
|
||||
def check_issues(issues) -> None:
|
||||
"""Форма списка задач состояния: список объектов с непустым строковым key. Нарушение — ValueError."""
|
||||
if not isinstance(issues, list):
|
||||
raise ValueError("issues — список задач")
|
||||
for n, it in enumerate(issues):
|
||||
if not isinstance(it, dict) or not isinstance(it.get("key"), str) or not it["key"]:
|
||||
raise ValueError(f"issues[{n}] — объект задачи с key")
|
||||
|
||||
|
||||
def check_attachments(issues) -> None:
|
||||
"""Форма поля attachments у задач состояния (агент v14+); поле необязательно. Нарушение — ValueError с ключом и полем."""
|
||||
for it in issues:
|
||||
|
||||
@ -94,6 +94,8 @@ def h_search(hub, conn, m, q, body):
|
||||
|
||||
|
||||
def h_sync(hub, conn, m, q, body):
|
||||
if body.get("project") is not None and not isinstance(body["project"], str):
|
||||
raise ApiError(422, "bad_request", "project — строка")
|
||||
q = dict(q, project=body.get("project"))
|
||||
author = body.get("author") or "api"
|
||||
if not isinstance(author, str):
|
||||
|
||||
@ -1299,3 +1299,17 @@ def test_rank_before_trimmed_on_claim(hub):
|
||||
{"type": "ensure_rank", "params": {"key": "X-1", "before": " X-2 "}}]})
|
||||
cmd = hub["agent"].post("/agent/claim", {})["command"]
|
||||
assert cmd["type"] == "ensure_rank" and cmd["params"]["before"] == "X-2"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("issues, detail", [
|
||||
(["X-1"], "issues[0] — объект задачи с key"),
|
||||
([{"summary": "без ключа"}], "issues[0] — объект задачи с key"),
|
||||
([{"key": 5}], "issues[0] — объект задачи с key"),
|
||||
([{"key": ""}], "issues[0] — объект задачи с key"),
|
||||
({"key": "X-1"}, "issues — список задач"),
|
||||
("X-1", "issues — список задач"),
|
||||
])
|
||||
def test_agent_state_bad_issue_shape_422(hub, issues, detail):
|
||||
with pytest.raises(HubError) as e:
|
||||
hub["agent"].post("/agent/state", {"issues": issues, "links": []})
|
||||
assert e.value.status == 422 and e.value.error == "bad_state" and detail in e.value.detail
|
||||
|
||||
@ -228,3 +228,12 @@ def test_page_attachment_failed_error_visible(api):
|
||||
c.close()
|
||||
att = api.client.get("/wiki/pages/DOC-3", project="AWPCA")["attachments"][0]
|
||||
assert att["state"] == "failed" and att["error"] == "Eva: 404"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("project", [5, ["AWPCA"], {"a": 1}, True])
|
||||
def test_sync_project_not_string_422(api, project):
|
||||
first_sync(api)
|
||||
before = api.rows("SELECT id FROM batch")
|
||||
e = err(api.client.post, "/wiki/sync", {"project": project})
|
||||
assert e.status == 422 and e.error == "bad_request" and e.detail == "project — строка"
|
||||
assert api.rows("SELECT id FROM batch") == before # пакета не создано
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user