refactor(api): yun8_sync 并入 tests 分层框架(接口层 apis/yun8 + 用例层 api + Allure)
- tests/core: 新增 BaseHttpClient 统一 HTTP 封装(直连绕行/超时/日志/自定义编码); db_helper 追加 Db 长连接类(事务/mogrify/自动重连),既有函数不动 - tests/apis/yun8: 21 个 step 方法按域拆分为 5 个 Mixin(driver/accounting/ audit_payment/receipt/third_party),Yun8Pipeline 24 步编排原样迁移, SQL 全部参数化并集中至 sql_templates - tests/api: 5 阶段有序 P0 E2E 用例(业务失败由 print 改为真实报错)+ README 迁移(删除明文密码段,修正步骤编号漂移) - yun8_sync 目录退役:配置并入 test_config.yaml 的 yun8_api 段; 造数 CLI 保留(python -m tests.apis.yun8.cli,兼容旧 config.yaml 结构) - pytest.ini 注册 api/yun8_sync marker;requirements.txt 补 requests>=2.28 - 静态验证: compileall / 全仓 collect 24 条 / marker 过滤 / CLI --help / 客户端与 pipeline 离线实例化 / SQL 占位符核对 / cross_end 兼容
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
"""yun8 运输流程 API 造数 — 24 步全链路 E2E。
|
||||
|
||||
⚠️ 此套件会真实调用测试环境接口并写入数据库(造运单/打款/回单/ETC 发票),
|
||||
不属于默认冒烟扫描范围,请显式按 marker 运行:
|
||||
pytest tests/api -m yun8_sync -v
|
||||
|
||||
5 个阶段用例按定义顺序执行(同一类内定义序即执行序,勿并行),
|
||||
共享一套 Yun8Pipeline(Postman Collection Variables 上下文):
|
||||
阶段 A driver → 阶段 B accounting → 阶段 C audit_payment
|
||||
→ 阶段 D receipt → 阶段 E etc
|
||||
前置阶段失败时,后续阶段自动 skip(与 CLI 一次循环失败即中止语义一致)。
|
||||
|
||||
Usage:
|
||||
pytest tests/api/test_yun8_transport_flow.py -v
|
||||
"""
|
||||
|
||||
import allure
|
||||
import pytest
|
||||
|
||||
pytestmark = [pytest.mark.api, pytest.mark.yun8_sync, pytest.mark.p0]
|
||||
|
||||
|
||||
@allure.epic("运八网络货运平台")
|
||||
@allure.feature("yun8 运输流程 API 造数 — 24 步全链路")
|
||||
class TestYun8TransportFlow:
|
||||
"""运输流程全链路:接口驱动 + SQL 造数(详见 tests/api/README.md)。"""
|
||||
|
||||
# ── 中断守卫:前置阶段失败则跳过 ──────────────────────────────
|
||||
def _guard(self, yun8_context: dict):
|
||||
if yun8_context["aborted"]:
|
||||
pytest.skip(f"前置阶段失败: {yun8_context['aborted']}")
|
||||
|
||||
def _run(self, yun8_context: dict, stage: str, fn, *args, **kwargs):
|
||||
"""执行编排方法,失败时记录中断原因后原样抛出。"""
|
||||
try:
|
||||
return fn(*args, **kwargs)
|
||||
except Exception as exc:
|
||||
yun8_context["aborted"] = f"{stage}: {exc}"
|
||||
raise
|
||||
|
||||
@staticmethod
|
||||
def _execute_after_audit(yun8_pipeline) -> bool:
|
||||
return yun8_pipeline.config.get("accounting", {}).get(
|
||||
"execute_after_audit", True)
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# 阶段 A-E
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
@allure.story("阶段A — 司机端 (Step 01-05)")
|
||||
@allure.title("TC-Y8-001 司机端 查询货源→接单→装货→卸货")
|
||||
def test_stage_a_driver_flow(self, yun8_pipeline, yun8_context):
|
||||
"""查询货源→接单→查询待运输单→装货→卸货,产出 code/orderCode。"""
|
||||
self._guard(yun8_context)
|
||||
with allure.step("执行司机端流程 run_driver_flow (Step 01-05)"):
|
||||
vars_ = self._run(yun8_context, "阶段A",
|
||||
yun8_pipeline.run_driver_flow)
|
||||
assert vars_.get("mainOrderCode"), "mainOrderCode 为空"
|
||||
assert vars_.get("code"), "待运输单 code 为空"
|
||||
assert vars_.get("orderCode"), "orderCode 为空"
|
||||
|
||||
@allure.story("阶段B — 运费核算 (Step 06-09)")
|
||||
@allure.title("TC-Y8-002 核算查询→详情→试算→提交")
|
||||
def test_stage_b_accounting_flow(self, yun8_pipeline, yun8_context):
|
||||
"""查询运单→运单详情→核算试算→核算提交,产出 30+ 核算字段。"""
|
||||
self._guard(yun8_context)
|
||||
with allure.step("执行运费核算流程 run_accounting_flow (Step 06-09)"):
|
||||
vars_ = self._run(yun8_context, "阶段B",
|
||||
yun8_pipeline.run_accounting_flow)
|
||||
assert vars_.get("deliveryCashFee") != "", "deliveryCashFee 为空"
|
||||
assert vars_.get("shipmentActualCash") != "", "shipmentActualCash 为空"
|
||||
|
||||
@allure.story("阶段C — 审核 & 付款 (Step 10-16)")
|
||||
@allure.title("TC-Y8-003 审核通过→油卡充值→司机打款→货主打款")
|
||||
def test_stage_c_audit_payment_flow(self, yun8_pipeline, yun8_context):
|
||||
"""查询审核→审核通过→(分账)→油卡充值→财务打款→货主打款。
|
||||
|
||||
成功判定 = 编排全程无 Yun8StepError / HTTP 异常。
|
||||
execute_after_audit=false 时编排内部自行跳过 12-16 步。
|
||||
"""
|
||||
self._guard(yun8_context)
|
||||
with allure.step("执行审核付款流程 run_audit_payment_flow (Step 10-16)"):
|
||||
self._run(yun8_context, "阶段C",
|
||||
yun8_pipeline.run_audit_payment_flow)
|
||||
|
||||
@allure.story("阶段D — 回单文件 + SQL (Step 17-21)")
|
||||
@allure.title("TC-Y8-004 提取payNo→回单INSERT→上报运单")
|
||||
def test_stage_d_receipt_flow(self, yun8_pipeline, yun8_context):
|
||||
"""payNo 提取→打款记录分页→文件名前缀→回单 INSERT→上报运单。"""
|
||||
self._guard(yun8_context)
|
||||
if not self._execute_after_audit(yun8_pipeline):
|
||||
pytest.skip("execute_after_audit=false,receipt 阶段被配置跳过")
|
||||
with allure.step("执行回单文件流程 run_receipt_file_flow (Step 17-21)"):
|
||||
results = self._run(yun8_context, "阶段D",
|
||||
yun8_pipeline.run_receipt_file_flow,
|
||||
dry_run=False)
|
||||
assert yun8_pipeline.vars.get("payNo"), "payNo 为空"
|
||||
assert results, "无任何打款记录可处理(remit_records 为空)"
|
||||
inserted = [r for r in results if r.get("status") == "inserted"]
|
||||
assert inserted, f"没有成功插入的回单记录: {results}"
|
||||
|
||||
@allure.story("阶段E — ETC 开票 SQL (Step 22-24)")
|
||||
@allure.title("TC-Y8-005 ETC发票INSERT→推送记录INSERT→开票状态更新")
|
||||
def test_stage_e_etc_invoice_flow(self, yun8_pipeline, yun8_context):
|
||||
"""写入 base_etc_invoice × N → tms_order_etc_push → 更新开票状态。"""
|
||||
self._guard(yun8_context)
|
||||
if not self._execute_after_audit(yun8_pipeline):
|
||||
pytest.skip("execute_after_audit=false,ETC 阶段被配置跳过")
|
||||
with allure.step("执行 ETC 开票流程 run_etc_invoice_flow (Step 22-24)"):
|
||||
results = self._run(yun8_context, "阶段E",
|
||||
yun8_pipeline.run_etc_invoice_flow,
|
||||
dry_run=False)
|
||||
errors = [r for r in results if r.get("status") == "error"]
|
||||
assert not errors, f"ETC SQL 写入存在失败项: {errors}"
|
||||
Reference in New Issue
Block a user