fix: финальное ревью — M-1/T3.1 атомарная подмена agent_update, T1.1/T1.2 open_db, T2.3, T4.1 (#32)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
1f1dad6911
commit
7d3f648d2c
@ -77,15 +77,17 @@ class Hub:
|
||||
def note_agent_update(self, body):
|
||||
"""update_state и launcher_version из claim: хранятся в памяти, агент шлёт их в каждом claim (R-3).
|
||||
Значение неверного типа игнорируется — claim из-за него не отвергается; сам claim отмечается (reported)."""
|
||||
self.agent_update["reported"] = True
|
||||
new = dict(self.agent_update)
|
||||
new["reported"] = True
|
||||
us = body.get("update_state")
|
||||
if us is None or isinstance(us, dict):
|
||||
if us is not None and isinstance(us.get("error"), str):
|
||||
us = {**us, "error": us["error"][:500]}
|
||||
self.agent_update["update_state"] = us
|
||||
new["update_state"] = us
|
||||
lv = body.get("launcher_version")
|
||||
if lv is None or (isinstance(lv, int) and not isinstance(lv, bool)):
|
||||
self.agent_update["launcher_version"] = lv
|
||||
new["launcher_version"] = lv
|
||||
self.agent_update = new
|
||||
|
||||
def make_backup(self, label):
|
||||
conn = self.conn()
|
||||
|
||||
@ -2,19 +2,19 @@ import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
from jira_hub import cli, db, paths, release, settings
|
||||
from jira_hub import cli, paths, release, settings
|
||||
|
||||
FIXTURE = Path(__file__).parent / "fixtures" / "config.example.json"
|
||||
|
||||
|
||||
def test_init_import_tokens(tmp_path, monkeypatch, capsys):
|
||||
def test_init_import_tokens(tmp_path, monkeypatch, capsys, open_db):
|
||||
monkeypatch.setenv("JIRA_HUB_HOME", str(tmp_path / "home"))
|
||||
assert cli.main(["init", "--config", str(FIXTURE)]) == 0
|
||||
out = capsys.readouterr().out
|
||||
assert "transitions_map" in out and "агентский токен" in out
|
||||
assert cli.main(["init", "--config", str(FIXTURE)]) == 1
|
||||
assert "уже инициализирован" in capsys.readouterr().err
|
||||
conn = db.connect(paths.db_path())
|
||||
conn = open_db(paths.db_path())
|
||||
assert settings.get(conn, "board_id") == 4216
|
||||
(tmp_path / "pkg").mkdir()
|
||||
(tmp_path / "pkg" / "tasks.csv").write_text("Код,Статус по факту,Вид работы\nAURORA-1,В работе,UI\n", encoding="utf-8")
|
||||
@ -23,7 +23,6 @@ def test_init_import_tokens(tmp_path, monkeypatch, capsys):
|
||||
assert rep["notes"] == 1 and rep["missing_keys"] == ["AURORA-1"]
|
||||
assert cli.main(["tokens"]) == 0
|
||||
assert settings.tokens(conn)["client"] in capsys.readouterr().out
|
||||
conn.close()
|
||||
|
||||
|
||||
def test_agent_publish(tmp_path, monkeypatch, capsys):
|
||||
|
||||
@ -162,6 +162,14 @@ def test_ensure_fields_text_compare_ignores_crlf(conn, mirror_value, param, expe
|
||||
assert ok is expected
|
||||
|
||||
|
||||
def test_ensure_fields_summary_difference_is_detected(conn):
|
||||
"""T4.1 #32: CRLF в summary игнорируется, но реальное расхождение текста — False."""
|
||||
conn.execute("UPDATE issue SET summary = ? WHERE key = 'A-1'", ("x\r\ny",))
|
||||
ok, _ = commands.check_confirmed(conn, {"type": "ensure_fields",
|
||||
"params": {"key": "A-1", "summary": "x\nz"}}, LT)
|
||||
assert ok is False
|
||||
|
||||
|
||||
@pytest.mark.parametrize("ctype, name, value, msg", [
|
||||
("ensure_fields", "duedate", True, "duedate — строка"),
|
||||
("ensure_fields", "labels_add", "AWPCA", "labels_add — список строк"),
|
||||
|
||||
@ -186,6 +186,8 @@ def test_restore_failure_is_logged_not_raised(tmp_path, monkeypatch, caplog):
|
||||
"""Review Focus 2: и возврат agent.py.prev не удался — PublishError с текстом, причина возврата в логе."""
|
||||
rd = tmp_path / "release"
|
||||
release.publish(rd, agent_file(tmp_path, 8))
|
||||
before = _listing(rd)
|
||||
new_src = agent_file(tmp_path, 9)
|
||||
_fail_replace_into(monkeypatch, "manifest.json")
|
||||
real = release.os.replace
|
||||
|
||||
@ -196,8 +198,14 @@ def test_restore_failure_is_logged_not_raised(tmp_path, monkeypatch, caplog):
|
||||
|
||||
monkeypatch.setattr(release.os, "replace", fake)
|
||||
with pytest.raises(release.PublishError, match="не удалось записать публикацию"):
|
||||
release.publish(rd, agent_file(tmp_path, 9))
|
||||
release.publish(rd, new_src)
|
||||
assert "прежняя публикация не восстановлена" in caplog.text
|
||||
monkeypatch.undo()
|
||||
after = _listing(rd) # задокументированное состояние: новый agent.py, старый agent.py.prev, старый манифест
|
||||
assert sorted(after) == ["agent.py", "agent.py.prev", "manifest.json"]
|
||||
assert after["agent.py"] == new_src.read_bytes()
|
||||
assert after["agent.py.prev"] == before["agent.py"]
|
||||
assert after["manifest.json"] == before["manifest.json"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("manifest", [
|
||||
|
||||
@ -3,7 +3,7 @@ from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from jira_hub import db, settings
|
||||
from jira_hub import settings
|
||||
|
||||
FIXTURE = Path(__file__).parent / "fixtures" / "config.example.json"
|
||||
|
||||
@ -53,8 +53,8 @@ def test_init_from_config(conn):
|
||||
settings.init_from_config(conn, cfg, force=True)
|
||||
|
||||
|
||||
def test_relay_dir_default_and_validation(tmp_path):
|
||||
conn = db.connect(tmp_path / "hub.sqlite")
|
||||
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)
|
||||
@ -63,4 +63,3 @@ def test_relay_dir_default_and_validation(tmp_path):
|
||||
with pytest.raises(ValueError):
|
||||
settings.validate("relay_dir", bad)
|
||||
assert "relay_dir" not in settings.AGENT_KEYS
|
||||
conn.close()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user