feat(ad): expose reward result by session

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
no_gen_mu
2026-07-15 11:44:10 +08:00
parent 5c6840dd71
commit ff83f90007
4 changed files with 172 additions and 0 deletions
+114
View File
@@ -356,3 +356,117 @@ def test_callback_disabled_returns_503(client, monkeypatch) -> None:
# 503 发生在验签/发奖之前,不需要真实用户
r = _callback(client, _signed(1, "trans_disabled"))
assert r.status_code == 503, r.text
# ===== 按广告会话查权威发奖结果(弹窗展示实发金币用)=====
def _reward_result(client, token: str, ad_session_id: str):
return client.get(f"/api/v1/ad/reward-result/{ad_session_id}", headers=_auth(token))
def test_reward_result_requires_auth(client) -> None:
"""按会话查发奖结果需登录。"""
assert client.get("/api/v1/ad/reward-result/sess_noauth_001").status_code == 401
def test_reward_result_pending_when_no_record(client) -> None:
"""本人没有这次会话的发奖记录 → pending、coin=null(不能把"尚未到账"当成 0 金币)。"""
phone = "13800003601"
token = _login(client, phone)
r = _reward_result(client, token, "sess_pending_never")
assert r.status_code == 200, r.text
body = r.json()
assert body["ad_session_id"] == "sess_pending_never"
assert body["status"] == "pending"
assert body["coin"] is None
def test_reward_result_granted_returns_real_coin(client) -> None:
"""本人存在 granted 记录 → 返回真实到账金币(与发奖公式一致),status=granted。"""
phone = "13800003602"
token = _login(client, phone)
uid = _user_id(phone)
session = "sess_granted_0001"
r = _callback(client, _signed(uid, "trans_rr_grant", ecpm="200",
extra=json.dumps({"ad_session_id": session})))
assert r.status_code == 200, r.text
expected = calculate_ad_reward_coin("200", 1)
assert expected > 0
assert _coin_balance(client, token) == expected
body = _reward_result(client, token, session).json()
assert body["status"] == "granted"
assert body["coin"] == expected
def test_reward_result_capped_returns_zero(client, monkeypatch) -> None:
"""达每日上限记 capped → status=capped、coin=0(客户端走上限提示,不显示"获得 0 金币")。"""
monkeypatch.setattr("app.core.rewards.get_ad_daily_limit", lambda db: 0)
phone = "13800003603"
token = _login(client, phone)
uid = _user_id(phone)
session = "sess_capped_0001"
before = _coin_balance(client, token)
r = _callback(client, _signed(uid, "trans_rr_capped", ecpm="200",
extra=json.dumps({"ad_session_id": session})))
assert r.status_code == 200, r.text
assert _coin_balance(client, token) == before # capped 未加币
body = _reward_result(client, token, session).json()
assert body["status"] == "capped"
assert body["coin"] == 0
def test_reward_result_closed_early_not_granted(client) -> None:
"""提前关闭留痕(closed_early)不是成功奖励 → 返回终态、coin=0,不能伪装成 granted。"""
phone = "13800003604"
token = _login(client, phone)
session = "sess_closed_early_0001"
r = client.post("/api/v1/ad/reward-noshow",
json={"ad_session_id": session, "watched_seconds": 2},
headers=_auth(token))
assert r.status_code == 200, r.text
assert r.json()["status"] == "closed_early"
body = _reward_result(client, token, session).json()
assert body["status"] == "closed_early"
assert body["status"] != "granted"
assert body["coin"] == 0
def test_reward_result_isolated_per_user(client) -> None:
"""他人用同一会话 id 查 → 读不到,仍 pending(会话结果按 user_id 隔离)。"""
owner_phone = "13800003605"
owner_token = _login(client, owner_phone)
owner_uid = _user_id(owner_phone)
session = "sess_shared_0001"
_callback(client, _signed(owner_uid, "trans_rr_shared", ecpm="200",
extra=json.dumps({"ad_session_id": session})))
assert _reward_result(client, owner_token, session).json()["status"] == "granted"
other_token = _login(client, "13800003606")
body = _reward_result(client, other_token, session).json()
assert body["status"] == "pending"
assert body["coin"] is None
def test_reward_result_granted_supersedes_earlier_closed_early(client) -> None:
"""同一会话先留痕 closed_early、S2S 延迟到账后又 granted → 查最新返回 granted 与真实金币
(弹窗最终展示真实到账,不被早先的 closed_early 顶掉;这正是 S2S 延迟场景)。"""
phone = "13800003607"
token = _login(client, phone)
uid = _user_id(phone)
session = "sess_late_grant_0001"
# 1) 客户端以为提前关闭,先留痕 closed_early
assert client.post("/api/v1/ad/reward-noshow",
json={"ad_session_id": session},
headers=_auth(token)).json()["status"] == "closed_early"
# 2) S2S 延迟到账,写入 granted(不同 trans_id)
r = _callback(client, _signed(uid, "trans_late_grant", ecpm="200",
extra=json.dumps({"ad_session_id": session})))
assert r.status_code == 200, r.text
expected = calculate_ad_reward_coin("200", 1)
body = _reward_result(client, token, session).json()
assert body["status"] == "granted"
assert body["coin"] == expected