141 lines
6.5 KiB
Python
141 lines
6.5 KiB
Python
import json
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
from sdlc_hub.core import queue, settings
|
||
from sdlc_hub.modules.jira import settings as jira_settings
|
||
from sdlc_hub.modules.jira.commands import CommandError
|
||
|
||
FIXTURE = Path(__file__).parent / "fixtures" / "config.example.json"
|
||
|
||
|
||
@pytest.fixture
|
||
def conn(open_db):
|
||
"""База в памяти с настройками по умолчанию; закрывается после теста."""
|
||
c = open_db()
|
||
settings.ensure_defaults(c)
|
||
return c
|
||
|
||
|
||
def test_defaults_and_tokens(conn):
|
||
assert settings.get(conn, "pacing")["ensure_fields"] == 120
|
||
tok = settings.tokens(conn)
|
||
assert len(tok["agents"]["jira"]) > 20 and tok["agents"]["jira"] != tok["client"]
|
||
assert "tokens" not in settings.get_all(conn)
|
||
|
||
|
||
def test_defaults_without_jql_and_board(conn):
|
||
assert "base_jql" not in settings.defaults() and "board_id" not in settings.defaults()
|
||
assert "base_jql" not in settings.get_all(conn) and "board_id" not in settings.get_all(conn)
|
||
assert "base_jql" not in settings.agent_keys("jira") and "board_id" not in settings.agent_keys("jira")
|
||
assert "board_id" not in settings.for_agent(conn, "jira") # мёртвый ключ: агент читает его через .get (design 6)
|
||
|
||
|
||
def test_jql_and_board_not_writable():
|
||
with pytest.raises(ValueError, match="собирается из проектов"):
|
||
settings.validate("base_jql", "project = AURORA")
|
||
with pytest.raises(ValueError, match="свойство проекта"):
|
||
settings.validate("board_id", 1)
|
||
|
||
|
||
def test_set_bumps_version(conn):
|
||
v0 = settings.get(conn, "settings_version")
|
||
v1 = settings.set_(conn, "pacing", {"ensure_fields": 5})
|
||
assert v1 == v0 + 1
|
||
assert settings.get(conn, "pacing") == {"ensure_fields": 5}
|
||
|
||
|
||
@pytest.mark.parametrize("key", ["tokens", "settings_version", "schema_version"])
|
||
def test_protected_keys(conn, key):
|
||
with pytest.raises(ValueError):
|
||
settings.set_(conn, key, "x")
|
||
|
||
|
||
def test_init_from_config(conn):
|
||
cfg = json.loads(FIXTURE.read_text(encoding="utf-8"))
|
||
keys = jira_settings.init_from_config(conn, cfg)
|
||
assert "link_types" in keys and "base_jql" not in keys and "board_id" not in keys
|
||
assert "transitions_map" not in keys and "status_labels" not in keys # fact-status-removal
|
||
assert {"transitions_map", "status_labels"}.isdisjoint(settings.get_all(conn))
|
||
assert "base_jql" not in settings.get_all(conn) and "board_id" not in settings.get_all(conn)
|
||
assert settings.get(conn, "field_ids")["planned_end"] == "customfield_10802"
|
||
assert settings.get(conn, "issue_types")["epic"] == {"name": "Фича", "id": "12300"}
|
||
assert settings.get(conn, "pacing") == jira_settings.DEFAULT_PACING
|
||
agent_view = settings.for_agent(conn, "jira")
|
||
assert "tokens" not in agent_view and "auth" not in agent_view and "base_url" not in agent_view
|
||
assert agent_view["link_types"]["связанные"] == "ASUPR_Связанные"
|
||
with pytest.raises(RuntimeError):
|
||
jira_settings.init_from_config(conn, cfg)
|
||
jira_settings.init_from_config(conn, cfg, force=True)
|
||
|
||
|
||
def test_relay_dir_default_and_validation(tmp_path, open_db):
|
||
conn = open_db(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("jira")
|
||
|
||
|
||
def test_init_without_bug_keys_rejects_bug(conn):
|
||
"""Спека data-model «Конфиг без ключей ошибки»: bug в issue_types нет, постановка bug — 422."""
|
||
cfg = json.loads(FIXTURE.read_text(encoding="utf-8"))
|
||
jira_settings.init_from_config(conn, cfg)
|
||
assert set(settings.get(conn, "issue_types")) == {"task", "epic"}
|
||
with pytest.raises(CommandError, match="нет настройки issue_types.bug"):
|
||
queue.create_batch(conn, "ошибка", "claude", [{"type": "ensure_issue", "params": {
|
||
"temp_key": "new:1", "issue_type": "bug", "summary": "x", "labels": ["AWPCA"]}}])
|
||
|
||
|
||
@pytest.mark.parametrize("extra, expected", [
|
||
({"issue_type_bug": "Ошибка"}, {"name": "Ошибка", "id": None}),
|
||
({"issue_type_bug_id": "10102"}, {"name": None, "id": "10102"}),
|
||
({"issue_type_bug": "Ошибка", "issue_type_bug_id": "10102"}, {"name": "Ошибка", "id": "10102"}),
|
||
])
|
||
def test_init_bug_from_config(conn, extra, expected):
|
||
cfg = {**json.loads(FIXTURE.read_text(encoding="utf-8")), **extra}
|
||
jira_settings.init_from_config(conn, cfg)
|
||
types = settings.get(conn, "issue_types")
|
||
assert types["bug"] == expected
|
||
assert types["task"] == {"name": "Задача", "id": "10100"} and types["epic"] == {"name": "Фича", "id": "12300"}
|
||
|
||
|
||
def test_attachment_pacing(conn):
|
||
assert jira_settings.DEFAULT_PACING["ensure_attachment"] == 30
|
||
assert jira_settings.pacing(conn)["ensure_attachment"] == 30
|
||
assert jira_settings.pacing_from_config({"issue_seconds": 90, "comment_seconds": 20})["ensure_attachment"] == 20
|
||
settings.validate("pacing", {"ensure_attachment": 10})
|
||
|
||
|
||
def test_closed_statuses_default(conn):
|
||
assert settings.get(conn, "closed_statuses") == ["Готово", "Снято"]
|
||
assert "closed_statuses" not in settings.agent_keys("jira")
|
||
assert "closed_statuses" not in settings.for_agent(conn, "jira")
|
||
|
||
|
||
def test_closed_statuses_must_be_list():
|
||
with pytest.raises(ValueError):
|
||
settings.validate("closed_statuses", "Готово")
|
||
settings.validate("closed_statuses", ["Готово"])
|
||
|
||
|
||
def test_pacing_fallback_for_new_type(conn):
|
||
settings.set_(conn, "pacing", {"ensure_fields": 5})
|
||
assert jira_settings.pacing(conn)["fetch_attachment"] == 2
|
||
settings.validate("pacing", {"fetch_attachment": 3})
|
||
|
||
|
||
def test_sprint_types_pacing(conn):
|
||
for t in ("ensure_sprint_meta", "ensure_sprint_state", "ensure_rank"):
|
||
assert jira_settings.DEFAULT_PACING[t] == 30 and jira_settings.pacing(conn)[t] == 30
|
||
assert jira_settings.pacing_from_config({"comment_seconds": 45})[t] == 45
|
||
settings.set_(conn, "pacing", {"ensure_fields": 5}) # тип не в сохранённой настройке — умолчание
|
||
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
|