import threading import pytest from sdlc_hub.core import api, settings from sdlc_hub.core.client import HubClient, HubError @pytest.fixture def live(tmp_path, add_project): h = api.Hub(tmp_path / "hub.sqlite", backups_dir=tmp_path / "backups") h.claim_wait = 0 conn = h.conn() settings.set_(conn, "link_types", {"связанные": "ASUPR_Связанные"}) add_project(conn) tok = settings.tokens(conn) conn.close() srv = api.make_server(h, "127.0.0.1", 0) th = threading.Thread(target=srv.serve_forever, daemon=True) th.start() base = f"http://127.0.0.1:{srv.server_port}" yield HubClient(base, tok["agents"]["jira"]), HubClient(base, tok["client"]) srv.shutdown() srv.server_close() th.join(5) def test_create_link_sprint_flow(live): agent, client = live client.put("/issues/new:60/note", {"fact_status": "Ожидание разработки", "work_kind": "разработка", "updated_by": "import"}) b = client.post("/batches", {"title": "новая задача в спринт", "author": "claude", "commands": [ {"type": "ensure_issue", "params": {"temp_key": "new:60", "issue_type": "task", "summary": "Новая", "duedate": "2026-10-23", "labels": ["AWPCA"]}}, {"type": "ensure_link", "params": {"source": "new:60", "target": "A-1", "link_type": "связанные"}}, {"type": "ensure_sprint", "params": {"key": "new:60", "sprint": "Спринт 2"}}, ]}) assert b["count"] == 4 bid = b["id"] assert client.get(f"/batches/{bid}")["status"] == "queued" seen = [] while True: cmd = agent.post("/agent/claim", {"agent_version": 1, "settings_version": 1})["command"] if cmd is None: break seen.append((cmd["type"], cmd["params"])) view = client.get(f"/batches/{bid}") assert view["status"] == "running" and view["remaining_seconds"] > 0 if cmd["type"] == "ensure_issue": # одна команда в полёте: повторный claim довыдаёт ensure_issue, зависимая ensure_link не выдаётся assert agent.post("/agent/claim", {"agent_version": 1, "settings_version": 1})["command"]["id"] == cmd["id"] agent.post(f"/agent/commands/{cmd['id']}/result", {"status": "done", "result": {"key": "A-90"}, "jira_calls": 2}) # done ensure_issue не пишет в зеркало; заметка переехала с new:60 на A-90 with pytest.raises(HubError) as e: client.get("/issues/A-90") assert e.value.status == 404 with pytest.raises(HubError) as e: client.get("/issues/new:60") assert e.value.status == 404 view = client.get(f"/batches/{bid}") assert view["status"] == "running" and view["remaining_seconds"] > 0 elif cmd["type"] == "refresh": agent.post("/agent/state", {"command_id": cmd["id"], "issues": [ {"key": "A-90", "summary": "Новая", "duedate": "2026-10-23", "sprint": "Спринт 2", "labels": [], "created": "2026-10-11T10:00:00.000+0300"}, {"key": "A-1", "summary": "Старая", "labels": []}], "links": [{"source_key": "A-90", "target_key": "A-1", "link_type": "ASUPR_Связанные"}]}) assert client.get("/issues/A-90")["summary"] == "Новая" agent.post(f"/agent/commands/{cmd['id']}/result", {"status": "done", "jira_calls": 1}) else: agent.post(f"/agent/commands/{cmd['id']}/result", {"status": "done", "jira_calls": 1}) assert [t for t, _ in seen] == ["ensure_issue", "ensure_link", "ensure_sprint", "refresh"] assert seen[1][1]["source"] == "A-90" and seen[2][1]["key"] == "A-90" and seen[2][1]["board_id"] == 4216 and seen[3][1]["keys"] == ["A-90", "A-1"] view = client.get(f"/batches/{bid}") assert view["status"] == "done" and view["remaining_seconds"] == 0 assert all(c["status"] == "done" for c in view["commands"]) issue = client.get("/issues/A-90") assert issue["note"]["work_kind"] == "разработка" assert len(issue["links_out"]) == 1 assert issue["links_out"][0]["target_key"] == "A-1" and issue["links_out"][0]["link_type"] == "ASUPR_Связанные" links_in = client.get("/issues/A-1")["links_in"] assert [(l["source_key"], l["link_type"]) for l in links_in] == [("A-90", "ASUPR_Связанные")] assert client.get("/sync-runs")["runs"][0]["issues_count"] == 2