refactor: пакеты core и modules.jira — перенос без логики (#87)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
808d7b710c
commit
6d7aebe01c
@ -1,87 +1 @@
|
||||
"""Команды jira-hub: init (настройки из config.example.json), serve (HTTP), import (CSV-пакет), mcp (stdio), tokens,
|
||||
agent publish (версия агента для самообновления)."""
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
|
||||
from . import db, importer, paths, projects, settings
|
||||
|
||||
|
||||
def main(argv=None) -> int:
|
||||
ap = argparse.ArgumentParser(prog="jira-hub", description="Мост Jira для АРМ КА: хаб")
|
||||
sub = ap.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("init", help="перенести настройки проекта из config.example.json, создать токены")
|
||||
p.add_argument("--config", required=True)
|
||||
p.add_argument("--force", action="store_true")
|
||||
p = sub.add_parser("serve", help="запустить HTTP API")
|
||||
p.add_argument("--host", default="0.0.0.0")
|
||||
p.add_argument("--port", type=int)
|
||||
p = sub.add_parser("import", help="импорт CSV-пакета admin/jira-sync в заметки")
|
||||
p.add_argument("folder")
|
||||
p = sub.add_parser("mcp", help="MCP-сервер по stdio")
|
||||
p.add_argument("--project", help="метка проекта, к которому привязана сессия")
|
||||
sub.add_parser("tokens", help="показать токены агента и клиента")
|
||||
p = sub.add_parser("agent", help="версия агента для самообновления")
|
||||
agent_sub = p.add_subparsers(dest="agent_cmd", required=True)
|
||||
p = agent_sub.add_parser("publish", help="опубликовать agent.py (версия только вверх)")
|
||||
p.add_argument("path")
|
||||
args = ap.parse_args(argv)
|
||||
|
||||
if args.cmd == "agent":
|
||||
from . import release
|
||||
try:
|
||||
m = release.publish(paths.release_dir(), args.path)
|
||||
except release.PublishError as e:
|
||||
print(e, file=sys.stderr)
|
||||
return 1
|
||||
print(f"опубликована v{m['version']}, sha256 {m['sha256'][:12]}…, {m['size']} байт")
|
||||
return 0
|
||||
|
||||
if args.cmd == "serve":
|
||||
from . import api
|
||||
hub = api.Hub(paths.db_path())
|
||||
conn = hub.conn()
|
||||
port = args.port or int(settings.get(conn, "port") or 8790)
|
||||
conn.close()
|
||||
api.serve(hub, args.host, port)
|
||||
return 0
|
||||
if args.cmd == "mcp":
|
||||
from . import mcp_server
|
||||
mcp_server.run(args.project)
|
||||
return 0
|
||||
|
||||
conn = db.connect(paths.db_path())
|
||||
settings.ensure_defaults(conn)
|
||||
try:
|
||||
if args.cmd == "init":
|
||||
with open(args.config, encoding="utf-8") as f:
|
||||
cfg = json.load(f)
|
||||
try:
|
||||
keys = settings.init_from_config(conn, cfg, force=args.force)
|
||||
except RuntimeError as e:
|
||||
print(e, file=sys.stderr)
|
||||
return 1
|
||||
print("перенесено: " + ", ".join(keys))
|
||||
if projects.ensure_legacy(conn, cfg.get("sprints", {}).get("board_id")):
|
||||
board = projects.get(conn, projects.LEGACY_LABEL)["board_id"]
|
||||
print(f"проект {projects.LEGACY_LABEL} «{projects.LEGACY_NAME}», доска {board}")
|
||||
_print_tokens(conn)
|
||||
return 0
|
||||
if args.cmd == "import":
|
||||
print(json.dumps(importer.import_package(conn, args.folder), ensure_ascii=False, indent=1))
|
||||
return 0
|
||||
if args.cmd == "tokens":
|
||||
_print_tokens(conn)
|
||||
return 0
|
||||
finally:
|
||||
conn.close()
|
||||
return 2
|
||||
|
||||
|
||||
def _print_tokens(conn) -> None:
|
||||
tok = settings.tokens(conn)
|
||||
print(f"агентский токен: {tok['agent']}\nклиентский токен: {tok['client']}\nбаза: {paths.db_path()}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
from .core.cli import main
|
||||
|
||||
0
src/jira_hub/core/__init__.py
Normal file
0
src/jira_hub/core/__init__.py
Normal file
@ -15,8 +15,9 @@ from pathlib import Path
|
||||
from typing import NamedTuple
|
||||
from urllib.parse import parse_qs, quote, unquote, urlparse
|
||||
|
||||
from . import attachments, backup, db, files, history, mirror, projects, queue, relay, release, settings
|
||||
from .commands import CommandError
|
||||
from . import backup, db, files, projects, queue, relay, release, settings
|
||||
from ..modules.jira import attachments, history, mirror
|
||||
from ..modules.jira.commands import CommandError
|
||||
from .paths import backups_dir as default_backups_dir
|
||||
from .paths import secure_dir
|
||||
from .queue import ClaimedError
|
||||
@ -24,7 +25,7 @@ from .queue import ClaimedError
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
MAX_BODY = 16 * 1024 * 1024 # предел тела запроса, байт
|
||||
WEB_APP = Path(__file__).parent / "web" / "app" # сборка веб-приложения (make web, не в git); читается на каждый запрос
|
||||
WEB_APP = Path(__file__).resolve().parent.parent / "web" / "app" # сборка веб-приложения (make web, не в git); читается на каждый запрос
|
||||
ASSET_TYPES = {".js": "text/javascript; charset=utf-8", ".css": "text/css; charset=utf-8", ".svg": "image/svg+xml",
|
||||
".png": "image/png", ".ico": "image/x-icon", ".woff2": "font/woff2", ".map": "application/json"}
|
||||
ASSET_CACHE = {"Cache-Control": "public, max-age=31536000, immutable"} # имена файлов сборки содержат хэш (R-3)
|
||||
88
src/jira_hub/core/cli.py
Normal file
88
src/jira_hub/core/cli.py
Normal file
@ -0,0 +1,88 @@
|
||||
"""Команды jira-hub: init (настройки из config.example.json), serve (HTTP), import (CSV-пакет), mcp (stdio), tokens,
|
||||
agent publish (версия агента для самообновления)."""
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
|
||||
from . import db, paths, projects, settings
|
||||
from ..modules.jira import importer
|
||||
|
||||
|
||||
def main(argv=None) -> int:
|
||||
ap = argparse.ArgumentParser(prog="jira-hub", description="Мост Jira для АРМ КА: хаб")
|
||||
sub = ap.add_subparsers(dest="cmd", required=True)
|
||||
p = sub.add_parser("init", help="перенести настройки проекта из config.example.json, создать токены")
|
||||
p.add_argument("--config", required=True)
|
||||
p.add_argument("--force", action="store_true")
|
||||
p = sub.add_parser("serve", help="запустить HTTP API")
|
||||
p.add_argument("--host", default="0.0.0.0")
|
||||
p.add_argument("--port", type=int)
|
||||
p = sub.add_parser("import", help="импорт CSV-пакета admin/jira-sync в заметки")
|
||||
p.add_argument("folder")
|
||||
p = sub.add_parser("mcp", help="MCP-сервер по stdio")
|
||||
p.add_argument("--project", help="метка проекта, к которому привязана сессия")
|
||||
sub.add_parser("tokens", help="показать токены агента и клиента")
|
||||
p = sub.add_parser("agent", help="версия агента для самообновления")
|
||||
agent_sub = p.add_subparsers(dest="agent_cmd", required=True)
|
||||
p = agent_sub.add_parser("publish", help="опубликовать agent.py (версия только вверх)")
|
||||
p.add_argument("path")
|
||||
args = ap.parse_args(argv)
|
||||
|
||||
if args.cmd == "agent":
|
||||
from . import release
|
||||
try:
|
||||
m = release.publish(paths.release_dir(), args.path)
|
||||
except release.PublishError as e:
|
||||
print(e, file=sys.stderr)
|
||||
return 1
|
||||
print(f"опубликована v{m['version']}, sha256 {m['sha256'][:12]}…, {m['size']} байт")
|
||||
return 0
|
||||
|
||||
if args.cmd == "serve":
|
||||
from . import api
|
||||
hub = api.Hub(paths.db_path())
|
||||
conn = hub.conn()
|
||||
port = args.port or int(settings.get(conn, "port") or 8790)
|
||||
conn.close()
|
||||
api.serve(hub, args.host, port)
|
||||
return 0
|
||||
if args.cmd == "mcp":
|
||||
from . import mcp_server
|
||||
mcp_server.run(args.project)
|
||||
return 0
|
||||
|
||||
conn = db.connect(paths.db_path())
|
||||
settings.ensure_defaults(conn)
|
||||
try:
|
||||
if args.cmd == "init":
|
||||
with open(args.config, encoding="utf-8") as f:
|
||||
cfg = json.load(f)
|
||||
try:
|
||||
keys = settings.init_from_config(conn, cfg, force=args.force)
|
||||
except RuntimeError as e:
|
||||
print(e, file=sys.stderr)
|
||||
return 1
|
||||
print("перенесено: " + ", ".join(keys))
|
||||
if projects.ensure_legacy(conn, cfg.get("sprints", {}).get("board_id")):
|
||||
board = projects.get(conn, projects.LEGACY_LABEL)["board_id"]
|
||||
print(f"проект {projects.LEGACY_LABEL} «{projects.LEGACY_NAME}», доска {board}")
|
||||
_print_tokens(conn)
|
||||
return 0
|
||||
if args.cmd == "import":
|
||||
print(json.dumps(importer.import_package(conn, args.folder), ensure_ascii=False, indent=1))
|
||||
return 0
|
||||
if args.cmd == "tokens":
|
||||
_print_tokens(conn)
|
||||
return 0
|
||||
finally:
|
||||
conn.close()
|
||||
return 2
|
||||
|
||||
|
||||
def _print_tokens(conn) -> None:
|
||||
tok = settings.tokens(conn)
|
||||
print(f"агентский токен: {tok['agent']}\nклиентский токен: {tok['client']}\nбаза: {paths.db_path()}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@ -15,9 +15,9 @@ from pathlib import Path
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
from . import paths
|
||||
from .attachments import fmt_size
|
||||
from ..modules.jira.attachments import fmt_size
|
||||
from .client import HubClient, HubError, HubUnavailable
|
||||
from .commands import describe, is_temp
|
||||
from ..modules.jira.commands import describe, is_temp
|
||||
from .files import MAX_SIZE
|
||||
|
||||
OFFLINE = "хаб не запущен: jira-hub serve"
|
||||
@ -25,7 +25,7 @@ POLL_SECONDS = 5
|
||||
AUTHOR = "claude"
|
||||
BOTH_FILE_AND_PATH = "у ensure_attachment либо path, либо file"
|
||||
NOT_BOUND = "сессия не привязана к проекту: запустите MCP с --project <метка> (список — projects)"
|
||||
HUB_ROOT = Path(__file__).resolve().parents[2] # корень репозитория хаба, из которого загружен пакет (решение 19)
|
||||
HUB_ROOT = Path(__file__).resolve().parents[3] # корень репозитория хаба, из которого загружен пакет (решение 19)
|
||||
PROJECT: str | None = None # привязка сессии, задаётся run(project)
|
||||
mcp = FastMCP("jira-hub")
|
||||
|
||||
@ -6,8 +6,9 @@ import logging
|
||||
from datetime import timedelta
|
||||
|
||||
from . import backup as _backup # noqa: F401 (имя модуля занято параметром backup в claim)
|
||||
from . import commands as C
|
||||
from . import attachments, db, files, history, mirror, projects, settings
|
||||
from ..modules.jira import commands as C
|
||||
from . import db, files, projects, settings
|
||||
from ..modules.jira import attachments, history, mirror
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
0
src/jira_hub/modules/__init__.py
Normal file
0
src/jira_hub/modules/__init__.py
Normal file
0
src/jira_hub/modules/jira/__init__.py
Normal file
0
src/jira_hub/modules/jira/__init__.py
Normal file
@ -3,7 +3,7 @@
|
||||
базу и хранилище файлов, очередь и HTTP не знает."""
|
||||
import re
|
||||
|
||||
from . import files
|
||||
from ...core import files
|
||||
|
||||
# lookahead: совпадения перекрываются, поэтому одиночный «!» ранее в тексте не съедает открывающий «!» настоящей ссылки;
|
||||
# после открывающего «!» пробела нет (как в Jira), иначе закрывающий «!» одной картинки породил бы ложное имя
|
||||
@ -2,7 +2,7 @@
|
||||
refresh. Пишет таблицу только приём журнала (POST /agent/history); API и MCP её только читают (spec data-model)."""
|
||||
from datetime import timedelta
|
||||
|
||||
from . import db, projects, settings
|
||||
from ...core import db, projects, settings
|
||||
|
||||
SINCE_MARGIN = timedelta(minutes=15) # расхождение часов Mac mini и Jira и длительность прогона (design #5)
|
||||
|
||||
@ -4,7 +4,8 @@
|
||||
import csv
|
||||
from pathlib import Path
|
||||
|
||||
from . import db, mirror, settings
|
||||
from . import mirror
|
||||
from ...core import db, settings
|
||||
|
||||
|
||||
def _rows(path: Path) -> list[dict]:
|
||||
@ -2,7 +2,8 @@
|
||||
Зеркало пишет только загрузка состояния от агента; заметки обновление зеркала не трогает."""
|
||||
import json
|
||||
|
||||
from . import attachments, db, history
|
||||
from . import attachments, history
|
||||
from ...core import db
|
||||
|
||||
ISSUE_COLUMNS = ["key", "id", "issue_type", "summary", "description", "status", "priority", "assignee", "reporter",
|
||||
"duedate", "planned_start", "planned_end", "epic_key", "labels", "sprint", "created", "jira_updated"]
|
||||
@ -2,7 +2,7 @@
|
||||
ResourceWarning «unclosed database» (hub-hygiene-d, решение 14; M-9/O-4 #4)."""
|
||||
import pytest
|
||||
|
||||
from jira_hub import db, projects
|
||||
from jira_hub.core import db, projects
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@ -11,9 +11,9 @@ from datetime import timedelta
|
||||
|
||||
import pytest
|
||||
|
||||
from jira_hub import api, db, files, projects, queue, release, settings
|
||||
from jira_hub.client import HubClient, HubError
|
||||
from jira_hub.commands import CommandError
|
||||
from jira_hub.core import api, db, files, projects, queue, release, settings
|
||||
from jira_hub.core.client import HubClient, HubError
|
||||
from jira_hub.modules.jira.commands import CommandError
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -157,7 +157,7 @@ def test_backup_created_on_first_write(hub, tmp_path):
|
||||
|
||||
|
||||
def test_client_unavailable():
|
||||
from jira_hub.client import HubUnavailable
|
||||
from jira_hub.core.client import HubUnavailable
|
||||
with pytest.raises(HubUnavailable):
|
||||
HubClient("http://127.0.0.1:9", "t", timeout=1).get("/issues")
|
||||
|
||||
@ -1040,7 +1040,7 @@ ATT_NAME = "скрин 1.png"
|
||||
def _seed_attachment(hub, mime="image/png", size=3, name=ATT_NAME):
|
||||
conn = hub["hub"].conn()
|
||||
try:
|
||||
from jira_hub import mirror
|
||||
from jira_hub.modules.jira import mirror
|
||||
mirror.apply_state(conn, [{"key": "A-1", "summary": "т", "labels": [], "attachments": [
|
||||
{"id": "31337", "filename": name, "size": size, "mime_type": mime, "created": "2026-10-20T10:00:00.000+0300"}]}], [])
|
||||
finally:
|
||||
|
||||
@ -2,7 +2,8 @@ import json
|
||||
|
||||
import pytest
|
||||
|
||||
from jira_hub import attachments, db, files, mirror
|
||||
from jira_hub.core import db, files
|
||||
from jira_hub.modules.jira import attachments, mirror
|
||||
|
||||
|
||||
def test_fmt_size():
|
||||
|
||||
@ -5,7 +5,7 @@ import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from jira_hub import api, db, mcp_server, settings
|
||||
from jira_hub.core import api, db, mcp_server, settings
|
||||
|
||||
FIXTURES = Path(__file__).parent / "fixtures" / "baseline"
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from jira_hub import cli, paths, projects, release, settings
|
||||
from jira_hub.core import cli, paths, projects, release, settings
|
||||
|
||||
FIXTURE = Path(__file__).parent / "fixtures" / "config.example.json"
|
||||
|
||||
@ -67,7 +67,7 @@ def test_agent_publish_write_failure(tmp_path, monkeypatch, capsys):
|
||||
|
||||
|
||||
def test_mcp_project_option(monkeypatch):
|
||||
from jira_hub import mcp_server
|
||||
from jira_hub.core import mcp_server
|
||||
seen = []
|
||||
monkeypatch.setattr(mcp_server, "run", lambda project=None: seen.append(project))
|
||||
assert cli.main(["mcp", "--project", "AWPCA"]) == 0
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import pytest
|
||||
|
||||
from jira_hub import commands, mirror
|
||||
from jira_hub.commands import CommandError
|
||||
from jira_hub.modules.jira import commands, mirror
|
||||
from jira_hub.modules.jira.commands import CommandError
|
||||
|
||||
NOW = "strftime('%Y-%m-%dT%H:%M:%S.000+0300', 'now')"
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import threading
|
||||
|
||||
import pytest
|
||||
|
||||
from jira_hub import backup, db, paths, projects, queue, settings
|
||||
from jira_hub.core import backup, db, paths, projects, queue, settings
|
||||
|
||||
|
||||
def test_connect_creates_schema(open_db):
|
||||
@ -154,7 +154,7 @@ def test_data_dir_is_private(tmp_path):
|
||||
|
||||
|
||||
def test_existing_backups_dir_made_private_on_start(tmp_path):
|
||||
from jira_hub import api
|
||||
from jira_hub.core import api
|
||||
backups = tmp_path / "backups"
|
||||
backups.mkdir()
|
||||
backups.chmod(0o755)
|
||||
|
||||
@ -2,8 +2,8 @@ import threading
|
||||
|
||||
import pytest
|
||||
|
||||
from jira_hub import api, settings
|
||||
from jira_hub.client import HubClient, HubError
|
||||
from jira_hub.core import api, settings
|
||||
from jira_hub.core.client import HubClient, HubError
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@ -7,7 +7,7 @@ import time
|
||||
|
||||
import pytest
|
||||
|
||||
from jira_hub import db, files, settings
|
||||
from jira_hub.core import db, files, settings
|
||||
|
||||
ABC = hashlib.sha256(b"abc").hexdigest() # ba7816bf…
|
||||
DAY = 86400
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import pytest
|
||||
|
||||
from jira_hub import history
|
||||
from jira_hub.modules.jira import history
|
||||
|
||||
ITEM = {"history_id": "5001", "seq": 0, "key": "X", "created": "2026-10-22T09:00:00.000+0300", "author": "Петров Пётр",
|
||||
"field_id": "status", "field_name": "status", "from": "3", "from_string": "В работе", "to": "10001",
|
||||
@ -29,7 +29,7 @@ def test_record_keeps_first_version(conn):
|
||||
|
||||
|
||||
def test_record_uses_field_settings(conn):
|
||||
from jira_hub import settings
|
||||
from jira_hub.core import settings
|
||||
settings.set_(conn, "field_ids", {"planned_end": "customfield_10201"})
|
||||
history.record_changes(conn, [{**ITEM, "field_id": "customfield_10201", "field_name": "Плановое окончание"}])
|
||||
assert conn.execute("SELECT field FROM issue_change").fetchone()["field"] == "planned_end"
|
||||
@ -92,7 +92,7 @@ def test_feed_closed_in_two_hours(conn): # сце
|
||||
|
||||
|
||||
def test_feed_comment(conn): # «Комментарий в ленте»
|
||||
from jira_hub import mirror
|
||||
from jira_hub.modules.jira import mirror
|
||||
mirror.apply_state(conn, [{"key": "X", "labels": ["AWPCA"], "comments": [
|
||||
{"id": "7", "author": "Иванов", "body": "Готово к проверке", "created": "2026-10-22T13:30:00.000+0300"}]}], [])
|
||||
got = history.feed(conn, "2026-10-22T10:00:00+00:00", "2026-10-22T12:00:00+00:00", history.DEFAULT_KINDS)
|
||||
@ -163,7 +163,7 @@ def test_parse_kinds_unknown():
|
||||
|
||||
|
||||
def test_issue_changes(conn): # «Задача с историей»
|
||||
from jira_hub import mirror
|
||||
from jira_hub.modules.jira import mirror
|
||||
_issue(conn, "X", ["AWPCA"])
|
||||
_change(conn, "X", "1", "2026-10-21T11:00:00Z", "status", "a", "b")
|
||||
_change(conn, "X", "2", "2026-10-22T11:00:00Z", "description", "c", "d", field_id="description")
|
||||
|
||||
@ -2,7 +2,8 @@ import csv
|
||||
|
||||
import pytest
|
||||
|
||||
from jira_hub import importer, mirror, settings
|
||||
from jira_hub.core import settings
|
||||
from jira_hub.modules.jira import importer, mirror
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
23
tests/test_layout.py
Normal file
23
tests/test_layout.py
Normal file
@ -0,0 +1,23 @@
|
||||
"""Раскладка пакетов: чистый импорт и пути, зависящие от места файла."""
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import jira_hub
|
||||
|
||||
|
||||
def test_fresh_imports():
|
||||
r = subprocess.run(
|
||||
[sys.executable, "-c", "import jira_hub.core.api, jira_hub.core.mcp_server, jira_hub.cli"],
|
||||
capture_output=True, text=True)
|
||||
assert r.returncode == 0, r.stderr
|
||||
|
||||
|
||||
def test_web_app_path():
|
||||
from jira_hub.core import api
|
||||
assert api.WEB_APP == Path(jira_hub.__file__).parent / "web" / "app"
|
||||
|
||||
|
||||
def test_hub_root():
|
||||
from jira_hub.core import mcp_server
|
||||
assert (mcp_server.HUB_ROOT / "pyproject.toml").is_file()
|
||||
@ -9,8 +9,9 @@ from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from jira_hub import api, commands, files, mcp_server, settings
|
||||
from jira_hub.client import HubClient
|
||||
from jira_hub.core import api, files, mcp_server, settings
|
||||
from jira_hub.modules.jira import commands
|
||||
from jira_hub.core.client import HubClient
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -811,7 +812,7 @@ def test_attachment_failed(downloads, monkeypatch):
|
||||
|
||||
|
||||
def test_attachment_413(downloads, monkeypatch):
|
||||
from jira_hub.client import HubError
|
||||
from jira_hub.core.client import HubError
|
||||
att = _att("5", "запись.mp4", 26214400)
|
||||
hub = FakeHub(_detail(key=ATT_KEY, attachments=[att]), fetch=HubError(413, "too_large", "большой"))
|
||||
_use(monkeypatch, hub)
|
||||
@ -820,7 +821,7 @@ def test_attachment_413(downloads, monkeypatch):
|
||||
|
||||
|
||||
def test_attachment_409_passes_hub_text(downloads, monkeypatch):
|
||||
from jira_hub.client import HubError
|
||||
from jira_hub.core.client import HubError
|
||||
hub = FakeHub(_detail(key=ATT_KEY, attachments=[_att("5", "a.png", 1)]),
|
||||
fetch=HubError(409, "agent_too_old", "агент v9 не умеет скачивать вложения — нужен v14"))
|
||||
_use(monkeypatch, hub)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import pytest
|
||||
|
||||
from jira_hub import db, mirror
|
||||
from jira_hub.core import db
|
||||
from jira_hub.modules.jira import mirror
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import pytest
|
||||
|
||||
from jira_hub import mirror, projects, settings
|
||||
from jira_hub.core import projects, settings
|
||||
from jira_hub.modules.jira import mirror
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@ -2,9 +2,10 @@ import hashlib
|
||||
|
||||
import pytest
|
||||
|
||||
from jira_hub import attachments, db, files, mirror, projects, queue, settings
|
||||
from jira_hub.commands import CommandError
|
||||
from jira_hub.queue import ClaimedError
|
||||
from jira_hub.core import db, files, projects, queue, settings
|
||||
from jira_hub.modules.jira import attachments, mirror
|
||||
from jira_hub.modules.jira.commands import CommandError
|
||||
from jira_hub.core.queue import ClaimedError
|
||||
|
||||
LT = {"связанные": "ASUPR_Связанные"}
|
||||
|
||||
@ -207,7 +208,7 @@ def test_no_backup_for_refresh_batch(conn):
|
||||
|
||||
|
||||
def test_make_backup_rotates(tmp_path, open_db):
|
||||
from jira_hub import backup
|
||||
from jira_hub.core import backup
|
||||
conn = open_db(tmp_path / "hub.sqlite")
|
||||
for i in range(4):
|
||||
backup.make_backup(conn, tmp_path / "backups", f"b{i}", keep=2)
|
||||
@ -218,7 +219,7 @@ def test_make_backup_rotates(tmp_path, open_db):
|
||||
|
||||
def test_rotation_keeps_schema_migration_copy(tmp_path, open_db):
|
||||
"""Копия базы перед миграцией схемы не участвует в ротации копий пакетов (M-1 финального ревью)."""
|
||||
from jira_hub import backup
|
||||
from jira_hub.core import backup
|
||||
conn = open_db(tmp_path / "hub.sqlite")
|
||||
backup.make_backup(conn, tmp_path / "backups", "schema-v1", keep=0)
|
||||
for i in range(4):
|
||||
|
||||
@ -8,8 +8,8 @@ from datetime import datetime, timedelta, timezone
|
||||
|
||||
import pytest
|
||||
|
||||
from jira_hub import api, relay, settings
|
||||
from jira_hub.client import HubClient
|
||||
from jira_hub.core import api, relay, settings
|
||||
from jira_hub.core.client import HubClient
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
||||
@ -6,7 +6,7 @@ from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from jira_hub import release
|
||||
from jira_hub.core import release
|
||||
|
||||
|
||||
def agent_file(tmp_path, version, body="print('x')\n", name="agent.py"):
|
||||
|
||||
@ -3,8 +3,8 @@ from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from jira_hub import queue, settings
|
||||
from jira_hub.commands import CommandError
|
||||
from jira_hub.core import queue, settings
|
||||
from jira_hub.modules.jira.commands import CommandError
|
||||
|
||||
FIXTURE = Path(__file__).parent / "fixtures" / "config.example.json"
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ from urllib.parse import urlparse
|
||||
|
||||
import pytest
|
||||
|
||||
from jira_hub import api
|
||||
from jira_hub.core import api
|
||||
|
||||
|
||||
def _get(base, path):
|
||||
|
||||
@ -5,7 +5,8 @@ import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from jira_hub import db, mirror, paths, settings
|
||||
from jira_hub.core import db, paths, settings
|
||||
from jira_hub.modules.jira import mirror
|
||||
|
||||
snap = json.loads(Path(sys.argv[1]).read_text(encoding="utf-8"))
|
||||
extras_path = Path(__file__).with_name("seed-extras.json")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user