From 93bf991fe1b44e343bfca53de6ff4cb986a29c3e Mon Sep 17 00:00:00 2001 From: marco Date: Fri, 31 Jul 2026 23:08:43 +0800 Subject: [PATCH] =?UTF-8?q?feat(coupon):=20/coupon/step=20=E6=AF=8F?= =?UTF-8?q?=E5=B8=A7=E5=93=8D=E5=BA=94=E9=A1=B6=E5=B1=82=E5=9B=9E=E6=98=BE?= =?UTF-8?q?=20trace=5Fid=EF=BC=8C=E8=A1=A5=E9=BD=90=E9=A2=86=E5=88=B8?= =?UTF-8?q?=E9=93=BE=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对齐 compare 链路(_forward 的 setdefault 注入,每步响应都带 trace_id):coupon_step 透传响应在 resp.json() 后 setdefault 注入本次任务 trace_id,客户端任一帧都能从响应 拿到全链 id。**只回显请求里带的、不 mint**——step 是循环接口,每帧签新 id 会把一次 任务打散;领券 trace_id 的唯一签发点保持在 /coupon/session (status=started)。带 isinstance dict + 非空 trace_id 双防御(pricebot 响应顶层本无 trace_id 字段,setdefault 不会覆盖任何上游值)。 顺手修复 test_coupon_proxy.py 一个 pre-existing 红测试:coupon_step 转发早已改为 content=raw 原始字节透传,但 fake_post 仍只捕获 json= 参数 → captured["json"] 恒 None、body 转发断言一直失败(git stash 验证不带本轮改动同样红)。fake 改捕 content、 断言按字节 json.loads 后对比;transparently-passes-through 断言更新为"透传 + 顶层 回显 trace_id"新契约。 领券相关测试 22 passed。 Co-Authored-By: Claude Fable 5 --- app/api/v1/coupon.py | 6 ++++++ tests/test_coupon_proxy.py | 18 ++++++++++++------ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/api/v1/coupon.py b/app/api/v1/coupon.py index 307423e..a3179d8 100644 --- a/app/api/v1/coupon.py +++ b/app/api/v1/coupon.py @@ -177,6 +177,12 @@ async def coupon_step( ) resp_json = resp.json() + # 每帧响应顶层回传本次任务 trace_id(对齐 compare _forward 的 setdefault):客户端任一帧 + # 都能从响应拿到全链 id。**只回显请求里带的、不 mint**——step 是循环接口,每帧签新 id + # 会把一次任务打散;领券 trace_id 的唯一签发点在 /coupon/session (status=started)。 + # pricebot 响应顶层本无 trace_id(只有 trace_url),setdefault 不会覆盖任何上游值。 + if isinstance(resp_json, dict) and trace_id: + resp_json.setdefault("trace_id", trace_id) # 领券结果沉淀:每日资产 + 逐次事件;中间帧和 done 全量帧均幂等写库。 # 当前只记录、不参与"要不要领"判断(MVP 先不去重)。写库失败不影响返回。 diff --git a/tests/test_coupon_proxy.py b/tests/test_coupon_proxy.py index c993181..5543cd3 100644 --- a/tests/test_coupon_proxy.py +++ b/tests/test_coupon_proxy.py @@ -8,6 +8,7 @@ mock 掉对 pricebot 的 httpx 调用,验证: """ from __future__ import annotations +import json import time from unittest.mock import MagicMock, patch @@ -62,7 +63,8 @@ def test_coupon_step_no_auth_required(client) -> None: def test_coupon_step_passes_body_through(client, access_token) -> None: - """带 token + pricebot 200 → 响应原样透传,请求 body 原样转发到 /api/coupon/step。""" + """带 token + pricebot 200 → 请求 body 原样转发到 /api/coupon/step; + 响应在透传基础上顶层回显本次任务 trace_id(setdefault 注入,其余字段原样)。""" fake_pricebot_resp = { "success": True, "action": { @@ -83,9 +85,12 @@ def test_coupon_step_passes_body_through(client, access_token) -> None: captured: dict = {} - async def fake_post(self, url, json=None, **kw): + # ⚠️ coupon_step 转发用 content=raw(原始字节透传,不重新 dumps),不是 json= —— + # fake 必须捕 content。旧 fake 只捕 json= 导致 captured["json"] 恒 None,本测试 + # 自 content=raw 优化后一直红着(pre-existing),本次顺手修正。 + async def fake_post(self, url, content=None, **kw): captured["url"] = url - captured["json"] = json + captured["content"] = content mock_resp = MagicMock() mock_resp.status_code = 200 mock_resp.json = lambda: fake_pricebot_resp @@ -99,9 +104,10 @@ def test_coupon_step_passes_body_through(client, access_token) -> None: ) assert r.status_code == 200, r.text - assert r.json() == fake_pricebot_resp - # 验证请求被原样转发(body 不动 + URL 指向 pricebot) - assert captured["json"] == _stub_request_body() + # 顶层多出 trace_id 回显(值=请求带的;不 mint,签发点唯一在 /coupon/session started) + assert r.json() == {**fake_pricebot_resp, "trace_id": "test-trace-1"} + # 验证请求被原样转发(body 字节不动 + URL 指向 pricebot) + assert json.loads(captured["content"]) == _stub_request_body() assert captured["url"].endswith("/api/coupon/step")