fix(agent): описание и комментарий сравниваются без различия CRLF/LF (#15)

T3.9 (agent#4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andrey Epifancev 2026-10-14 12:37:20 +04:00
parent 93c2b0247d
commit 0532b2476c
2 changed files with 26 additions and 2 deletions

View File

@ -453,6 +453,11 @@ def _date(value) -> Optional[str]:
return (value or "")[:10] or None
def _nl(value) -> str:
r"""Текст для сравнения: Jira хранит правленное в UI с \r\n, команды приходят с \n (T3.9, проверено 13.10.2026)."""
return (value or "").replace("\r\n", "\n")
class ConfigGap(Exception):
"""В настройках хаба нет того, что нужно команде (R-11): команда падает с понятным текстом, а не молча теряет поле."""
@ -567,7 +572,7 @@ class Executor:
if "summary" in p and (cur.get("summary") or "") != (p["summary"] or ""):
fields["summary"] = p["summary"]
changed.append("summary")
if "description" in p and (cur.get(self.fid("description")) or "") != (p["description"] or ""):
if "description" in p and _nl(cur.get(self.fid("description"))) != _nl(p["description"]):
fields[self.fid("description")] = p["description"]
changed.append("description")
if "priority" in p:
@ -622,7 +627,7 @@ class Executor:
start += len(comments)
if not comments or page.get("total") is None or start >= int(page["total"]):
break
if existing and (existing.get("body") or "") == body:
if existing and _nl(existing.get("body")) == _nl(body):
return self._out("done", {"comment_id": existing["id"], "changed": False})
if existing:
self.jira.put(API + "/issue/%s/comment/%s" % (key, existing["id"]), {"body": body})

View File

@ -101,6 +101,25 @@ class ExecutorFieldsTest(unittest.TestCase):
self.assertEqual(len(self.fake.comments["AURORA-1"]), 1)
self.assertEqual(self.fake.comments["AURORA-1"][0]["body"], "*Актуализация 10.10.2026.* Сделано: всё")
def test_fields_description_crlf_equals_lf(self):
self.fake.add("AURORA-1", description="а\r\nб") # так Jira хранит текст, правленный в UI
out = self.run_cmd("ensure_fields", key="AURORA-1", description="а\nб")
self.assertEqual(out["result"]["changed"], [])
out = self.run_cmd("ensure_fields", key="AURORA-1", description="а\nв")
self.assertEqual(out["result"]["changed"], ["description"])
self.assertEqual(self.fake.issues["AURORA-1"]["description"], "а\nв") # отправлен текст команды как есть
def test_fields_description_none_equals_empty(self):
self.fake.add("AURORA-1", description=None)
out = self.run_cmd("ensure_fields", key="AURORA-1", description="")
self.assertEqual(out["result"]["changed"], [])
def test_comment_crlf_equals_lf(self):
self.fake.add("AURORA-1")
self.fake.comments["AURORA-1"] = [{"id": "10", "body": "*Актуализация 10.10.2026.* строка1\r\nстрока2"}]
out = self.run_cmd("ensure_comment", key="AURORA-1", marker="Актуализация 10.10.2026", body="строка1\nстрока2")
self.assertEqual(out["result"], {"comment_id": "10", "changed": False})
def test_unknown_type(self):
self.assertEqual(self.run_cmd("ensure_magic", key="x")["status"], "failed")