test(hub): сквозной прогон очереди через HTTP с имитацией агента

Фикстура останавливает сервер (server_close, join потока), иначе
ResourceWarning о незакрытом сокете.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014Typ5sXWNMFirr2va2uL8o
This commit is contained in:
Andrey Epifancev 2026-10-11 22:51:11 +04:00
parent d7f82cb611
commit 181dd65958

59
tests/test_e2e_hub.py Normal file
View File

@ -0,0 +1,59 @@
import threading
import pytest
from jira_hub import api, settings
from jira_hub.client import HubClient
@pytest.fixture
def live(tmp_path):
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_Связанные"})
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["agent"]), 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"}},
{"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
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"]))
if cmd["type"] == "ensure_issue":
agent.post(f"/agent/commands/{cmd['id']}/result", {"status": "done", "result": {"key": "A-90"}, "jira_calls": 2})
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_Связанные"}]})
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[3][1]["keys"] == ["A-90", "A-1"]
view = client.get(f"/batches/{b['id']}")
assert view["status"] == "done" and all(c["status"] == "done" for c in view["commands"])
issue = client.get("/issues/A-90")
assert issue["note"]["work_kind"] == "разработка" and issue["links_out"][0]["target_key"] == "A-1"
assert client.get("/sync-runs")["runs"][0]["issues_count"] == 2