estelam ticket, varizha , others
This commit is contained in:
1 parent
35a1ec1794
commit
6b546aad76
71 files changed
+2865
-191
No files matched your search
@@ -0,0 +1,103 @@
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from threading import Barrier
|
||||
|
||||
import pytest
|
||||
from django.db import connection, connections
|
||||
from django.db.migrations.executor import MigrationExecutor
|
||||
|
||||
from apps.accounts.models import User
|
||||
from apps.messaging.models import Message
|
||||
from apps.notifications.dispatch_opening import ensure_opening_template
|
||||
from apps.notifications.models import NotificationJob
|
||||
from apps.tickets.dispatch_responses import OPENING_EVENT, open_dispatch_responses, render_opening_sms
|
||||
from apps.tickets.models import DispatchResponseControl, GroupDispatch
|
||||
from apps.tickets.services import reply_ticket
|
||||
from tests.test_dispatch_response_opening import opening_case
|
||||
|
||||
pytestmark = pytest.mark.django_db(transaction=True)
|
||||
|
||||
|
||||
def _postgres_only():
|
||||
if connection.vendor != "postgresql":
|
||||
pytest.skip("Real row-lock concurrency requires PostgreSQL")
|
||||
|
||||
|
||||
def test_two_simultaneous_openings_commit_one_event_and_one_outbox_job(opening_case):
|
||||
_postgres_only()
|
||||
c = opening_case
|
||||
ensure_opening_template()
|
||||
text = render_opening_sms(c["dispatch"])
|
||||
barrier = Barrier(2)
|
||||
def open_once(user_id):
|
||||
connections.close_all()
|
||||
try:
|
||||
user = User.objects.get(pk=user_id)
|
||||
dispatch = GroupDispatch.objects.get(pk=c["dispatch"].pk)
|
||||
barrier.wait(timeout=10)
|
||||
return open_dispatch_responses(dispatch, user, text)[1]
|
||||
finally:
|
||||
connections.close_all()
|
||||
with ThreadPoolExecutor(max_workers=2) as executor:
|
||||
futures = [executor.submit(open_once, c[actor].pk) for actor in ("staff", "admin")]
|
||||
assert sorted(future.result(timeout=20) for future in futures) == [False, True]
|
||||
assert NotificationJob.objects.filter(event_type=OPENING_EVENT).count() == 1
|
||||
assert DispatchResponseControl.objects.get(dispatch=c["dispatch"]).opened_at
|
||||
|
||||
|
||||
def test_reply_racing_opening_matches_the_atomic_recipient_snapshot(opening_case):
|
||||
_postgres_only()
|
||||
c = opening_case
|
||||
ensure_opening_template()
|
||||
text = render_opening_sms(c["dispatch"])
|
||||
barrier = Barrier(2)
|
||||
def run(action):
|
||||
connections.close_all()
|
||||
try:
|
||||
barrier.wait(timeout=10)
|
||||
if action == "reply":
|
||||
return reply_ticket(ticket_id=c["tickets"][1].pk, user=User.objects.get(pk=c["customers"][1].pk), body="پاسخ همزمان")
|
||||
return open_dispatch_responses(c["dispatch"], User.objects.get(pk=c["staff"].pk), text)
|
||||
finally:
|
||||
connections.close_all()
|
||||
with ThreadPoolExecutor(max_workers=2) as executor:
|
||||
futures = [executor.submit(run, action) for action in ("reply", "open")]
|
||||
for future in futures:
|
||||
future.result(timeout=20)
|
||||
control = DispatchResponseControl.objects.get(dispatch=c["dispatch"])
|
||||
expected = set(Message.objects.filter(ticket__group_dispatch=c["dispatch"], author_id__in=[u.pk for u in c["customers"]], created_at__lte=control.opened_at).values_list("author_id", flat=True))
|
||||
assert set(control.respondents_snapshot) == {str(value) for value in User.objects.filter(pk__in=expected).values_list("public_id", flat=True)}
|
||||
assert set(NotificationJob.objects.filter(event_type=OPENING_EVENT).values_list("customer_id", flat=True)) == expected
|
||||
|
||||
|
||||
def test_additive_migrations_preserve_existing_rows_and_legacy_response_visibility(opening_case):
|
||||
c = opening_case
|
||||
# Turn this fixture into a pre-feature dispatch before moving the test schema back.
|
||||
DispatchResponseControl.objects.filter(dispatch=c["dispatch"]).delete()
|
||||
before_dispatch = list(GroupDispatch.objects.values())
|
||||
before_messages = list(Message.objects.values())
|
||||
before_users = list(User.objects.values())
|
||||
old_targets = [("tickets", "0017_ticketaccessgrant_first_viewed_at_and_more"), ("notifications", "0020_receipt_rejection_template")]
|
||||
executor = MigrationExecutor(connection)
|
||||
current_targets = executor.loader.graph.leaf_nodes()
|
||||
try:
|
||||
executor.migrate(old_targets)
|
||||
MigrationExecutor(connection).migrate(current_targets)
|
||||
assert list(GroupDispatch.objects.values()) == before_dispatch
|
||||
assert list(Message.objects.values()) == before_messages
|
||||
assert list(User.objects.values()) == before_users
|
||||
assert not DispatchResponseControl.objects.filter(dispatch=c["dispatch"]).exists()
|
||||
from apps.tickets.staff_conversation import visible_messages
|
||||
assert visible_messages(c["tickets"][0], c["staff"]).filter(pk=c["message"].pk).exists()
|
||||
finally:
|
||||
MigrationExecutor(connection).migrate(current_targets)
|
||||
|
||||
|
||||
def test_migration_refuses_to_drop_response_confidentiality_after_use(opening_case):
|
||||
from django.apps import apps
|
||||
from django.db.migrations.exceptions import IrreversibleError
|
||||
from types import SimpleNamespace
|
||||
import importlib
|
||||
guard = importlib.import_module("apps.tickets.migrations.0018_dispatch_response_opening").preserve_used_response_controls
|
||||
with pytest.raises(IrreversibleError):
|
||||
guard(apps, SimpleNamespace(connection=connection))
|
||||
assert DispatchResponseControl.objects.filter(dispatch=opening_case["dispatch"]).exists()
|
||||
Reference in new issue
Block a user