锁定缴费校验失败时整单回滚,避免样本半成功落库;前端禁止删除锁定缴费样本,改码走更正编码。同步落地样本更正、采集微信通知、OA提及未读、细胞产品套餐与相关迁移文档。 Co-authored-by: Cursor <cursoragent@cursor.com>
274 lines
8.0 KiB
Python
274 lines
8.0 KiB
Python
"""基础实验交付日前一天服务号提醒单测。"""
|
||
from datetime import date, datetime
|
||
from unittest.mock import MagicMock, patch
|
||
|
||
from app.services.basic_experiment_delivery_remind import (
|
||
format_delivery_product_name,
|
||
run_delivery_remind_for_date,
|
||
)
|
||
|
||
|
||
def test_format_delivery_product_name_full():
|
||
assert (
|
||
format_delivery_product_name(
|
||
project_name="NK扩增",
|
||
spec_text="1e8",
|
||
package_text="冻存管",
|
||
)
|
||
== "NK扩增(1e8、冻存管)"
|
||
)
|
||
|
||
|
||
def test_format_delivery_product_name_name_and_spec_only():
|
||
assert (
|
||
format_delivery_product_name(
|
||
project_name="NK扩增",
|
||
spec_text="1e8",
|
||
package_text="",
|
||
)
|
||
== "NK扩增(1e8)"
|
||
)
|
||
|
||
|
||
def test_format_delivery_product_name_name_and_package_only():
|
||
assert (
|
||
format_delivery_product_name(
|
||
project_name="NK扩增",
|
||
spec_text="",
|
||
package_text="冻存管",
|
||
)
|
||
== "NK扩增(冻存管)"
|
||
)
|
||
|
||
|
||
def test_format_delivery_product_name_name_only():
|
||
assert (
|
||
format_delivery_product_name(
|
||
project_name="NK扩增",
|
||
spec_text="",
|
||
package_text="",
|
||
)
|
||
== "NK扩增"
|
||
)
|
||
|
||
|
||
def test_format_delivery_product_name_empty():
|
||
assert (
|
||
format_delivery_product_name(
|
||
project_name="",
|
||
spec_text="",
|
||
package_text="",
|
||
)
|
||
== ""
|
||
)
|
||
|
||
|
||
def test_send_basic_experiment_delivery_remind_builds_data():
|
||
with patch(
|
||
"app.services.wechat_mp_message._send_template"
|
||
) as send_tpl, patch("app.services.wechat_mp_message.settings") as st:
|
||
st.WX_MP_TEMPLATE_BASIC_EXPERIMENT_DELIVERY = "tpl-be"
|
||
st.WX_MP_TPL_BE_DELIVERY_FIELD_CUSTOMER = "thing6"
|
||
st.WX_MP_TPL_BE_DELIVERY_FIELD_PRODUCT = "thing10"
|
||
st.WX_MP_TPL_BE_DELIVERY_FIELD_QTY = "character_string8"
|
||
st.WX_MP_TPL_BE_DELIVERY_FIELD_CONTACT = "thing7"
|
||
st.WX_MP_TPL_BE_DELIVERY_FIELD_TIME = "time14"
|
||
send_tpl.return_value = {"success": True, "msgid": "1"}
|
||
|
||
from app.services.wechat_mp_message import send_basic_experiment_delivery_remind
|
||
|
||
long_name = "这是一个超过二十个汉字的产品名称规格包装拼接"
|
||
send_basic_experiment_delivery_remind(
|
||
mp_openid="oid",
|
||
customer_name="张三客户",
|
||
product_name=long_name,
|
||
quantity="3",
|
||
contact_name="李联系人",
|
||
time_text="2026-10-23 14:00",
|
||
)
|
||
assert send_tpl.called
|
||
kwargs = send_tpl.call_args.kwargs
|
||
assert kwargs["template_id"] == "tpl-be"
|
||
assert kwargs["mp_openid"] == "oid"
|
||
data = kwargs["data"]
|
||
assert data["thing6"]["value"] == "张三客户"
|
||
assert len(data["thing10"]["value"]) <= 20
|
||
assert data["character_string8"]["value"] == "3"
|
||
assert data["thing7"]["value"] == "李联系人"
|
||
assert data["time14"]["value"] == "2026-10-23 14:00"
|
||
|
||
|
||
def _make_db_for_remind(
|
||
*,
|
||
schedules,
|
||
already_success=False,
|
||
user_openid="mp-oid",
|
||
):
|
||
"""构造轻量 MagicMock Session,覆盖 remind 扫描路径。"""
|
||
db = MagicMock()
|
||
|
||
candidate_q = MagicMock()
|
||
candidate_q.join.return_value = candidate_q
|
||
candidate_q.outerjoin.return_value = candidate_q
|
||
candidate_q.filter.return_value = candidate_q
|
||
candidate_q.order_by.return_value = candidate_q
|
||
candidate_q.all.return_value = schedules
|
||
|
||
success_q = MagicMock()
|
||
success_q.filter.return_value = success_q
|
||
success_q.first.return_value = MagicMock() if already_success else None
|
||
|
||
user = MagicMock()
|
||
user.wx_mp_openid = user_openid
|
||
user_q = MagicMock()
|
||
user_q.filter.return_value = user_q
|
||
user_q.first.return_value = user
|
||
|
||
def query_side_effect(*models):
|
||
from app.models.models import BizWxNotifyLog, SysUser
|
||
|
||
if len(models) > 1:
|
||
return candidate_q
|
||
model = models[0]
|
||
if model is BizWxNotifyLog:
|
||
return success_q
|
||
if model is SysUser:
|
||
return user_q
|
||
return candidate_q
|
||
|
||
db.query.side_effect = query_side_effect
|
||
return db
|
||
|
||
|
||
def test_run_delivery_remind_sends_for_tomorrow_schedule():
|
||
sched = MagicMock()
|
||
sched.id = "sch-1"
|
||
sched.tenant_no = "zkxw"
|
||
sched.infusion_time = datetime(2026, 10, 23, 14, 0, 0)
|
||
sched.spec_text = "1e8"
|
||
sched.package_text = "冻存管"
|
||
sched.schedule_status = "dsc"
|
||
|
||
app = MagicMock()
|
||
app.id = "app-1"
|
||
app.tenant_no = "zkxw"
|
||
app.create_by = "user-1"
|
||
app.customer_name = "客户甲"
|
||
app.customer_contact = "联系人乙"
|
||
|
||
prod = MagicMock()
|
||
prod.project_name = "NK扩增"
|
||
prod.spec_text = "1e8"
|
||
prod.package_text = "冻存管"
|
||
prod.quantity = 2
|
||
|
||
db = _make_db_for_remind(schedules=[(sched, app, prod)])
|
||
|
||
with patch(
|
||
"app.services.basic_experiment_delivery_remind._notify_enabled",
|
||
return_value=True,
|
||
), patch(
|
||
"app.services.wechat_mp_message.send_basic_experiment_delivery_remind"
|
||
) as send_fn:
|
||
send_fn.return_value = {"success": True, "msgid": "m1"}
|
||
stats = run_delivery_remind_for_date(db, today=date(2026, 10, 22))
|
||
|
||
assert stats["candidates"] == 1
|
||
assert stats["sent"] == 1
|
||
assert send_fn.called
|
||
kwargs = send_fn.call_args.kwargs
|
||
assert kwargs["customer_name"] == "客户甲"
|
||
assert kwargs["product_name"] == "NK扩增(1e8、冻存管)"
|
||
assert kwargs["quantity"] == "2"
|
||
assert kwargs["contact_name"] == "联系人乙"
|
||
assert kwargs["time_text"] == "2026-10-23 14:00"
|
||
assert db.add.called
|
||
assert db.commit.called
|
||
|
||
|
||
def test_run_delivery_remind_skips_when_already_success():
|
||
sched = MagicMock()
|
||
sched.id = "sch-1"
|
||
sched.tenant_no = "zkxw"
|
||
sched.infusion_time = datetime(2026, 10, 23, 14, 0, 0)
|
||
sched.spec_text = ""
|
||
sched.package_text = ""
|
||
|
||
app = MagicMock()
|
||
app.id = "app-1"
|
||
app.tenant_no = "zkxw"
|
||
app.create_by = "user-1"
|
||
app.customer_name = "客户甲"
|
||
app.customer_contact = "联系人乙"
|
||
|
||
prod = MagicMock()
|
||
prod.project_name = "NK"
|
||
prod.spec_text = ""
|
||
prod.package_text = ""
|
||
prod.quantity = 1
|
||
|
||
db = _make_db_for_remind(
|
||
schedules=[(sched, app, prod)], already_success=True
|
||
)
|
||
|
||
with patch(
|
||
"app.services.basic_experiment_delivery_remind._notify_enabled",
|
||
return_value=True,
|
||
), patch(
|
||
"app.services.wechat_mp_message.send_basic_experiment_delivery_remind"
|
||
) as send_fn:
|
||
send_fn.return_value = {"success": True}
|
||
stats = run_delivery_remind_for_date(db, today=date(2026, 10, 22))
|
||
|
||
assert stats["skipped"] == 1
|
||
assert stats["sent"] == 0
|
||
assert not send_fn.called
|
||
|
||
|
||
def test_run_delivery_remind_skips_without_openid():
|
||
sched = MagicMock()
|
||
sched.id = "sch-1"
|
||
sched.tenant_no = "zkxw"
|
||
sched.infusion_time = datetime(2026, 10, 23, 9, 0, 0)
|
||
sched.spec_text = ""
|
||
sched.package_text = ""
|
||
|
||
app = MagicMock()
|
||
app.id = "app-1"
|
||
app.tenant_no = "zkxw"
|
||
app.create_by = "user-1"
|
||
app.customer_name = "客户甲"
|
||
app.customer_contact = "联系人乙"
|
||
|
||
prod = MagicMock()
|
||
prod.project_name = "NK"
|
||
prod.spec_text = ""
|
||
prod.package_text = ""
|
||
prod.quantity = 1
|
||
|
||
db = _make_db_for_remind(
|
||
schedules=[(sched, app, prod)], user_openid=""
|
||
)
|
||
|
||
with patch(
|
||
"app.services.basic_experiment_delivery_remind._notify_enabled",
|
||
return_value=True,
|
||
), patch(
|
||
"app.services.wechat_mp_message.send_basic_experiment_delivery_remind"
|
||
) as send_fn:
|
||
stats = run_delivery_remind_for_date(db, today=date(2026, 10, 22))
|
||
|
||
assert stats["skipped"] == 1
|
||
assert not send_fn.called
|
||
|
||
|
||
def test_run_delivery_remind_noop_when_disabled():
|
||
db = MagicMock()
|
||
with patch(
|
||
"app.services.basic_experiment_delivery_remind._notify_enabled",
|
||
return_value=False,
|
||
):
|
||
stats = run_delivery_remind_for_date(db, today=date(2026, 10, 22))
|
||
assert stats == {"candidates": 0, "sent": 0, "skipped": 0, "failed": 0}
|
||
assert not db.query.called
|