lingo: poll backend during asynchronous rendering (#57790)

This commit is contained in:
Benjamin Dauvergne 2021-10-13 19:34:59 +02:00
parent 0aac640e37
commit f92b855774
3 changed files with 69 additions and 17 deletions

View File

@ -638,13 +638,16 @@ class BasketItem(models.Model):
ordering = ['regie', 'extra_fee', 'subject']
@classmethod
def get_items_to_be_paid(cls, user):
def get_items_to_be_paid(cls, user, poll=False, raise_on_poll=False):
qs = cls.objects.filter(
user=user, payment_date__isnull=True, waiting_date__isnull=True, cancellation_date__isnull=True
)
for transaction in Transaction.objects.filter(items__in=qs):
if transaction.can_poll_backend():
transaction.poll_backend()
if poll:
for transaction in Transaction.objects.filter(items__in=qs):
if transaction.can_poll_backend():
if raise_on_poll:
raise NothingInCacheException
transaction.poll_backend()
return qs
def notify(self, status):
@ -1087,7 +1090,9 @@ class LingoBasketCell(CellBase):
def render(self, context):
basket_template = template.loader.get_template('lingo/combo/basket.html')
items = BasketItem.get_items_to_be_paid(context['request'].user)
items = BasketItem.get_items_to_be_paid(
context['request'].user, poll=True, raise_on_poll=not context.get('synchronous')
)
regies = {}
for item in items:
if not item.regie_id in regies:
@ -1112,7 +1117,7 @@ class LingoRecentTransactionsCell(CellBase):
def is_enabled(cls):
return Regie.objects.exists()
def get_transactions_queryset(self, context):
def get_transactions_queryset(self, context, poll=False):
user = context['request'].user
# list transactions :
# * paid by the user
@ -1120,9 +1125,12 @@ class LingoRecentTransactionsCell(CellBase):
qs = Transaction.objects.filter(models.Q(user=user) | models.Q(items__user=user)).filter(
start_date__gte=timezone.now() - datetime.timedelta(days=7)
)
for transaction in qs:
if transaction.can_poll_backend() and transaction.is_running():
transaction.poll_backend()
if poll:
for transaction in qs:
if transaction.can_poll_backend() and transaction.is_running():
if not context.get('synchronous'):
raise NothingInCacheException
transaction.poll_backend()
return qs
def is_relevant(self, context):
@ -1132,7 +1140,9 @@ class LingoRecentTransactionsCell(CellBase):
def render(self, context):
recent_transactions_template = template.loader.get_template('lingo/combo/recent_transactions.html')
context['transactions'] = self.get_transactions_queryset(context).distinct().order_by('-start_date')
context['transactions'] = (
self.get_transactions_queryset(context, poll=True).distinct().order_by('-start_date')
)
return recent_transactions_template.render(context)

View File

@ -70,4 +70,14 @@ def nocache(settings):
@pytest.fixture
def synchronous_cells(settings):
settings.COMBO_TEST_ALWAYS_RENDER_CELLS_SYNCHRONOUSLY = True
class M:
@staticmethod
def on():
settings.COMBO_TEST_ALWAYS_RENDER_CELLS_SYNCHRONOUSLY = True
@staticmethod
def off():
settings.COMBO_TEST_ALWAYS_RENDER_CELLS_SYNCHRONOUSLY = False
M.on()
return M

View File

@ -2153,6 +2153,8 @@ class TestPolling:
self,
payment_status,
app,
user,
synchronous_cells,
):
# Try to pay
pay_resp = app.get('/test_basket_cell/')
@ -2171,15 +2173,40 @@ class TestPolling:
order_id=transaction.order_id,
)
# Try to pay again
# check get_items_to_be_paid() does not poll anymore
BasketItem.get_items_to_be_paid(user)
assert payment_status.call_count == 0
# Try to pay again, only with current information
synchronous_cells.off()
resp = app.get('/test_basket_cell/')
assert 'foo item' not in resp
assert 'Pay' not in resp
assert 'Running' in resp
assert 'Loading' in resp.pyquery('.lingo-basket-cell').text()
assert 'Loading' in resp.pyquery('.lingo-recent-transactions-cell').text()
resp = pay_resp.click('Pay').follow()
assert 'Some items are already paid or' in resp
assert 'foo item' not in resp
assert 'Running' in resp
assert len(resp.pyquery('.lingo-basket-cell')) == 0
assert 'Loading' in resp.pyquery('.lingo-recent-transactions-cell').text()
assert payment_status.call_count == 1 # pay made a call to payfip backend
payment_status.reset_mock()
# Make rendering synchronous and retry
synchronous_cells.on()
resp = app.get('/test_basket_cell/')
assert len(resp.pyquery('.lingo-basket-cell')) == 0
assert 'Running' in resp.pyquery('.lingo-recent-transactions-cell').text()
assert payment_status.call_count == 1 # transactions cell polled
payment_status.reset_mock()
resp = pay_resp.click('Pay')
assert payment_status.call_count == 1 # pay polled
payment_status.reset_mock()
resp = resp.follow()
assert 'Some items are already paid or' in resp
assert len(resp.pyquery('.lingo-basket-cell')) == 0
assert 'Running' in resp.pyquery('.lingo-recent-transactions-cell').text()
assert payment_status.call_count == 1 # transactions cell polled
payment_status.reset_mock()
# Simulate paid status on polling
payment_status.return_value = eopayment.common.PaymentResponse(
@ -2193,12 +2220,17 @@ class TestPolling:
assert 'foo item: 42.00' in resp
assert 'Pay' not in resp
assert 'Running' not in resp
assert len(resp.pyquery('.lingo-basket-cell')) == 0
assert '42.00' in resp.pyquery('.lingo-recent-transactions-cell').text()
assert payment_status.call_count == 1 # transactions cell polled
payment_status.reset_mock()
@mock.patch('eopayment.payfip_ws.Payment.payment_status')
def test_exception_during_polling(
self,
payment_status,
app,
synchronous_cells,
caplog,
):
# Try to pay