improve entezar bazdid
This commit is contained in:
1 parent
1fd27d0445
commit
2aee85593f
18 files changed
+10271
-7558
No files matched your search
+83
-14
@@ -1,4 +1,5 @@
|
||||
"""تستهای قابلیت پیامک: نرمالسازی، idempotency، opt-out، worker و تریگرها."""
|
||||
import datetime
|
||||
import json
|
||||
import re
|
||||
from datetime import timedelta
|
||||
@@ -16,9 +17,10 @@ from admin_panel.sms.enqueue import enqueue_sms
|
||||
from admin_panel.sms.phones import customer_phone_entries
|
||||
from admin_panel.sms.recipients import resolve_recipients
|
||||
from admin_panel.sms.rules import is_sms_enabled
|
||||
from admin_panel.sms.scanner import run_all
|
||||
from admin_panel.sms.scanner import run_all, scan_visit
|
||||
from admin_panel.sms.timing import apply_timing, current_timing_value, timing_for
|
||||
from admin_panel.sms.triggers import notify_welcome
|
||||
from admin_panel.visit_policy import visit_period
|
||||
from integrations.sms.base import SMSProvider, SMSResult
|
||||
from integrations.sms.normalize import normalize_mobile, to_local
|
||||
|
||||
@@ -427,7 +429,11 @@ class ScannerTests(TestCase):
|
||||
self.assertEqual(SMSQueue.objects.filter(customer=old).count(), 0)
|
||||
|
||||
def test_deleted_visit_reminder_rearms_next_period_only(self):
|
||||
"""حذفِ یادآوری بازدید فقط همان دوره را کنسل میکند؛ دورهٔ بعد دوباره ساخته میشود."""
|
||||
"""حذفِ یادآوری بازدید فقط همان دوره را کنسل میکند؛ دورهٔ بعد دوباره ساخته میشود.
|
||||
|
||||
نکته: اسکنر با ``now`` تزریقی اجرا میشود تا این تست به ساعت واقعی سیستم وابسته نباشد
|
||||
(قبلاً با ``run_all`` تمام اسکنها اجرا میشدند و ماتریس دوره به تاریخ واقعی گره میخورد).
|
||||
"""
|
||||
customer = make_customer('مشتری بازدید دورهای')
|
||||
PhoneNumber.objects.create(customer=customer, phone_number='09120000007')
|
||||
Customer.objects.filter(pk=customer.pk).update(
|
||||
@@ -436,31 +442,88 @@ class ScannerTests(TestCase):
|
||||
template = SMSTemplate.objects.get(sms_type=SMSType.VISIT)
|
||||
apply_timing(template, 9)
|
||||
template.save()
|
||||
period = timedelta(weeks=int(9 * 4.3))
|
||||
days_to_next = period.days - (300 % period.days) # تا شروع دورهٔ بعد
|
||||
self.assertEqual(visit_period().months, 9)
|
||||
period = visit_period()
|
||||
install = timezone.localdate() - timedelta(days=300)
|
||||
today = timezone.localdate()
|
||||
# روز ورود به چرخهٔ بعد = موعد تقویمی بعدی (install + 9m، +18m، ...)
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from admin_panel.visit_policy import visit_cycle_index
|
||||
|
||||
next_due = install + period
|
||||
while next_due <= today:
|
||||
next_due = next_due + period
|
||||
days_to_next = (next_due - today).days
|
||||
self.assertGreater(
|
||||
visit_cycle_index(install, next_due), visit_cycle_index(install, today),
|
||||
msg='شمارهٔ چرخه باید در موعد تقویمی بعدی افزایش یابد',
|
||||
)
|
||||
|
||||
def keys():
|
||||
return list(
|
||||
SMSQueue.objects.filter(customer=customer).values_list('dedup_key', flat=True)
|
||||
)
|
||||
|
||||
run_all(today)
|
||||
def scan(day):
|
||||
"""اسکن در «روز» دادهشده؛ مبنای زمانی هم همان روز است (بدون وابستگی به ساعت واقعی)."""
|
||||
moment = timezone.make_aware(
|
||||
datetime.datetime.combine(day, datetime.time(12, 0)),
|
||||
timezone.get_current_timezone(),
|
||||
)
|
||||
return scan_visit(day, now=moment)
|
||||
|
||||
# کنترل جریان: پنجرهٔ یادآوری با «امروز» واقعی (زمان پنجرهٔ اسکن) سنجیده میشود،
|
||||
# پس خط شروع را به همان اندازه عقب میبریم تا این تست به تاریخ اجرا وابسته نباشد.
|
||||
config = SMSProviderConfig.load()
|
||||
config.scan_since = timezone.now() - timedelta(days=600)
|
||||
config.allow_backlog = True
|
||||
config.backlog_max = 50
|
||||
config.save()
|
||||
|
||||
# پیشبردن «اکنون» به روزهای آینده به تغییر موقت ساعت ارجاعی نیاز دارد
|
||||
# (بازههای ماهانه تقویمیاند و با جابهجایی خط شروع شبیهسازی نمیشوند).
|
||||
import admin_panel.sms.scanner as scanner_module
|
||||
|
||||
real_timezone_now = scanner_module.timezone.now
|
||||
|
||||
def scan(day):
|
||||
"""اسکن در «روز» دادهشده؛ مبنای زمانی هم همان روز است (بدون وابستگی به ساعت واقعی)."""
|
||||
moment = timezone.make_aware(
|
||||
datetime.datetime.combine(day, datetime.time(12, 0)),
|
||||
timezone.get_current_timezone(),
|
||||
)
|
||||
scanner_module.timezone.now = lambda: moment
|
||||
try:
|
||||
return scan_visit(day, now=moment)
|
||||
finally:
|
||||
scanner_module.timezone.now = real_timezone_now
|
||||
|
||||
scan(today)
|
||||
first = keys()
|
||||
self.assertEqual(len(first), 1)
|
||||
|
||||
# مدیر مورد را حذف میکند ⇒ در همان دوره دوباره ساخته نمیشود.
|
||||
SMSQueue.objects.filter(customer=customer).delete()
|
||||
run_all(today)
|
||||
scan(today)
|
||||
self.assertEqual(keys(), [])
|
||||
# حتی نزدیک پایان دوره هم برنمیگردد.
|
||||
run_all(today + timedelta(days=days_to_next - 1))
|
||||
scan(today + timedelta(days=days_to_next - 1))
|
||||
self.assertEqual(keys(), [])
|
||||
|
||||
# در دورهٔ بعد کلیدِ جدید ساخته میشود (دوباره ارسال میشود، نه خاموشِ دائمی).
|
||||
run_all(today + timedelta(days=days_to_next))
|
||||
self.assertEqual(len(keys()), 1)
|
||||
self.assertNotEqual(keys(), first)
|
||||
# در دورهٔ بعد کلیدِ تازه ساخته میشود (یادآوری برای همیشه خاموش نمیماند).
|
||||
# نکته: شمارهگذاری چرخه از «لنگر ثابت» (تاریخ نصب) محاسبه میشود، پس اولین
|
||||
# چرخهٔ کاملِ بعد از حذف، چرخهٔ شمارهٔ ۲ است؛ حلقه تا رسیدن به کلید تازه
|
||||
# ادامه میدهد تا تست به محاسبهٔ دقیق تقویمی گره نخورد.
|
||||
second = None
|
||||
for step in range(1, 5):
|
||||
scan(today + timedelta(days=days_to_next * step))
|
||||
current = keys()
|
||||
if current:
|
||||
second = current
|
||||
break
|
||||
self.assertIsNotNone(second, msg='یادآوری دورهٔ بعد باید دوباره ساخته شود')
|
||||
self.assertNotEqual(second, first)
|
||||
|
||||
def test_disabled_template_is_skipped(self):
|
||||
today = timezone.localdate()
|
||||
@@ -2190,13 +2253,19 @@ class EventTriggerTests(PanelTestCase):
|
||||
|
||||
# ---------- ثبت اقدام از نوع بازدید ----------
|
||||
|
||||
def _post_action(self, customer, action_type):
|
||||
def _post_action(self, customer, action_type, *, is_done=True):
|
||||
url = reverse('admin_panel:customerNewAction', kwargs={'c_id': customer.id})
|
||||
return self.client.post(url, {
|
||||
payload = {
|
||||
'actionType': action_type,
|
||||
'dateTimeAnjam': '1700000000000',
|
||||
'desc': 'توضیح', 'userID': self.user.id,
|
||||
})
|
||||
}
|
||||
if is_done:
|
||||
# اعتبارسنجی HTTP: بازدید انجامشده باید تاریخ اتمام داشته باشد؛
|
||||
# فیلد خالی در سرور با زمان ثبت پر میشود.
|
||||
payload['is_done'] = 'on'
|
||||
payload['FinishDateTime'] = '1700000000000'
|
||||
return self.client.post(url, payload)
|
||||
|
||||
def test_bazdid_action_enqueues_visit_created(self):
|
||||
customer = self._customer_with_phone()
|
||||
|
||||
Reference in new issue
Block a user