feat(core-B): 운영자 sync/verify CLI + cold-start onboarding (BAT-051, REQ-105/106, PR #32)
dcy-034 마지막 조각. sync(슬롯 qty←브로커+warning해제+OQ-A 전이엣지+FR-7 평단seed)/verify(C 재대조·강제해제없음). core-A QC minor 2건 정리 동봉. QC(opus) PASS, 683 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VHtM5Ws1HKCKQ4JGi6GRb5
This commit is contained in:
@@ -328,3 +328,116 @@ def test_cancel_json_outputs_json(capsys):
|
||||
assert rc == 0
|
||||
out = json.loads(capsys.readouterr().out)
|
||||
assert out["status"] == "ok"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# sync parser tests (REQ-105 / BAT-051) — parser-level only, no DB/broker
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_sync_parser_portfolio_id_accepted():
|
||||
"""sync --portfolio-id <pid> is parseable."""
|
||||
parser = cli._build_parser()
|
||||
args = parser.parse_args(["sync", "--portfolio-id", "PF-1", "--yes"])
|
||||
assert args.cmd == "sync"
|
||||
assert args.portfolio_id == "PF-1"
|
||||
assert args.yes is True
|
||||
|
||||
|
||||
def test_sync_parser_all_accepted():
|
||||
"""sync --all is parseable."""
|
||||
parser = cli._build_parser()
|
||||
args = parser.parse_args(["sync", "--all", "--yes"])
|
||||
assert args.cmd == "sync"
|
||||
assert args.all is True
|
||||
|
||||
|
||||
def test_sync_parser_dry_run_accepted():
|
||||
"""sync --dry-run is parseable."""
|
||||
parser = cli._build_parser()
|
||||
args = parser.parse_args(["sync", "--portfolio-id", "PF-1", "--dry-run"])
|
||||
assert args.dry_run is True
|
||||
|
||||
|
||||
def test_sync_parser_live_accepted():
|
||||
"""sync --yes --live is parseable."""
|
||||
parser = cli._build_parser()
|
||||
args = parser.parse_args(["sync", "--portfolio-id", "PF-1", "--yes", "--live"])
|
||||
assert args.live is True
|
||||
|
||||
|
||||
def test_sync_no_target_fails(capsys, monkeypatch):
|
||||
"""sync without --portfolio-id or --all returns rc=1."""
|
||||
def _fake_sync(args, cfg, confirm):
|
||||
from trex_core.runtime.cli import _fail
|
||||
_fail("--portfolio-id <pid> or --all is required", args.json)
|
||||
return 1
|
||||
monkeypatch.setattr(cli, "_cmd_sync", _fake_sync)
|
||||
rc = cli.main(["sync"])
|
||||
assert rc == 1
|
||||
|
||||
|
||||
def test_sync_requires_yes_when_not_dry_run(capsys, monkeypatch):
|
||||
"""sync without --yes (and not dry-run) should abort."""
|
||||
def _fake_sync(args, cfg, confirm):
|
||||
# Simulate the confirmation prompt returning False (no --yes, interactive aborts)
|
||||
if not getattr(args, "dry_run", False) and not getattr(args, "yes", False):
|
||||
if not confirm("Sync?"):
|
||||
print("aborted")
|
||||
return 1
|
||||
return 0
|
||||
monkeypatch.setattr(cli, "_cmd_sync", _fake_sync)
|
||||
rc = cli.main(["sync", "--portfolio-id", "PF-1"], confirm=lambda _: False)
|
||||
assert rc == 1
|
||||
|
||||
|
||||
def test_sync_live_requires_live_flag(capsys, monkeypatch):
|
||||
"""sync on live account without --live returns rc=2."""
|
||||
monkeypatch.setattr(cli, "_broker_mode", lambda cfg: ("kis", True))
|
||||
|
||||
def _fake_sync(args, cfg, confirm):
|
||||
broker, is_live = cli._broker_mode(cfg)
|
||||
if is_live and not getattr(args, "live", False):
|
||||
cli._fail("live account requires --live (with --yes) to confirm a real sync", args.json)
|
||||
return 2
|
||||
return 0
|
||||
monkeypatch.setattr(cli, "_cmd_sync", _fake_sync)
|
||||
rc = cli.main(["sync", "--portfolio-id", "PF-1", "--yes"])
|
||||
assert rc == 2
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# verify parser tests (REQ-106 / BAT-051) — parser-level only
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_verify_parser_portfolio_id_accepted():
|
||||
"""verify --portfolio-id <pid> is parseable."""
|
||||
parser = cli._build_parser()
|
||||
args = parser.parse_args(["verify", "--portfolio-id", "PF-1"])
|
||||
assert args.cmd == "verify"
|
||||
assert args.portfolio_id == "PF-1"
|
||||
|
||||
|
||||
def test_verify_parser_no_portfolio_id_accepted():
|
||||
"""verify (no --portfolio-id) is parseable (all C-warned slots)."""
|
||||
parser = cli._build_parser()
|
||||
args = parser.parse_args(["verify"])
|
||||
assert args.cmd == "verify"
|
||||
assert args.portfolio_id is None
|
||||
|
||||
|
||||
def test_verify_parser_json_accepted():
|
||||
"""verify --json is parseable."""
|
||||
parser = cli._build_parser()
|
||||
args = parser.parse_args(["verify", "--json"])
|
||||
assert args.json is True
|
||||
|
||||
|
||||
def test_verify_no_db_no_live_flag_needed(capsys, monkeypatch):
|
||||
"""verify does not require --yes or --live (read-centric, REQ-106)."""
|
||||
def _fake_verify(args, cfg):
|
||||
# Simulate: read-centric, no live gate needed
|
||||
print("no C-warned slots found")
|
||||
return 0
|
||||
monkeypatch.setattr(cli, "_cmd_verify", _fake_verify)
|
||||
rc = cli.main(["verify"])
|
||||
assert rc == 0
|
||||
|
||||
Reference in New Issue
Block a user