feat(agent): тип задачи из issue_types с ошибкой конфигурации, тип bug (#31)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
cd8213b71c
commit
90dc429434
@ -165,7 +165,7 @@ python3 agent.py prune
|
||||
|
||||
| Тип | Что делает |
|
||||
|---|---|
|
||||
| `ensure_issue` | ищет задачу своей темы (`reporter = currentUser()`, за 7 дней), иначе создаёт; возвращает `key` |
|
||||
| `ensure_issue` | ищет задачу своей темы (`reporter = currentUser()`, за 7 дней), иначе создаёт с типом из `issue_types` хаба (`task`, `epic`, `bug`; нет настройки типа — `failed` «нет настройки issue_types.<тип>» без вызова Jira); возвращает `key` |
|
||||
| `ensure_fields` | читает задачу и пишет только отличающиеся поля |
|
||||
| `ensure_comment` | комментарий с маркером в начале: обновляет, если текст изменился, иначе создаёт |
|
||||
| `ensure_status` | идёт по пути статусов, на каждом шаге выбирает переход; нет перехода — `failed` со списком доступных |
|
||||
|
||||
14
src/agent.py
14
src/agent.py
@ -572,8 +572,14 @@ class Executor:
|
||||
return {self.s.get("priority_value_key") or "name": mapped}
|
||||
|
||||
def issue_type_ref(self, kind: str) -> dict:
|
||||
it = (self.s.get("issue_types") or {}).get(kind) or {}
|
||||
return {"id": str(it["id"])} if it.get("id") else {"name": it.get("name")}
|
||||
"""Тип задачи Jira из issue_types.<kind>: {id}, если id задан, иначе {name}; нет ни того, ни другого —
|
||||
ошибка конфигурации, а не {"name": None}, на которое Jira ответит 400 без понятной причины (спека agent v11)."""
|
||||
it = (self.s.get("issue_types") or {}).get(kind)
|
||||
if isinstance(it, dict) and it.get("id"):
|
||||
return {"id": str(it["id"])}
|
||||
if isinstance(it, dict) and it.get("name"):
|
||||
return {"name": it["name"]}
|
||||
raise ConfigGap("нет настройки issue_types.%s" % kind)
|
||||
|
||||
def epic_field_id(self) -> str:
|
||||
ep = self.s.get("epic_link") or {}
|
||||
@ -593,6 +599,7 @@ class Executor:
|
||||
|
||||
# --- ensure_issue ----------------------------------------------------------
|
||||
def do_ensure_issue(self, p: dict, cmd: dict) -> dict:
|
||||
issuetype = self.issue_type_ref(p["issue_type"]) # до поиска дубля: без настройки — ни одного вызова Jira
|
||||
summary = (p["summary"] or "").strip()
|
||||
jql = "project = %s AND reporter = currentUser() AND created >= -7d ORDER BY created DESC" % self.s["project_key"]
|
||||
start = 0
|
||||
@ -605,8 +612,7 @@ class Executor:
|
||||
start += len(issues)
|
||||
if not issues or start >= int(found.get("total") or 0):
|
||||
break
|
||||
fields = {"project": {"key": self.s["project_key"]}, "issuetype": self.issue_type_ref(p["issue_type"]),
|
||||
"summary": p["summary"]}
|
||||
fields = {"project": {"key": self.s["project_key"]}, "issuetype": issuetype, "summary": p["summary"]}
|
||||
if p.get("description") is not None:
|
||||
fields[self.fid("description")] = p["description"]
|
||||
if p.get("priority"):
|
||||
|
||||
@ -14,7 +14,8 @@ ZERO_PACING = {"ensure_issue": 0, "ensure_fields": 0, "ensure_comment": 0, "ensu
|
||||
SETTINGS = {
|
||||
"base_jql": "project = AURORA AND labels = AWPCA",
|
||||
"project_key": "AURORA",
|
||||
"issue_types": {"task": {"name": "Задача", "id": "10100"}, "epic": {"name": "Фича", "id": "12300"}},
|
||||
"issue_types": {"task": {"name": "Задача", "id": "10100"}, "epic": {"name": "Фича", "id": "12300"},
|
||||
"bug": {"name": "Ошибка", "id": None}},
|
||||
"epic_name_field_id": "customfield_15304",
|
||||
"field_ids": {"description": "description", "priority": "priority", "duedate": "duedate", "labels": "labels",
|
||||
"planned_start": "customfield_10801", "planned_end": "customfield_10802"},
|
||||
|
||||
@ -48,6 +48,34 @@ class ExecutorFieldsTest(unittest.TestCase):
|
||||
out = self.run_cmd("ensure_issue", temp_key="new:3", issue_type="task", summary="x", priority="Наивысший")
|
||||
self.assertEqual(self.fake.issues[out["result"]["key"]]["priority"], "Критичный")
|
||||
|
||||
def test_ensure_issue_bug_by_name(self):
|
||||
self.assertEqual(self.ex.issue_type_ref("bug"), {"name": "Ошибка"}) # id нет — по имени
|
||||
self.assertEqual(self.ex.issue_type_ref("task"), {"id": "10100"}) # id есть — по id
|
||||
out = self.run_cmd("ensure_issue", temp_key="new:1", issue_type="bug", summary="ОСВ 60 не распознана",
|
||||
labels=["AWPCA"])
|
||||
self.assertEqual(out["status"], "done")
|
||||
self.assertTrue(out["result"]["created"])
|
||||
self.assertEqual(self.fake.issues[out["result"]["key"]]["issuetype"], "Ошибка")
|
||||
|
||||
def test_ensure_issue_type_not_configured_no_jira_calls(self):
|
||||
self.ex.s = dict(SETTINGS, issue_types={"task": {"name": "Задача", "id": "10100"}})
|
||||
out = self.run_cmd("ensure_issue", temp_key="new:1", issue_type="bug", summary="x")
|
||||
self.assertEqual(out["status"], "failed")
|
||||
self.assertEqual(out["error"], "нет настройки issue_types.bug")
|
||||
self.assertNotIn("retryable", out)
|
||||
self.assertEqual(self.fake.requests, []) # ни поиска дубля, ни создания
|
||||
|
||||
def test_issue_type_entry_without_name_and_id(self):
|
||||
for entry in ({}, {"name": None, "id": None}, {"name": "", "id": ""}, None, "Ошибка"):
|
||||
self.ex.s = dict(SETTINGS, issue_types={"bug": entry})
|
||||
with self.assertRaises(agent.ConfigGap) as cm:
|
||||
self.ex.issue_type_ref("bug")
|
||||
self.assertEqual(str(cm.exception), "нет настройки issue_types.bug")
|
||||
self.ex.s = dict(SETTINGS)
|
||||
del self.ex.s["issue_types"]
|
||||
with self.assertRaises(agent.ConfigGap):
|
||||
self.ex.issue_type_ref("task")
|
||||
|
||||
def test_ensure_fields_writes_only_diff(self):
|
||||
self.fake.add("AURORA-1", duedate="2026-10-20", labels=["AWPCA", "fact-open"], priority="Средний")
|
||||
out = self.run_cmd("ensure_fields", key="AURORA-1", duedate="2026-10-23", priority="Средний",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user