Projet

Général

Profil

0002-lingo-do-not-retry-notify-on-4xx-error-48393.patch

Lauréline Guérin, 23 novembre 2020 15:52

Télécharger (11,1 ko)

Voir les différences:

Subject: [PATCH 2/2] lingo: do not retry notify on 4xx error (#48393)

 combo/apps/lingo/models.py       |  41 +++++++--
 tests/test_lingo_payment.py      | 139 +++++++++++++++++++++++++++++++
 tests/test_lingo_remote_regie.py |   6 +-
 3 files changed, 177 insertions(+), 9 deletions(-)
combo/apps/lingo/models.py
253 253
            return []
254 254
        return []
255 255

  
256
    def get_invoice(self, user, invoice_id, log_errors=True):
256
    def get_invoice(self, user, invoice_id, log_errors=True, raise_4xx=False):
257 257
        if not self.is_remote():
258 258
            return self.basketitem_set.get(pk=invoice_id)
259 259
        url = self.webservice_url + '/invoice/%s/' % invoice_id
260 260
        response = requests.get(url, user=user, remote_service='auto', cache_duration=0, log_errors=log_errors)
261
        if raise_4xx and 400 <= response.status_code < 500:
262
            raise ObjectDoesNotExist()
261 263
        if response.status_code == 404:
262 264
            raise ObjectDoesNotExist()
263 265
        response.raise_for_status()
......
286 288
            'transaction_date': transaction_date.strftime('%Y-%m-%dT%H:%M:%S')
287 289
        }
288 290
        headers = {'content-type': 'application/json'}
289
        return requests.post(url, remote_service='auto',
290
                             data=json.dumps(data), headers=headers).json()
291
        try:
292
            response = requests.post(
293
                url, remote_service='auto',
294
                data=json.dumps(data), headers=headers)
295
            if 400 <= response.status_code < 500:
296
                raise ObjectDoesNotExist()
297
            response.raise_for_status()
298
        except RequestException as e:
299
            raise RemoteInvoiceException from e
300
        try:
301
            resp = response.json()
302
        except ValueError as e:
303
            raise RemoteInvoiceException from e
304
        if resp.get('err'):
305
            raise RemoteInvoiceException
306
        return resp
291 307

  
292 308
    def as_api_dict(self):
