From fd13770427052fd8ad33382b68d836843ec6c171 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Mon, 26 Oct 2026 03:02:11 +0400 Subject: [PATCH] =?UTF-8?q?=D0=9D=D0=B0=D1=81=D1=82=D1=80=D0=BE=D0=B9?= =?UTF-8?q?=D0=BA=D0=B8:=20=D0=BA=D0=BB=D1=8E=D1=87=D0=B8=20v3=20base=5Fjq?= =?UTF-8?q?l=20=D0=B8=20board=5Fid=20=D1=83=D0=B4=D0=B0=D0=BB=D1=8F=D1=8E?= =?UTF-8?q?=D1=82=D1=81=D1=8F=20=D0=BF=D1=80=D0=B8=20=D0=BE=D1=82=D0=BA?= =?UTF-8?q?=D1=80=D1=8B=D1=82=D0=B8=D0=B8=20=D1=85=D0=B0=D0=B1=D0=B0=20(#1?= =?UTF-8?q?29,=20#64)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OBSOLETE расширен ключами base_jql и board_id: ensure_defaults убирает их при каждом открытии, миграция v7 -> v8 тоже. settings_version не растёт. PUT /settings для этих ключей по-прежнему отвечает 422 с пояснением. Тест миграции v3 обновлён: после миграции ключей в настройках нет (проекты уже созданы из них). Co-Authored-By: Claude Opus 5.5 --- src/sdlc_hub/modules/jira/settings.py | 4 +-- tests/test_db.py | 3 +- tests/test_settings.py | 45 +++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/sdlc_hub/modules/jira/settings.py b/src/sdlc_hub/modules/jira/settings.py index 127390c..e6610ed 100644 --- a/src/sdlc_hub/modules/jira/settings.py +++ b/src/sdlc_hub/modules/jira/settings.py @@ -24,9 +24,9 @@ DEFAULTS = { "closed_statuses": ["Готово", "Снято"], # статусы Jira закрытых задач: список и доска приложения (hide-closed) } -# Ключи прежних версий (fact-status-removal): их удаляет миграция v8 и каждый ensure_defaults — процесс со старым кодом +# Ключи прежних версий (fact-status-removal; base_jql и board_id — v3, #129): их удаляет миграция v8 и каждый ensure_defaults — процесс со старым кодом # (MCP-сессия до reconnect) вставил бы их снова своими умолчаниями -OBSOLETE = ("status_labels", "transitions_map") +OBSOLETE = ("status_labels", "transitions_map", "base_jql", "board_id") # Что получает агент от модуля (без токенов и служебных ключей хаба); settings_version добавляет ядро AGENT_KEYS = ["project_key", "issue_types", "epic_name_field_id", "field_ids", "epic_link", diff --git a/tests/test_db.py b/tests/test_db.py index 4b86003..3884527 100644 --- a/tests/test_db.py +++ b/tests/test_db.py @@ -299,8 +299,7 @@ def test_migrates_v3_creates_awpca(tmp_path): assert {k: v for k, v in api.project_rows(conn)[0].items() if not k.startswith(("wiki_", "jira_"))} | { "created_at": None, "issues_count": None} == { "label": "AWPCA", "name": "АРМ КА", "board_id": 4216, "board_type": None, "created_at": None, "issues_count": None} - assert settings.get(conn, "base_jql") == "project = AURORA AND labels = AWPCA" # ключи на месте - assert settings.get(conn, "board_id") == 4216 + assert settings.get(conn, "base_jql") is None and settings.get(conn, "board_id") is None # ключи v3 убраны (#129) assert conn.execute("SELECT query FROM sync_run").fetchone()["query"] == new jqls = {r["status"]: json.loads(r["params"]).get("jql") for r in conn.execute("SELECT status, params FROM command WHERE type = 'refresh'")} assert jqls["done"] == "project = AURORA AND labels = AWPCA" # завершённые не трогаем diff --git a/tests/test_settings.py b/tests/test_settings.py index 50c6f20..438c6ac 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -138,3 +138,48 @@ def test_sprint_types_pacing(conn): assert jira_settings.pacing(conn)["ensure_rank"] == 30 settings.validate("pacing", {"ensure_rank": 10}) assert queue.estimate(conn, ["ensure_sprint_meta", "ensure_sprint_state", "ensure_rank"]) == 90 + + +def test_v3_keys_removed_on_open(conn): + v0 = settings.get(conn, "settings_version") + conn.execute("INSERT OR REPLACE INTO setting(key, value) VALUES ('board_id', '4216'), ('base_jql', '\"project = AURORA\"')") + settings.ensure_defaults(conn) + stored = {r["key"] for r in conn.execute("SELECT key FROM setting")} + assert {"board_id", "base_jql"}.isdisjoint(stored) + assert settings.get(conn, "settings_version") == v0 # версия настроек не растёт + with pytest.raises(ValueError, match="доска Jira — свойство проекта"): + settings.validate("board_id", 4216) + with pytest.raises(ValueError, match="собирается из проектов"): + settings.validate("base_jql", "project = AURORA") + + +def test_v3_keys_removed_on_hub_start_api(tmp_path): + import threading + + from sdlc_hub.core import api, db + from sdlc_hub.core.client import HubClient, HubError + path = tmp_path / "hub.sqlite" + c = db.connect(path) + settings.ensure_defaults(c) + c.execute("INSERT OR REPLACE INTO setting(key, value) VALUES ('board_id', '4216'), ('base_jql', '\"project = AURORA\"')") + c.commit() + c.close() + h = api.Hub(path, backups_dir=tmp_path / "backups") + conn = h.conn() + token = settings.tokens(conn)["client"] + conn.close() + srv = api.make_server(h, "127.0.0.1", 0) + th = threading.Thread(target=srv.serve_forever, daemon=True) + th.start() + try: + client = HubClient(f"http://127.0.0.1:{srv.server_port}", token) + got = client.get("/settings") + assert "board_id" not in got and got["base_jql"] != "project = AURORA" # base_jql вычисляется (без проектов — None) + for key in ("board_id", "base_jql"): + with pytest.raises(HubError) as e: + client.put("/settings", {key: 1}) + assert e.value.status == 422 and e.value.detail + finally: + srv.shutdown() + srv.server_close() + th.join(5)