import json from pathlib import Path import pytest from jira_hub import queue, settings from jira_hub.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, "base_jql") == "project = AURORA AND labels = AWPCA" assert settings.get(conn, "pacing")["ensure_fields"] == 120 tok = settings.tokens(conn) assert len(tok["agent"]) > 20 and tok["agent"] != tok["client"] assert "tokens" not in settings.get_all(conn) 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 = settings.init_from_config(conn, cfg) assert "transitions_map" in keys assert settings.get(conn, "field_ids")["planned_end"] == "customfield_10802" assert settings.get(conn, "issue_types")["epic"] == {"name": "Фича", "id": "12300"} assert settings.get(conn, "board_id") == 4216 assert settings.get(conn, "pacing") == settings.DEFAULT_PACING agent_view = settings.for_agent(conn) 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): settings.init_from_config(conn, cfg) 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 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")) 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} 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 settings.DEFAULT_PACING["ensure_attachment"] == 30 assert settings.pacing(conn)["ensure_attachment"] == 30 assert 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 assert "closed_statuses" not in settings.for_agent(conn) def test_closed_statuses_must_be_list(): with pytest.raises(ValueError): settings.validate("closed_statuses", "Готово") settings.validate("closed_statuses", ["Готово"])