293 309
        return {'id': self.slug,
......
620 636
        to_be_paid_remote_items = []
621 637
        for item_id in items.split(','):
622 638
            try:
623
                remote_item = regie.get_invoice(user=self.user, invoice_id=item_id)
639
                remote_item = regie.get_invoice(user=self.user, invoice_id=item_id, raise_4xx=True)
624 640
                regie.pay_invoice(item_id, self.order_id, self.bank_transaction_date or self.end_date)
641
            except ObjectDoesNotExist:
642
                # 4xx error
643
                logger.error(
644
                    'unable to retrieve or pay remote item %s from transaction %s, ignore it',
645
                    item_id, self)
646
            except (RequestException, RemoteInvoiceException):
647
                # 5xx, err or requests error
648
                to_be_paid_remote_items.append(item_id)
649
                logger.warning(
650
                    'unable to notify payment for remote item %s from transaction %s, retry later',
651
                    item_id, self)
625 652
            except Exception:
653
                # unknown error
626 654
                to_be_paid_remote_items.append(item_id)
627
                logger.exception(u'unable to notify payment for remote item %s from transaction %s',
628
                                 item_id, self)
655
                logger.exception(
656
                    'unable to notify payment for remote item %s from transaction %s',
657
                    item_id, self)
629 658
            else:
630 659
                logger.info(u'notified payment for remote item %s from transaction %s',
631 660
                            item_id, self)
tests/test_lingo_payment.py
8 8
import uuid
9 9

  
10 10
from mellon.models import UserSAMLIdentifier
11
from requests.exceptions import ConnectionError
12
from requests.models import Response
11 13

  
12 14
from django.apps import apps
13 15
from django.contrib.auth.models import User
......
1073 1075
    assert transaction.to_be_paid_remote_items == '42,35'
1074 1076

  
1075 1077

  
1078
@mock.patch('combo.utils.requests_wrapper.RequestsSession.request')
1079
def test_transaction_retry_failure(mock_request, remote_regie):
1080
    transaction = Transaction.objects.create(
1081
        status=eopayment.PAID, regie=remote_regie,
1082
        end_date=timezone.now(),
1083
        to_be_paid_remote_items='42,35')
1084

  
1085
    appconfig = apps.get_app_config('lingo')
1086

  
1087
    mock_json_item = mock.Mock(status_code=200)
1088
    mock_json_item.json.return_value = {'err': 0, 'data': {
1089
        'created': '2020-11-23', 'pay_limit_date': '2021-11-23',
1090
        'total_amount': '123.45', 'amount': '123.45'}}
1091

  
1092
    mock_json_paid = mock.Mock(status_code=200)
1093
    mock_json_paid.json.return_value = {'err': 0}
1094

  
1095
    mock_5xx = mock.Mock(status_code=500)
1096

  
1097
    mock_4xx = mock.Mock(status_code=400)
1098

  
1099
    mock_err = mock.Mock(status_code=200)
1100
    mock_err.json.return_url = {'err': 1}
1101

  
1102
    # error on get invoice
1103
    mock_request.side_effect = [
1104
        ConnectionError('where is my hostname?'),  # get invoice 42
1105
        mock_json_item,  # get invoice 35
1106
        mock_json_paid,  # pay invoice 35
1107
    ]
1108
    appconfig.update_transactions()
1109
    transaction.refresh_from_db()
1110
    assert transaction.to_be_paid_remote_items == '42'  # retry for first one
1111

  
1112
    # error on pay invoice
1113
    Transaction.objects.update(to_be_paid_remote_items='42,35')
1114
    mock_request.side_effect = [
1115
        mock_json_item,  # get invoice 42
1116
        ConnectionError('where is my hostname?'),  # pay invoice 42
1117
        mock_json_item,  # get invoice 35
1118
        mock_json_paid,  # pay invoice 35
1119
    ]
1120
    appconfig.update_transactions()
1121
    transaction.refresh_from_db()
1122
    assert transaction.to_be_paid_remote_items == '42'  # retry for first one
1123

  
1124
    # unknown error on get invoice
1125
    Transaction.objects.update(to_be_paid_remote_items='42,35')
1126
    mock_request.side_effect = [
1127
        Exception,  # get invoice 42
1128
        mock_json_item,  # get invoice 35
1129
        mock_json_paid,  # pay invoice 35
1130
    ]
1131
    appconfig.update_transactions()
1132
    transaction.refresh_from_db()
1133
    assert transaction.to_be_paid_remote_items == '42'  # retry for first one
1134

  
1135
    # unknown error on pay invoice
1136
    Transaction.objects.update(to_be_paid_remote_items='42,35')
1137
    mock_request.side_effect = [
1138
        mock_json_item,  # get invoice 42
1139
        Exception,  # pay invoice 42
1140
        mock_json_item,  # get invoice 35
1141
        mock_json_paid,  # pay invoice 35
1142
    ]
1143
    appconfig.update_transactions()
1144
    transaction.refresh_from_db()
1145
    assert transaction.to_be_paid_remote_items == '42'  # retry for first one
1146

  
1147
    # 5xx on get invoice
1148
    Transaction.objects.update(to_be_paid_remote_items='42,35')
1149
    mock_request.side_effect = [
1150
        mock_5xx,  # get invoice 42
1151
        mock_json_item,  # get invoice 35
1152
        mock_json_paid,  # pay invoice 35
1153
    ]
1154
    appconfig.update_transactions()
1155
    transaction.refresh_from_db()
1156
    assert transaction.to_be_paid_remote_items == '42'  # retry for first one
1157

  
1158
    # 5xx on pay invoice
1159
    Transaction.objects.update(to_be_paid_remote_items='42,35')
1160
    mock_request.side_effect = [
1161
        mock_json_item,  # get invoice 42
1162
        mock_5xx,  # pay invoice 42
1163
        mock_json_item,  # get invoice 35
1164
        mock_json_paid,  # pay invoice 35
1165
    ]
1166
    appconfig.update_transactions()
1167
    transaction.refresh_from_db()
1168
    assert transaction.to_be_paid_remote_items == '42'  # retry for first one
1169

  
1170
    # err on get invoice
1171
    Transaction.objects.update(to_be_paid_remote_items='42,35')
1172
    mock_request.side_effect = [
1173
        mock_err,  # get invoice 42
1174
        mock_json_item,  # get invoice 35
1175
        mock_json_paid,  # pay invoice 35
1176
    ]
1177
    appconfig.update_transactions()
1178
    transaction.refresh_from_db()
1179
    assert transaction.to_be_paid_remote_items == '42'  # retry for first one
1180

  
1181
    # err on pay invoice
1182
    Transaction.objects.update(to_be_paid_remote_items='42,35')
1183
    mock_request.side_effect = [
1184
        mock_json_item,  # get invoice 42
1185
        mock_err,  # pay invoice 42
1186
        mock_json_item,  # get invoice 35
1187
        mock_json_paid,  # pay invoice 35
1188
    ]
1189
    appconfig.update_transactions()
1190
    transaction.refresh_from_db()
1191
    assert transaction.to_be_paid_remote_items == '42'  # retry for first one
1192

  
1193
    # 4xx on get invoice
1194
    Transaction.objects.update(to_be_paid_remote_items='42,35')
1195
    mock_request.side_effect = [
1196
        mock_4xx,  # get invoice 42
1197
        mock_5xx,  # get invoice 35
1198
    ]
1199
    appconfig.update_transactions()
1200
    transaction.refresh_from_db()
1201
    assert transaction.to_be_paid_remote_items == '35'  # retry for the second only
1202

  
1203
    # 4xx on pay invoice
1204
    Transaction.objects.update(to_be_paid_remote_items='42,35')
1205
    mock_request.side_effect = [
1206
        mock_json_item,  # get invoice 42
1207
        mock_4xx,  # get invoice 35
1208
        mock_5xx,  # pay invoice 35
1209
    ]
1210
    appconfig.update_transactions()
1211
    transaction.refresh_from_db()
1212
    assert transaction.to_be_paid_remote_items == '35'  # retry for the second only
1213

  
1214

  
1076 1215
def test_transaction_validate(app, key, regie, user):
1077 1216
    t1 = Transaction(regie=regie, bank_data={'bank': 'data'}, amount=12,
1078 1217
            status=eopayment.PAID)
tests/test_lingo_remote_regie.py
315 315
    invoices = copy.deepcopy(INVOICES)
316 316
    invoices[0]['amount'] = '100.00'
317 317
    invoices[0]['amount_paid'] = '23.45'
318
    mock_json = mock.Mock()
318
    mock_json = mock.Mock(status_code=200)
319 319
    mock_json.json.return_value = {'err': 0, 'data': invoices[0]}
320 320
    mock_get.return_value = mock_json
321 321
    mock_pay_invoice.return_value = mock.Mock(status_code=200)
......
325 325
    assert 'Amount already paid: <span class="amount">23.45€</span>' in resp.text
326 326

  
327 327
    # invoice without amount_paid
328
    mock_json = mock.Mock()
328
    mock_json = mock.Mock(status_code=200)
329 329
    mock_json.json.return_value = {'err': 0, 'data': INVOICES[0]}
330 330
    mock_get.return_value = mock_json
331 331
    mock_pay_invoice.return_value = mock.Mock(status_code=200)
......
491 491
    page.save()
492 492
    assert remote_regie.is_remote()
493 493
    encrypt_id = aes_hex_encrypt(settings.SECRET_KEY, force_bytes('F201601'))
494
    mock_json = mock.Mock()
494
    mock_json = mock.Mock(status_code=200)
495 495
    mock_json.json.return_value = {'err': 0, 'data': INVOICES[0]}
496 496
    mock_get.return_value = mock_json
497 497
    mock_pay_invoice.return_value = mock.Mock(status_code=200)
498
-