From e025577d051458fefd2fc7a45df5f53ab0a0d90c Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Tue, 13 Oct 2026 00:37:59 +0400 Subject: [PATCH] =?UTF-8?q?feat(settings):=20=D0=BD=D0=B0=D1=81=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=B9=D0=BA=D0=B0=20relay=5Fdir=20=E2=80=94=20?= =?UTF-8?q?=D0=B0=D0=B1=D1=81=D0=BE=D0=BB=D1=8E=D1=82=D0=BD=D1=8B=D0=B9=20?= =?UTF-8?q?=D0=BF=D1=83=D1=82=D1=8C=20=D0=BF=D0=B0=D0=BF=D0=BA=D0=B8=20?= =?UTF-8?q?=D1=80=D0=B5=D0=BB=D0=B5=20=D0=B8=D0=BB=D0=B8=20null=20(#5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- src/jira_hub/settings.py | 7 +++++++ tests/test_api.py | 12 ++++++++++++ tests/test_settings.py | 13 +++++++++++++ 3 files changed, 32 insertions(+) diff --git a/src/jira_hub/settings.py b/src/jira_hub/settings.py index 6a063c0..fc91e75 100644 --- a/src/jira_hub/settings.py +++ b/src/jira_hub/settings.py @@ -1,6 +1,7 @@ """Настройки хаба: пары ключ → JSON в таблице setting. Любая правка поднимает settings_version, агент перечитывает настройки, когда видит новую версию в ответе claim.""" import json +import os import secrets DEFAULT_PACING = {"ensure_issue": 120, "ensure_fields": 120, "ensure_comment": 30, "ensure_status": 30, @@ -29,6 +30,7 @@ DEFAULTS = { "transitions_map": {}, "link_types": {}, "board_id": None, + "relay_dir": None, } # Что получает агент (без токенов и служебных ключей хаба) @@ -47,6 +49,7 @@ def validate(key, value) -> None: """Проверить значение настройки до записи; ValueError — на 422 в API. pacing — объект {тип команды: целое >= 0} только с известными типами команд; backup_keep, auto_refresh_minutes, min_agent_version — целые >= 0; port — целое 1..65535; + relay_dir — null или абсолютный путь строкой; остальные ключи — по типу значения по умолчанию (объект, список, строка, флаг), если оно не None; неизвестные ключи и ключи с умолчанием None принимаются как есть.""" if key == "pacing": @@ -66,6 +69,10 @@ def validate(key, value) -> None: if not _is_int(value) or value < 0: raise ValueError(f"{key} — целое >= 0") return + if key == "relay_dir": + if value is not None and (not isinstance(value, str) or not os.path.isabs(value)): + raise ValueError("relay_dir — абсолютный путь папки реле или null") + return default = DEFAULTS.get(key) if default is not None and type(value) is not type(default): names = {dict: "объект", list: "список", str: "строка", bool: "true/false", int: "целое"} diff --git a/tests/test_api.py b/tests/test_api.py index 62d3ec1..652468a 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -301,3 +301,15 @@ def test_full_refresh_marks_unseen_issue_as_neighbor(hub): # частичный refresh (по ключам) других задач ничего не помечает _run_refresh(hub, {"keys": ["A-1"]}, [{"key": "A-1", "summary": "т2", "labels": []}]) assert hub["client"].get("/issues/A-1")["neighbor"] is False + + +def test_relay_dir_put_validation(hub): + c = hub["client"] + before = c.get("/settings")["settings_version"] + with pytest.raises(HubError) as e: + c.put("/settings", {"relay_dir": "rshb/jira-bridge"}) + assert e.value.status == 422 + assert c.get("/settings")["settings_version"] == before + assert c.put("/settings", {"relay_dir": "/tmp/jira-bridge"})["settings_version"] == before + 1 + assert c.put("/settings", {"relay_dir": None})["settings_version"] == before + 2 + assert "relay_dir" not in hub["agent"].get("/agent/settings") diff --git a/tests/test_settings.py b/tests/test_settings.py index 6ec2466..7d689be 100644 --- a/tests/test_settings.py +++ b/tests/test_settings.py @@ -53,3 +53,16 @@ def test_init_from_config(): with pytest.raises(RuntimeError): settings.init_from_config(conn, cfg) settings.init_from_config(conn, cfg, force=True) + + +def test_relay_dir_default_and_validation(tmp_path): + conn = db.connect(tmp_path / "hub.sqlite") + settings.ensure_defaults(conn) + assert settings.get(conn, "relay_dir") is None + settings.validate("relay_dir", None) + settings.validate("relay_dir", "/Volumes/externalSSD/utm/rshb/jira-bridge") + for bad in ("rshb/jira-bridge", "", 5, ["/x"]): + with pytest.raises(ValueError): + settings.validate("relay_dir", bad) + assert "relay_dir" not in settings.AGENT_KEYS + conn.close()