import pytest from jira_hub import db, mirror def issue(key, **over): base = {"key": key, "id": "1", "issue_type": "Задача", "summary": f"Тема {key}", "description": "h3. Контекст", "status": "Ожидание разработки", "priority": "Средний", "assignee": None, "reporter": "me", "duedate": "2026-10-23", "planned_start": None, "planned_end": "2026-10-23", "epic_key": "AURORA-1350", "labels": ["AWPCA"], "sprint": "Спринт 2", "created": "2026-10-10T12:00:00.000+0300", "jira_updated": "2026-10-11T12:00:00.000+0300"} base.update(over) return base def test_apply_state_upserts_and_replaces_links(): conn = db.connect(":memory:") links = [{"source_key": "A-1", "target_key": "A-2", "link_type": "ASUPR_Блокер_Начала"}] out = mirror.apply_state(conn, [issue("A-1"), issue("A-2")], links) assert out == {"issues": 2, "links": 1} mirror.apply_state(conn, [issue("A-1", summary="Новая тема")], [{"source_key": "A-1", "target_key": "A-2", "link_type": "ASUPR_Связанные"}]) got = mirror.get_issue(conn, "A-1") assert got["summary"] == "Новая тема" and got["labels"] == ["AWPCA"] assert got["links_out"] == [{"target_key": "A-2", "link_type": "ASUPR_Связанные"}] assert mirror.get_issue(conn, "A-2")["links_in"] == [{"source_key": "A-1", "link_type": "ASUPR_Связанные"}] def test_links_from_other_source_survive(): conn = db.connect(":memory:") mirror.apply_state(conn, [issue("A-1"), issue("A-2")], [{"source_key": "A-2", "target_key": "A-1", "link_type": "ASUPR_Связанные"}]) mirror.apply_state(conn, [issue("A-1")], []) # пришла только A-1 без связей assert mirror.get_issue(conn, "A-1")["links_in"] == [{"source_key": "A-2", "link_type": "ASUPR_Связанные"}] def test_apply_state_marks_neighbors(): conn = db.connect(":memory:") mirror.apply_state(conn, [issue("A-1"), issue("B-9")], [], neighbor_keys={"B-9"}) assert mirror.get_issue(conn, "B-9")["neighbor"] is True assert mirror.get_issue(conn, "A-1")["neighbor"] is False mirror.apply_state(conn, [issue("B-9")], []) # вошла в базовую JQL assert mirror.get_issue(conn, "B-9")["neighbor"] is False assert mirror.known_keys(conn) == ["A-1", "B-9"] def test_partial_load_never_lowers_neighbor(): conn = db.connect(":memory:") mirror.apply_state(conn, [issue("B-9")], [], neighbor_keys={"B-9"}) mirror.apply_state(conn, [issue("B-9", summary="новая")], [], authoritative=False) # refresh {keys} assert mirror.get_issue(conn, "B-9")["neighbor"] is True mirror.apply_state(conn, [issue("B-9")], [], authoritative=True) # полный прогон: вошла в базовую assert mirror.get_issue(conn, "B-9")["neighbor"] is False def test_note_survives_refresh_and_renames(): conn = db.connect(":memory:") mirror.set_note(conn, "new:7", {"fact_status": "В работе", "work_kind": "разработка"}, "import") mirror.rename_note(conn, "new:7", "A-5") mirror.apply_state(conn, [issue("A-5")], []) got = mirror.get_issue(conn, "A-5") assert got["note"]["fact_status"] == "В работе" and got["note"]["updated_by"] == "import" assert mirror.get_note(conn, "new:7") is None with pytest.raises(ValueError): mirror.set_note(conn, "A-5", {"summary": "нельзя"}, "x") def test_list_issues_filters(): conn = db.connect(":memory:") mirror.apply_state(conn, [issue("A-1", sprint="Спринт 2", assignee="ivanov", duedate="2026-10-20"), issue("A-2", sprint="Спринт 1", labels=["AWPCA", "fact-done"]), issue("A-3", sprint="Спринт 2", duedate=None)], []) mirror.set_note(conn, "A-3", {"work_kind": "тестирование"}, "x") keys = [i["key"] for i in mirror.list_issues(conn, {"sprint": "Спринт 2"})] assert keys == ["A-1", "A-3"] # без срока — последней assert [i["key"] for i in mirror.list_issues(conn, {"label": "fact-done"})] == ["A-2"] assert [i["key"] for i in mirror.list_issues(conn, {"work_kind": "тестирование"})] == ["A-3"] assert [i["key"] for i in mirror.list_issues(conn, {"q": "A-2"})] == ["A-2"] assert mirror.list_issues(conn, {"assignee": "ivanov"})[0]["work_kind"] is None assert len(mirror.list_issues(conn, None, limit=1)) == 1 def test_rename_note_same_key_keeps_note(): conn = db.connect(":memory:") mirror.apply_state(conn, [issue("A-1")], []) mirror.set_note(conn, "A-1", {"fact_status": "В работе"}, "x") mirror.rename_note(conn, "A-1", "A-1") assert mirror.get_note(conn, "A-1")["fact_status"] == "В работе" def test_rename_note_missing_is_noop(): conn = db.connect(":memory:") mirror.rename_note(conn, "new:404", "A-9") # заметки нет — не падает assert mirror.get_note(conn, "new:404") is None assert mirror.get_note(conn, "A-9") is None def test_set_note_partial_keeps_other_fields(): conn = db.connect(":memory:") mirror.set_note(conn, "A-1", {"work_kind": "разработка", "done_text": "сделано"}, "x") 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"