fix sms and ticket user frindly

This commit is contained in:
javad committed 2026-09-14 15:48:13 +03:30
1 parent 1ee83374fd
commit b8b3aba855
30 files changed
+2220 -119

No files matched your search

+143 -10
View File
@@ -421,6 +421,105 @@ def test_due_unread_staff_reply_is_sent_once(category,customer,monkeypatch):
call_command("process_notifications",once=True,job_id=job.pk)
assert len(sent)==1
@pytest.mark.django_db
def test_every_failed_sms_gets_exactly_three_retries_three_minutes_apart(category,customer,monkeypatch,settings):
from django.core.management import call_command
from apps.integrations.sms.base import SMSResult
from apps.notifications.management.commands import process_notifications
settings.SMS_MAX_RETRIES=3
settings.SMS_RETRY_DELAY_SECONDS=180
ticket=create_ticket(customer=customer,category=category,subject="آزمون retry پیامک",body="متن")
job=NotificationJob.objects.get(ticket=ticket,event_type="CUSTOMER_TICKET_CREATED")
calls=[]
class Provider:
def send_message(self,mobile,message):
calls.append((mobile,message))
return SMSResult(False,error="0098SMS [کد -19]: شارژ پنل برای ارسال کافی نیست.",provider_code="-19")
monkeypatch.setattr(process_notifications,"get_sms_provider",lambda:Provider())
for attempt in range(1,5):
before=timezone.now()
call_command("process_notifications",once=True,job_id=job.pk)
job.refresh_from_db()
assert job.attempts==attempt
assert job.provider_status_code=="-19"
assert "کد -19" in job.last_error
if attempt<4:
assert job.status==NotificationJob.Status.RETRYING
assert timedelta(seconds=179) <= job.next_attempt_at-before <= timedelta(seconds=181)
NotificationJob.objects.filter(pk=job.pk).update(next_attempt_at=timezone.now()-timedelta(seconds=1))
else:
assert job.status==NotificationJob.Status.FAILED
call_command("process_notifications",once=True,job_id=job.pk)
assert len(calls)==4
@pytest.mark.django_db
def test_worker_persists_0098_delivery_result(category,customer,monkeypatch,settings):
from django.core.management import call_command
from apps.integrations.sms.base import SMSDeliveryResult
from apps.notifications.management.commands import process_notifications
ticket=create_ticket(customer=customer,category=category,subject="استعلام دلیوری",body="متن")
job=NotificationJob.objects.get(ticket=ticket,event_type="CUSTOMER_TICKET_CREATED")
NotificationJob.objects.filter(pk=job.pk).update(
status=NotificationJob.Status.SENT,
provider_message_id="123456789",
delivery_status=NotificationJob.DeliveryStatus.PENDING,
delivery_next_check_at=timezone.now()-timedelta(seconds=1),
)
class Provider:
def get_delivery_status(self,provider_id):
assert provider_id=="123456789"
return SMSDeliveryResult("DELIVERED","5","0098SMS [کد 5]: پیامک به گوشی گیرنده رسیده است.")
monkeypatch.setattr(process_notifications,"get_sms_provider",lambda:Provider())
call_command("process_notifications",once=True,job_id=job.pk)
job.refresh_from_db()
assert job.delivery_status==NotificationJob.DeliveryStatus.DELIVERED
assert job.delivery_status_code=="5"
assert job.delivery_next_check_at is None
@pytest.mark.django_db
def test_worker_rechecks_provisional_delivery_until_definitive_result(category,customer,monkeypatch,settings):
from django.core.management import call_command
from apps.integrations.sms.base import SMSDeliveryResult
from apps.notifications.management.commands import process_notifications
settings.SMS_DELIVERY_CHECK_DELAY_SECONDS=180
settings.SMS_DELIVERY_MAX_CHECKS=15
ticket=create_ticket(customer=customer,category=category,subject="پیگیری طولانی دلیوری",body="متن")
job=NotificationJob.objects.get(ticket=ticket,event_type="CUSTOMER_TICKET_CREATED")
NotificationJob.objects.filter(pk=job.pk).update(
status=NotificationJob.Status.SENT, sent_at=timezone.now(), provider_message_id="123456789",
delivery_status=NotificationJob.DeliveryStatus.PENDING,
delivery_next_check_at=timezone.now()-timedelta(seconds=1),
)
results=[
SMSDeliveryResult("UNKNOWN","8","وضعیت هنوز مشخص نشده است.",is_final=False),
SMSDeliveryResult("UNDELIVERED","6","تحویل هنوز تأیید نشده است.",is_final=False),
SMSDeliveryResult("DELIVERED","5","پیامک به گوشی رسیده است."),
]
class Provider:
def get_delivery_status(self,provider_id): return results.pop(0)
provider=Provider()
monkeypatch.setattr(process_notifications,"get_sms_provider",lambda:provider)
for expected_status,expected_attempts in ((NotificationJob.DeliveryStatus.UNKNOWN,1),(NotificationJob.DeliveryStatus.UNDELIVERED,2)):
before=timezone.now()
call_command("process_notifications",once=True,job_id=job.pk)
job.refresh_from_db()
assert job.delivery_status==expected_status
assert job.delivery_check_attempts==expected_attempts
assert timedelta(seconds=179) <= job.delivery_next_check_at-before <= timedelta(seconds=181)
NotificationJob.objects.filter(pk=job.pk).update(delivery_next_check_at=timezone.now()-timedelta(seconds=1))
call_command("process_notifications",once=True,job_id=job.pk)
job.refresh_from_db()
assert job.delivery_status==NotificationJob.DeliveryStatus.DELIVERED
assert job.delivery_check_attempts==3 and job.delivery_next_check_at is None
@pytest.mark.django_db
def test_customer_ticket_created_sms_has_seven_day_magic_link(category,customer,settings,monkeypatch):
from datetime import timedelta
@@ -512,16 +611,50 @@ def test_behin_provider_maps_send_array_contract(settings):
assert calls["CheckingMessageID"]=={"long":[0]}
assert calls["SpecialNumber"]=="3000" and calls["IsFlashMessage"] is False
def test_0098_provider_maps_http_contract(settings,monkeypatch):
def test_0098_provider_maps_soap_contract(settings):
from apps.integrations.sms.sms0098 import ZeroZeroNinetyEightSMSProvider
settings.SMS0098_USERNAME="user"; settings.SMS0098_PASSWORD="pass"; settings.SMS0098_FROM="3000"
calls={}
class Response:
text="42"
def raise_for_status(self): return None
def fake_get(url,**kwargs): calls.update(url=url,**kwargs); return Response()
monkeypatch.setattr("apps.integrations.sms.sms0098.requests.get",fake_get)
result=ZeroZeroNinetyEightSMSProvider().send_message("09202202129","پیام آزمایشی")
assert result.success and result.provider_id=="42"
assert calls["params"]["TO"]==9202202129
assert calls["params"]["TEXT"]=="پیام آزمایشی"
class Service:
def SendSMSWithID(self,**parameters): calls.update(parameters); return "123456789"
class Client: service=Service()
provider=ZeroZeroNinetyEightSMSProvider(); provider._client=Client()
result=provider.send_message("09202202129","پیام آزمایشی")
assert result.success and result.provider_id=="123456789"
assert calls["mobileno"]=="09202202129"
assert calls["text"]=="پیام آزمایشی"
assert calls["pnlno"]=="3000"
assert set(calls)=={"username","password","text","mobileno","pnlno"}
@pytest.mark.parametrize("code,expected", [
("-3", "تطابق ندارند"), ("10", "اشتباه است"), ("11", "کاراکتر غیرمجاز"),
("-17", "خالی است"), ("-18", "خطای شارژ"), ("-19", "شارژ پنل"),
("-22", "شماره موبایل"), ("66", "تطابق ندارند"), ("1111", "کاراکتر غیرمجاز"),
("Hang", "مسدود است"), ("No Doc", "مرحله دوم"), ("-23", "مجوز ارسال"),
("-24", "شماره پنل"), ("-25", "اعتبار شماره پنل"), ("-26", "تنظیمات کد OTP"),
("-27", "فرمت کد OTP"), ("-28", "تأیید نشده"), ("Wrong timestamp", "timestamp"),
("Wrong time", "خارج از بازه"), ("-29", "فرمت کد OTP"), ("-31", "یافت نشد"),
("-32", "مطابقت ندارد"), ("13", "IP غیرمجاز"),
])
def test_0098_provider_maps_every_documented_send_error(settings,code,expected):
from apps.integrations.sms.sms0098 import ZeroZeroNinetyEightSMSProvider
settings.SMS0098_USERNAME="user"; settings.SMS0098_PASSWORD="pass"; settings.SMS0098_FROM="3000"
provider=ZeroZeroNinetyEightSMSProvider()
result=provider._parse_send_result(code)
assert not result.success
assert result.provider_code==code
assert expected in result.error
@pytest.mark.parametrize("code,status,is_final", [("4","BLOCKED",True),("5","DELIVERED",True),("6","UNDELIVERED",False),("7","EXPIRED",True),("8","UNKNOWN",False),("9","ERROR",True),("12","ERROR",True),("13","ERROR",True)])
def test_0098_provider_maps_documented_delivery_states(settings,code,status,is_final):
from apps.integrations.sms.sms0098 import ZeroZeroNinetyEightSMSProvider
settings.SMS0098_USERNAME="user"; settings.SMS0098_PASSWORD="pass"; settings.SMS0098_FROM="3000"
class Service:
def smsdeliveryState(self,**parameters): return code
class Client: service=Service()
provider=ZeroZeroNinetyEightSMSProvider(); provider._client=Client()
result=provider.get_delivery_status("123456789")
assert result.status==status and result.provider_code==code and result.is_final is is_final
assert f"کد {code}" in result.description