3d6064dc14
- test_lead_push_cc: 两处"双钉"注释与模块头改为如实的跨文件互补分工 (常量同源钉本文件,字节级措辞钉 test_lead_push_service.py / test_email_rendering.py) - chat_rest: _server_ip/_merge_client_info docstring 与调用点注释恢复 上游半角标点原文(逐字同步,便于将来 diff 式跟上游) - chat.py WS 自报注释补 REST 侧交叉引用,闭合悬空不对称说明 全量回归 459 passed(不变) Co-Authored-By: Claude Code <noreply@anthropic.com>
145 lines
6.9 KiB
Python
145 lines
6.9 KiB
Python
"""推送侧 CC 补全渲染(同步自上游 ai-kefu tests/test_lead_push_cc.py)。
|
|
|
|
惯例照搬 tests/test_lead_push_service.py: async_db fixture +
|
|
monkeypatch settings.PUSH_ENABLED → dispatch_lead_push → 查 request_payload。
|
|
|
|
断言按 jc(Task 6)主线重锚,语义等价:
|
|
- 派生键名: 上游 "区号未确认" → jc "号码补全说明"(号码补全的推送披露口);
|
|
- 条目措辞: 上游 "渠道(标注)" → jc completion_note 的 "原文→e164+标注"
|
|
(标注常量与 cc_completion 同源;字节级措辞钉在 test_lead_push_service.py /
|
|
test_email_rendering.py,跨文件互补);
|
|
- NOTE_CUSTOMER 已在 jc 并入 NOTE_UNCONFIRMED(customer_stated 未确认与
|
|
IP 推测同口径标注,上游的措辞区分被主线统一);
|
|
- confirmed 入参传裸号原文(jc 生产契约: dispatch 收 normalize_contacts
|
|
的裸号, e164 由 _cc 派生),上游传 e164——canon 口径下两者等价。
|
|
"""
|
|
import asyncio
|
|
|
|
from sqlalchemy import select
|
|
|
|
from app.engine.models import (
|
|
Conversation, CustomerProfile, Evaluation, IntentLevel,
|
|
)
|
|
from app.engine.services.cc_completion import NOTE_MISSING_CC, NOTE_UNCONFIRMED
|
|
|
|
|
|
def _cc(confirmed=False, source="ip_proposed", e164="+254712345678"):
|
|
return {"e164": e164, "intl": "+254 712 345678", "confirmed": confirmed,
|
|
"attempts": 0, "source": source, "canon": "0712345678"}
|
|
|
|
|
|
def _seed(async_db, raw_info):
|
|
from app.engine.models import ConversationSource
|
|
|
|
async def run():
|
|
async with async_db() as db:
|
|
conv = Conversation(
|
|
session_id="s-push-cc", source=ConversationSource.widget,
|
|
client_info={"ip": "8.8.8.8", "lang": "en"},
|
|
)
|
|
db.add(conv)
|
|
await db.flush()
|
|
profile = CustomerProfile(conversation_id=conv.id, raw_info=raw_info)
|
|
ev = Evaluation(
|
|
conversation_id=conv.id,
|
|
intent_level=IntentLevel.interested, contact_info={})
|
|
db.add_all([profile, ev])
|
|
await db.commit()
|
|
asyncio.run(run())
|
|
|
|
|
|
def _dispatch(async_db, confirmed):
|
|
from app.engine.services import lead_push_service as svc
|
|
|
|
async def run():
|
|
async with async_db() as db:
|
|
conv = (await db.execute(select(Conversation))).scalar_one()
|
|
profile = (await db.execute(select(CustomerProfile))).scalar_one()
|
|
ev = (await db.execute(select(Evaluation))).scalar_one()
|
|
log = await svc.dispatch_lead_push(
|
|
db, ev, conv, profile, confirmed, "lead",
|
|
{"intent_level": "interested"}, True)
|
|
await db.commit()
|
|
return log
|
|
return asyncio.run(run())
|
|
|
|
|
|
def test_push_payload_uses_e164_and_filters_underscore(async_db, monkeypatch):
|
|
"""消费 _cc → 过滤 `_`键(顺序钉死 spec §3.5);未确认 IP 推测 → 标注字段。"""
|
|
from app.engine.services import lead_push_service as svc
|
|
monkeypatch.setattr(svc.settings, "PUSH_ENABLED", True)
|
|
_seed(async_db, {"目标国家": "肯尼亚", "_cc": {"whatsapp": _cc()},
|
|
"_secret": "x"})
|
|
log = _dispatch(async_db, {"whatsapp": "0712345678"})
|
|
p = log.request_payload
|
|
assert p["contact"]["whatsapp"] == "+254712345678" # 契约值=e164(Task6投影)
|
|
assert not [k for k in p["profile"] if str(k).startswith("_")] # _cc/_secret不外泄
|
|
# 常量同源钉在本文件,字节级措辞钉在 test_lead_push_service.py /
|
|
# test_email_rendering.py(跨文件互补)
|
|
assert p["profile"]["号码补全说明"] == f"0712345678→+254712345678{NOTE_UNCONFIRMED}"
|
|
|
|
|
|
def test_push_payload_confirmed_no_note(async_db, monkeypatch):
|
|
"""confirmed=true → 无标注字段;`_` 内部态同样不得外泄。"""
|
|
from app.engine.services import lead_push_service as svc
|
|
monkeypatch.setattr(svc.settings, "PUSH_ENABLED", True)
|
|
_seed(async_db, {"目标国家": "肯尼亚",
|
|
"_cc": {"whatsapp": _cc(confirmed=True)}})
|
|
log = _dispatch(async_db, {"whatsapp": "0712345678"})
|
|
p = log.request_payload
|
|
assert "号码补全说明" not in p["profile"]
|
|
assert not [k for k in p["profile"] if str(k).startswith("_")]
|
|
|
|
|
|
def test_push_payload_no_ip_note(async_db, monkeypatch):
|
|
"""no_ip(e164=None,值留原裸号) → "缺国家区号"标注;contact 值=原号。
|
|
|
|
phone 态显式构造(照上游混合渠道用例的形态): jc 的 cc_state_for 按
|
|
canon 匹配(canon≠裸号数字一律判 None,旧补全永不跟随新号),故 canon
|
|
必须与渠道值一致——生产 _cc.canon 恒为客户裸号纯数字。"""
|
|
from app.engine.services import lead_push_service as svc
|
|
monkeypatch.setattr(svc.settings, "PUSH_ENABLED", True)
|
|
_seed(async_db, {"目标国家": "肯尼亚",
|
|
"_cc": {"phone": {"e164": None, "intl": None,
|
|
"confirmed": False, "attempts": 0,
|
|
"source": "no_ip",
|
|
"canon": "18325698745"}}})
|
|
log = _dispatch(async_db, {"phone": "18325698745"})
|
|
p = log.request_payload
|
|
assert p["profile"]["号码补全说明"] == f"18325698745{NOTE_MISSING_CC}"
|
|
assert p["contact"]["phone"] == "18325698745"
|
|
|
|
|
|
def test_push_payload_customer_stated_note(async_db, monkeypatch):
|
|
"""customer_stated 未确认 → 标注在位(jc 主线: 与 IP 推测同口径
|
|
NOTE_UNCONFIRMED,上游 NOTE_CUSTOMER 的独立措辞已统一)。"""
|
|
from app.engine.services import lead_push_service as svc
|
|
monkeypatch.setattr(svc.settings, "PUSH_ENABLED", True)
|
|
_seed(async_db, {"目标国家": "肯尼亚",
|
|
"_cc": {"whatsapp": _cc(source="customer_stated",
|
|
e164="+256712345678")}})
|
|
log = _dispatch(async_db, {"whatsapp": "0712345678"})
|
|
p = log.request_payload
|
|
# 常量同源钉在本文件,字节级措辞钉在 test_lead_push_service.py /
|
|
# test_email_rendering.py(跨文件互补)
|
|
assert p["profile"]["号码补全说明"] == f"0712345678→+256712345678{NOTE_UNCONFIRMED}"
|
|
assert p["contact"]["whatsapp"] == "+256712345678"
|
|
|
|
|
|
def test_push_payload_mixed_channels_join(async_db, monkeypatch):
|
|
"""混合渠道: whatsapp 已确认(不标注) + phone no_ip 未确认(标注)——
|
|
只记未确认渠道,join 格式钉死。"""
|
|
from app.engine.services import lead_push_service as svc
|
|
monkeypatch.setattr(svc.settings, "PUSH_ENABLED", True)
|
|
_seed(async_db, {"目标国家": "肯尼亚", "_cc": {
|
|
"whatsapp": _cc(confirmed=True, e164="+254712345678"),
|
|
"phone": {"e164": None, "intl": None, "confirmed": False,
|
|
"attempts": 0, "source": "no_ip",
|
|
"canon": "18325698745"}}})
|
|
log = _dispatch(async_db, {"whatsapp": "0712345678",
|
|
"phone": "18325698745"})
|
|
p = log.request_payload
|
|
assert p["contact"]["whatsapp"] == "+254712345678"
|
|
assert p["contact"]["phone"] == "18325698745"
|
|
assert p["profile"]["号码补全说明"] == f"18325698745{NOTE_MISSING_CC}"
|