From 7a2f2e694b742baddd9d23a2ff4fb4a361015971 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Wed, 14 Oct 2026 12:39:31 +0400 Subject: [PATCH] =?UTF-8?q?fix(mirror):=20rename=5Fnote=20=D1=81=D0=BB?= =?UTF-8?q?=D0=B8=D0=B2=D0=B0=D0=B5=D1=82=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=82?= =?UTF-8?q?=D0=BA=D0=B8,=20=D0=B0=20=D0=BD=D0=B5=20=D1=83=D0=B4=D0=B0?= =?UTF-8?q?=D0=BB=D1=8F=D0=B5=D1=82=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=82=D0=BA?= =?UTF-8?q?=D1=83=20=D0=BF=D0=BE=D0=B4=20=D0=BA=D0=BB=D1=8E=D1=87=D0=BE?= =?UTF-8?q?=D0=BC=20(#13)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit M-3 (hub#4, PR #2). Co-Authored-By: Claude Opus 5.5 --- src/jira_hub/mirror.py | 19 ++++++++++++++----- tests/test_mirror.py | 22 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/src/jira_hub/mirror.py b/src/jira_hub/mirror.py index 701e2f3..d0f4c47 100644 --- a/src/jira_hub/mirror.py +++ b/src/jira_hub/mirror.py @@ -112,12 +112,21 @@ def set_note(conn, key, fields: dict, updated_by: str) -> dict: def rename_note(conn, old_key, new_key) -> None: - """После создания задачи: заметка с временного ключа переезжает на настоящий (старая запись под новым ключом, - если вдруг была, уступает место).""" + """После создания задачи: заметка с временного ключа переезжает на настоящий. Если под настоящим ключом заметка уже + есть — слияние (M-3 #4): поле временной заметки побеждает, если оно непустое; updated_at/updated_by — от более + поздней; временная строка удаляется.""" if old_key == new_key: return with db.transaction(conn): - if get_note(conn, old_key) is None: + old = get_note(conn, old_key) + if old is None: return - conn.execute("DELETE FROM issue_note WHERE key = ?", (new_key,)) - conn.execute("UPDATE issue_note SET key = ? WHERE key = ?", (new_key, old_key)) + cur = get_note(conn, new_key) + if cur is None: + conn.execute("UPDATE issue_note SET key = ? WHERE key = ?", (new_key, old_key)) + return + merged = [old[f] if old[f] not in (None, "") else cur[f] for f in NOTE_FIELDS] + newer = old if (old["updated_at"] or "") >= (cur["updated_at"] or "") else cur + conn.execute(f"UPDATE issue_note SET {', '.join(f'{f} = ?' for f in NOTE_FIELDS)}, updated_at = ?, updated_by = ? " + "WHERE key = ?", merged + [newer["updated_at"], newer["updated_by"], new_key]) + conn.execute("DELETE FROM issue_note WHERE key = ?", (old_key,)) diff --git a/tests/test_mirror.py b/tests/test_mirror.py index 710b2f7..894f1fe 100644 --- a/tests/test_mirror.py +++ b/tests/test_mirror.py @@ -101,3 +101,25 @@ def test_set_note_partial_keeps_other_fields(): got = mirror.set_note(conn, "A-1", {"fact_status": "Готово"}, "y") assert got["work_kind"] == "разработка" and got["done_text"] == "сделано" assert got["fact_status"] == "Готово" and got["updated_by"] == "y" + + +def test_rename_note_merges_with_existing(): + conn = db.connect(":memory:") + mirror.set_note(conn, "A-5", {"owner_note": "спросить ИБ", "work_kind": "Старое"}, "human") + mirror.set_note(conn, "new:5", {"work_kind": "Аналитика", "fact_status": "В работе"}, "import") + mirror.rename_note(conn, "new:5", "A-5") + note = mirror.get_note(conn, "A-5") + assert note["work_kind"] == "Аналитика" # непустое поле временной заметки побеждает + assert note["owner_note"] == "спросить ИБ" # пустое у временной — остаётся под ключом + assert note["fact_status"] == "В работе" + assert note["updated_by"] == "import" # временная заметка записана позже + assert mirror.get_note(conn, "new:5") is None + + +def test_rename_note_empty_string_does_not_overwrite(): + conn = db.connect(":memory:") + mirror.set_note(conn, "A-6", {"rest_text": "доделать отчёт"}, "human") + mirror.set_note(conn, "new:6", {"rest_text": "", "done_text": "сделано"}, "import") + mirror.rename_note(conn, "new:6", "A-6") + note = mirror.get_note(conn, "A-6") + assert note["rest_text"] == "доделать отчёт" and note["done_text"] == "сделано"