From 058975dd99de794a66e53594d8b3ff4022bbcd9c Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Thu, 13 Jan 2022 15:26:40 +0100
Subject: [PATCH] axel: handle transport errors (#58925)

---
 passerelle/contrib/utils/axel.py | 10 +++++++---
 tests/test_caluire_axel.py       | 20 ++++++++++++++++++++
 tests/test_toulouse_axel.py      | 21 +++++++++++++++++++++
 3 files changed, 48 insertions(+), 3 deletions(-)

diff --git a/passerelle/contrib/utils/axel.py b/passerelle/contrib/utils/axel.py
index e31f3516..dc62aa01 100644
--- a/passerelle/contrib/utils/axel.py
+++ b/passerelle/contrib/utils/axel.py
@@ -20,16 +20,17 @@ import datetime
 import os
 import re
 import xml.etree.ElementTree as ET
 from collections import namedtuple
 
 import pytz
 import xmlschema
 from django.utils.encoding import force_text
+from zeep.exceptions import TransportError
 
 from passerelle.utils.xml import JSONSchemaFromXMLSchema
 
 boolean_type = {
     'oneOf': [
         {'type': 'boolean'},
         {
             'type': 'string',
@@ -235,19 +236,22 @@ class Operation(object):
                 raise AxelError('invalid request %s' % str(e))
             indent(serialized_request)
             serialized_request = force_text(ET.tostring(serialized_request))
             try:
                 self.request_converter.xml_schema.validate(serialized_request)
             except xmlschema.XMLSchemaValidationError as e:
                 raise AxelError('invalid request %s' % str(e), xml_request=serialized_request)
 
-        result = getattr(client.service, self.data_method)(
-            self.operation, serialized_request, ''
-        )  # FIXME: What is the user parameter for ?
+        try:
+            result = getattr(client.service, self.data_method)(
+                self.operation, serialized_request, ''
+            )  # FIXME: What is the user parameter for ?
+        except TransportError as e:
+            raise AxelError('invalid response %s' % str(e), xml_request=serialized_request)
 
         xml_result = ET.fromstring(result.encode('utf-8'))
         indent(xml_result)
         pretty_result = force_text(ET.tostring(xml_result))
         if xml_result.find('RESULTAT/STATUS').text != 'OK':
             msg = xml_result.find('RESULTAT/COMMENTAIRES').text
             raise AxelError(msg, xml_request=serialized_request, xml_response=pretty_result)
 
diff --git a/tests/test_caluire_axel.py b/tests/test_caluire_axel.py
index cc12823d..e935e430 100644
--- a/tests/test_caluire_axel.py
+++ b/tests/test_caluire_axel.py
@@ -20,16 +20,17 @@ import os
 import xml.etree.ElementTree as ET
 from contextlib import contextmanager
 
 import freezegun
 import mock
 import pytest
 import utils
 import xmlschema
+from zeep.exceptions import TransportError
 
 from passerelle.contrib.caluire_axel import schemas
 from passerelle.contrib.caluire_axel.models import CaluireAxel, Link
 from passerelle.contrib.utils.axel import AxelError, OperationResult, json_date_format, xml_date_format
 from passerelle.utils.soap import SOAPError
 
 XML_RESPONSE_TEMPLATE = '''<?xml version="1.0"?>
 <PORTAILSERVICE>
@@ -216,16 +217,35 @@ def test_operation_status_error(resource):
                             'TEL': None,
                             'MAIL': None,
                         }
                     }
                 },
             )
 
 
+def test_operation_transport_error(resource):
+    with mock.patch('passerelle.contrib.caluire_axel.models.CaluireAxel.soap_client') as client:
+        client.return_value.service.setData.side_effect = TransportError('Foo reason')
+        with pytest.raises(AxelError, match='Foo reason'):
+            schemas.set_paiement(
+                resource,
+                {
+                    'PORTAIL': {
+                        'SETPAIEMENT': {
+                            'IDFACTURE': 42,
+                            'IDENTREGIEENC': 'MAREGIE',
+                            'MONTANT': '4.94',
+                            'IDENTMODEREGLEMENT': 'INCB',
+                        }
+                    }
+                },
+            )
+
+
 def test_link_endpoint_nameid_empty(app, resource, link_params):
     resp = app.post_json('/caluire-axel/test/link?NameID=', params=link_params, status=400)
     assert resp.json['err_desc'] == "NameID is empty"
     assert resp.json['err'] == 'bad-request'
 
 
 def test_link_endpoint_axel_error(app, resource, link_params):
     with mock.patch('passerelle.contrib.caluire_axel.schemas.find_individus') as operation:
diff --git a/tests/test_toulouse_axel.py b/tests/test_toulouse_axel.py
index f16bf4ae..be8711a9 100644
--- a/tests/test_toulouse_axel.py
+++ b/tests/test_toulouse_axel.py
@@ -27,16 +27,17 @@ from contextlib import contextmanager
 
 import freezegun
 import jsonschema
 import mock
 import pytest
 import utils
 import xmlschema
 from django.core.cache import cache
+from zeep.exceptions import TransportError
 
 from passerelle.contrib.toulouse_axel import schemas
 from passerelle.contrib.toulouse_axel.models import Link, Lock, ToulouseAxel
 from passerelle.contrib.toulouse_axel.utils import (
     csp_mapping,
     lien_parente_mapping,
     regime_mapping,
     situation_familiale_mapping,
@@ -296,16 +297,36 @@ def test_operation_status_error(resource):
                             'NOM': 'Doe',
                             'NAISSANCE': '2010-10-10',
                         }
                     }
                 },
             )
 
 
+def test_operation_transport_error(resource):
+    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.soap_client') as client:
+        client.return_value.service.getData.side_effect = TransportError('Foo reason')
+        with pytest.raises(AxelError, match='Foo reason'):
+            schemas.form_paiement_dui(
+                resource,
+                {
+                    'PORTAIL': {
+                        'DUI': {
+                            'DATEPAIEMENT': '01/01/2020 13:00:00',
+                            'IDFACTURE': 42,
+                            'IDREGIEENCAISSEMENT': '',
+                            'MONTANTPAYE': decimal.Decimal('4.94'),
+                            'REFERENCE': 'foo',
+                        }
+                    }
+                },
+            )
+
+
 @contextmanager
 def mock_getdata(content, operation):
     with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.soap_client') as client:
         resp = '''
         <?xml version="1.0"?>
         <PORTAILSERVICE>
           <RESULTAT>
             <TYPE>%s</TYPE>
-- 
2.30.2

