Files
gaokao-volunteer-system/data/orders/tests/test_cli.py
Hermes Agent 3f46811e49
Some checks failed
CI / pytest (Python 3.10) (push) Has been cancelled
CI / pytest (Python 3.11) (push) Has been cancelled
CI / pytest (Python 3.12) (push) Has been cancelled
release: cut v2.1
2026-06-13 14:49:58 +08:00

363 lines
10 KiB
Python

"""gaokao-order-manager CLI tests (T4.3/T4.5).
覆盖 create/list/show/update/pay/deliver/stats/export 主链路,并验证默认输出走遮罩模式。
"""
from __future__ import annotations
import csv
import json
import os
import subprocess
import sys
from pathlib import Path
import pytest
from data.orders.cli import main as cli_main
os.environ.setdefault("GAOKAO_ORDERS_FERNET_KEY", "test-secret-for-cli")
PROJECT_ROOT = Path(__file__).resolve().parents[3]
SCRIPT_PATH = PROJECT_ROOT / "scripts" / "gaokao-order-manager"
@pytest.fixture
def tmp_db_path(tmp_path: Path) -> Path:
return tmp_path / "orders.db"
def _run_cli(
*args: str, env: dict[str, str] | None = None
) -> subprocess.CompletedProcess[str]:
merged_env = os.environ.copy()
if env:
merged_env.update(env)
return subprocess.run(
[sys.executable, str(SCRIPT_PATH), *args],
cwd=PROJECT_ROOT,
capture_output=True,
text=True,
env=merged_env,
)
def _load_json(stdout: str) -> dict:
return json.loads(stdout)
def test_create_list_show_update_pay_deliver_stats_flow(tmp_db_path: Path) -> None:
create = _run_cli(
"--db",
str(tmp_db_path),
"create",
"--source",
"web",
"--service-version",
"standard",
"--amount-cents",
"9900",
"--customer-name",
"张三",
"--customer-phone",
"13800001234",
"--candidate-name",
"李同学",
"--candidate-id-card",
"430102200501011234",
"--candidate-province",
"湖南",
"--candidate-score",
"578",
"--candidate-rank",
"12345",
"--candidate-subject",
"物理",
"--candidate-subject",
"化学",
"--note",
"首单",
"--tag",
"VIP",
)
assert create.returncode == 0, create.stderr
created = _load_json(create.stdout)
order_id = created["order"]["id"]
assert created["order"]["status"] == "pending"
assert created["order"]["customer_phone"] == "138****1234"
assert created["order"]["candidate_id_card"] == "430102********1234"
listed = _run_cli("--db", str(tmp_db_path), "list")
assert listed.returncode == 0, listed.stderr
listed_payload = _load_json(listed.stdout)
assert listed_payload["count"] == 1
assert listed_payload["orders"][0]["id"] == order_id
assert listed_payload["orders"][0]["customer_phone"] == "138****1234"
shown = _run_cli("--db", str(tmp_db_path), "show", order_id)
assert shown.returncode == 0, shown.stderr
shown_payload = _load_json(shown.stdout)
assert shown_payload["order"]["id"] == order_id
assert shown_payload["order"]["notes"] == "首单"
updated = _run_cli(
"--db",
str(tmp_db_path),
"update",
order_id,
"--assigned-consultant",
"consultant-a",
"--note",
"已分配顾问",
"--tag",
"已跟进",
"--tag",
"VIP",
)
assert updated.returncode == 0, updated.stderr
updated_payload = _load_json(updated.stdout)
assert updated_payload["order"]["assigned_consultant"] == "consultant-a"
assert updated_payload["order"]["notes"] == "已分配顾问"
assert updated_payload["order"]["tags"] == ["已跟进", "VIP"]
paid = _run_cli("--db", str(tmp_db_path), "pay", order_id, "--reason", "wechat-pay")
assert paid.returncode == 0, paid.stderr
paid_payload = _load_json(paid.stdout)
assert paid_payload["order"]["status"] == "paid"
assert paid_payload["order"]["paid_at"] is not None
delivered = _run_cli(
"--db",
str(tmp_db_path),
"deliver",
order_id,
"--reason",
"report-ready",
)
assert delivered.returncode == 0, delivered.stderr
delivered_payload = _load_json(delivered.stdout)
assert delivered_payload["order"]["status"] == "delivered"
assert delivered_payload["order"]["started_at"] is not None
assert delivered_payload["order"]["delivered_at"] is not None
assert [item["to_status"] for item in delivered_payload["history"]] == [
"pending",
"paid",
"serving",
"delivered",
]
stats = _run_cli("--db", str(tmp_db_path), "stats")
assert stats.returncode == 0, stats.stderr
stats_payload = _load_json(stats.stdout)
assert stats_payload["total_orders"] == 1
assert stats_payload["by_status"]["delivered"] == 1
assert stats_payload["by_source"]["web"] == 1
assert stats_payload["by_service_version"]["standard"] == 1
def test_show_missing_order_returns_nonzero_and_message(tmp_db_path: Path) -> None:
result = _run_cli("--db", str(tmp_db_path), "show", "missing-order")
assert result.returncode == 1
assert "missing-order" in result.stderr
def test_update_requires_at_least_one_mutation_field(tmp_db_path: Path) -> None:
created = _run_cli(
"--db",
str(tmp_db_path),
"create",
"--source",
"web",
"--service-version",
"basic",
"--amount-cents",
"4900",
)
order_id = _load_json(created.stdout)["order"]["id"]
result = _run_cli("--db", str(tmp_db_path), "update", order_id)
assert result.returncode == 2
assert "至少指定一个可更新字段" in result.stderr
def test_module_main_full_flow_covers_core_commands(
tmp_db_path: Path, capsys: pytest.CaptureFixture[str]
) -> None:
create_code = cli_main(
[
"--db",
str(tmp_db_path),
"create",
"--source",
"wechat",
"--service-version",
"basic",
"--amount-cents",
"4900",
"--customer-phone",
"13911112222",
"--tag",
"直连",
]
)
assert create_code == 0
created_payload = _load_json(capsys.readouterr().out)
order_id = created_payload["order"]["id"]
assert cli_main(["--db", str(tmp_db_path), "list"]) == 0
list_payload = _load_json(capsys.readouterr().out)
assert list_payload["count"] == 1
assert (
cli_main(
[
"--db",
str(tmp_db_path),
"update",
order_id,
"--assigned-consultant",
"consultant-b",
"--note",
"待支付",
]
)
== 0
)
update_payload = _load_json(capsys.readouterr().out)
assert update_payload["order"]["assigned_consultant"] == "consultant-b"
assert cli_main(["--db", str(tmp_db_path), "show", order_id]) == 0
show_payload = _load_json(capsys.readouterr().out)
assert show_payload["order"]["customer_phone"] == "139****2222"
assert cli_main(["--db", str(tmp_db_path), "pay", order_id]) == 0
pay_payload = _load_json(capsys.readouterr().out)
assert pay_payload["order"]["status"] == "paid"
assert cli_main(["--db", str(tmp_db_path), "deliver", order_id]) == 0
deliver_payload = _load_json(capsys.readouterr().out)
assert deliver_payload["order"]["status"] == "delivered"
stats_code = cli_main(["--db", str(tmp_db_path), "--human", "stats"])
captured = capsys.readouterr()
assert stats_code == 0
assert "total_orders: 1" in captured.out
assert '"wechat": 1' in captured.out
def test_upgrade_command_creates_delta_order_and_marks_source(
tmp_db_path: Path,
) -> None:
created = _run_cli(
"--db",
str(tmp_db_path),
"create",
"--source",
"web",
"--service-version",
"basic",
"--amount-cents",
"4900",
"--customer-name",
"张三",
"--customer-phone",
"13800001234",
"--candidate-name",
"李同学",
"--note",
"49 元首单",
"--tag",
"首单",
)
assert created.returncode == 0, created.stderr
order_id = _load_json(created.stdout)["order"]["id"]
upgraded = _run_cli(
"--db",
str(tmp_db_path),
"upgrade",
order_id,
"--service-version",
"standard",
"--target-amount-cents",
"9900",
"--reason",
"upgrade_to_standard",
)
assert upgraded.returncode == 0, upgraded.stderr
upgraded_payload = _load_json(upgraded.stdout)
upgrade_order = upgraded_payload["order"]
source_order = upgraded_payload["source_order"]
assert upgrade_order["upgrade_from"] == order_id
assert upgrade_order["service_version"] == "standard"
assert upgrade_order["amount_cents"] == 5000
assert upgrade_order["status"] == "pending"
assert upgrade_order["customer_phone"] == "138****1234"
assert source_order["id"] == order_id
assert "upgraded" in source_order["tags"]
assert upgrade_order["id"] in (source_order["notes"] or "")
listed = _run_cli("--db", str(tmp_db_path), "list")
listed_payload = _load_json(listed.stdout)
assert listed_payload["count"] == 2
def test_export_command_writes_minimal_csv_report(tmp_db_path: Path) -> None:
created = _run_cli(
"--db",
str(tmp_db_path),
"create",
"--source",
"school",
"--service-version",
"premium",
"--amount-cents",
"19900",
"--customer-name",
"王家长",
)
assert created.returncode == 0, created.stderr
order_id = _load_json(created.stdout)["order"]["id"]
export_path = tmp_db_path.parent / "orders-report.csv"
exported = _run_cli(
"--db",
str(tmp_db_path),
"export",
"--output",
str(export_path),
"--status",
"pending",
"--source",
"school",
)
assert exported.returncode == 0, exported.stderr
payload = _load_json(exported.stdout)
assert payload["format"] == "csv"
assert payload["rows"] == 1
assert payload["output"] == str(export_path)
assert export_path.exists()
with export_path.open("r", encoding="utf-8-sig", newline="") as fh:
rows = list(csv.DictReader(fh))
assert rows == [
{
"订单号": order_id,
"渠道": "school",
"金额": "199.00",
"状态": "pending",
"创建时间": rows[0]["创建时间"],
}
]
assert rows[0]["创建时间"]
def test_module_main_missing_show_returns_error(
tmp_db_path: Path, capsys: pytest.CaptureFixture[str]
) -> None:
result = cli_main(["--db", str(tmp_db_path), "show", "missing-order"])
captured = capsys.readouterr()
assert result == 1
assert "missing-order" in captured.err