Files

118 lines
4.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Dramatiq 调度任务测试。
方式:
- TASK_BROKER=stub:进程内 StubBroker,验证 actor 注册与消息入队;
- 直接调用 actor(dramatiq actor 可调用,同步执行函数体)验证业务结果,
避免测试进程内 Worker 线程与 SQLite 事务锁冲突。
"""
import pytest
from datetime import date, timedelta
from decimal import Decimal
from model_bakery import baker
import dramatiq
from dramatiq.brokers.stub import StubBroker
from dramatiq.middleware import AgeLimit, Callbacks, Retries, TimeLimit
# 显式安装 StubBroker(覆盖 core.ready 可能已装的 RedisBroker)
broker = StubBroker(middleware=[AgeLimit(), TimeLimit(), Callbacks(), Retries()])
dramatiq.set_broker(broker)
# 在 stub broker 就绪后导入 actors(完成注册)
from apps.notify.tasks import ( # noqa: E402
run_alert_checks_for_tenant,
run_all_alert_checks_for_all_tenants,
)
from apps.channel.tasks import ( # noqa: E402
sync_orders_for_account,
sync_orders_for_all_active_accounts,
)
from apps.catalog.models import Product # noqa: E402
from apps.inventory.models import Warehouse, Stock # noqa: E402
from apps.partner.models import Customer # noqa: E402
from apps.finance.models import Receivable # noqa: E402
from apps.notify.models import Notification # noqa: E402
from apps.channel.models import ChannelAccount # noqa: E402
from apps.sales.models import SalesOrder # noqa: E402
@pytest.fixture(autouse=True)
def _flush_broker():
broker.flush_all()
yield
broker.flush_all()
@pytest.mark.django_db
def test_actors_registered():
names = {a.actor_name for a in broker.actors.values()}
assert "run_alert_checks_for_tenant" in names
assert "run_all_alert_checks_for_all_tenants" in names
assert "sync_orders_for_account" in names
assert "sync_orders_for_all_active_accounts" in names
@pytest.mark.django_db
def test_alert_actor_enqueues_message(tenant):
run_alert_checks_for_tenant.send(tenant.id)
assert broker.queues["default"].qsize() == 1
import json
raw = broker.queues["default"].queue[0]
msg = json.loads(raw)
assert msg["actor_name"] == "run_alert_checks_for_tenant"
assert msg["args"] == [tenant.id]
@pytest.mark.django_db
def test_alert_actor_inline_execution_triggers_overdue_alert(tenant):
from apps.notify.models import AlertRule
baker.make(AlertRule, tenant=tenant, code="receivable_overdue_30d",
name="逾期催收", rule_type="receivable_overdue", is_enabled=True)
cust = baker.make(Customer, tenant=tenant, code="C_TQ", name="逾期客户")
baker.make(
Receivable,
tenant=tenant, customer=cust, bill_no="RC-TQ-01",
bill_date=date.today() - timedelta(days=60),
due_date=date.today() - timedelta(days=10),
total_amount=Decimal("500"), paid_amount=Decimal("0"), status="open",
)
result = run_alert_checks_for_tenant(tenant.id) # 直接调用 = 同步执行
assert result["receivable_alerts_count"] == 1
assert Notification.objects.filter(tenant=tenant, category="warning").count() == 1
@pytest.mark.django_db
def test_all_tenants_alert_actor_inline(tenant, other_tenant):
for t in (tenant, other_tenant):
wh = baker.make(Warehouse, tenant=t, code=f"W_{t.code}")
p = baker.make(Product, tenant=t, code=f"P_{t.code}", name=f"缺货商品{t.code}")
baker.make(Stock, tenant=t, warehouse=wh, product=p, on_hand=Decimal("2"), locked=Decimal("0"))
summary = run_all_alert_checks_for_all_tenants()
by_code = {row["tenant"]: row for row in summary}
assert by_code[tenant.code]["stock_alerts_count"] == 1
assert by_code[other_tenant.code]["stock_alerts_count"] == 1
assert Notification.objects.filter(tenant=tenant, category="warning").count() == 1
assert Notification.objects.filter(tenant=other_tenant, category="warning").count() == 1
@pytest.mark.django_db
def test_channel_sync_actor_inline_with_mock_fallback(tenant):
account = baker.make(
ChannelAccount, tenant=tenant, platform="douyin",
shop_id="DY_TASK", shop_name="任务店铺",
app_key="", access_token="", # 无凭证 → Mock 回退
)
baker.make(Product, tenant=tenant, code="P_TASK", status="active", sale_price=Decimal("30.00"))
result = sync_orders_for_account(account.id)
assert result["pulled_count"] == 1
assert result["converted_count"] == 1
assert SalesOrder.objects.filter(tenant=tenant, remark__contains=result["pulled_order"] if "pulled_order" in result else "").exists() or \
SalesOrder.objects.filter(tenant=tenant).count() == 1