diff --git a/debian/control b/debian/control
index b8ae6b3f..a3f92e14 100644
--- a/debian/control
+++ b/debian/control
@@ -17,6 +17,7 @@ Depends: ghostscript,
pdftk,
poppler-utils,
python3-cmislib,
+ python3-cryptography,
python3-dateutil,
python3-distutils,
python3-django (>= 2:3.2),
@@ -43,6 +44,7 @@ Depends: ghostscript,
python3-uwsgidecorators,
python3-vobject,
python3-xmlschema,
+ python3-xmltodict,
python3-zeep (>= 3.2),
${misc:Depends},
${python3:Depends},
diff --git a/passerelle/apps/sne/__init__.py b/passerelle/apps/sne/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/passerelle/apps/sne/migrations/0001_initial.py b/passerelle/apps/sne/migrations/0001_initial.py
new file mode 100644
index 00000000..14c911be
--- /dev/null
+++ b/passerelle/apps/sne/migrations/0001_initial.py
@@ -0,0 +1,75 @@
+# Generated by Django 3.2.18 on 2023-05-31 08:55
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ initial = True
+
+ dependencies = [
+ ('base', '0030_resourcelog_base_resour_appname_298cbc_idx'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='SNE',
+ fields=[
+ (
+ 'id',
+ models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
+ ),
+ ('title', models.CharField(max_length=50, verbose_name='Title')),
+ ('slug', models.SlugField(unique=True, verbose_name='Identifier')),
+ ('description', models.TextField(verbose_name='Description')),
+ (
+ 'basic_auth_username',
+ models.CharField(
+ blank=True, max_length=128, verbose_name='Basic authentication username'
+ ),
+ ),
+ (
+ 'basic_auth_password',
+ models.CharField(
+ blank=True, max_length=128, verbose_name='Basic authentication password'
+ ),
+ ),
+ (
+ 'client_certificate',
+ models.FileField(
+ blank=True, null=True, upload_to='', verbose_name='TLS client certificate'
+ ),
+ ),
+ (
+ 'trusted_certificate_authorities',
+ models.FileField(blank=True, null=True, upload_to='', verbose_name='TLS trusted CAs'),
+ ),
+ (
+ 'verify_cert',
+ models.BooleanField(blank=True, default=True, verbose_name='TLS verify certificates'),
+ ),
+ (
+ 'http_proxy',
+ models.CharField(blank=True, max_length=128, verbose_name='HTTP and HTTPS proxy'),
+ ),
+ (
+ 'wsdl_url',
+ models.URLField(
+ help_text='URL of the WSDL file', max_length=400, verbose_name='WSDL URL'
+ ),
+ ),
+ (
+ 'certificate_name',
+ models.CharField(max_length=128, verbose_name='SOAP client certificate name'),
+ ),
+ (
+ 'users',
+ models.ManyToManyField(
+ blank=True, related_name='_sne_sne_users_+', related_query_name='+', to='base.ApiUser'
+ ),
+ ),
+ ],
+ options={
+ 'verbose_name': 'SNE',
+ },
+ ),
+ ]
diff --git a/passerelle/apps/sne/migrations/__init__.py b/passerelle/apps/sne/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/passerelle/apps/sne/models.py b/passerelle/apps/sne/models.py
new file mode 100644
index 00000000..0f1633b1
--- /dev/null
+++ b/passerelle/apps/sne/models.py
@@ -0,0 +1,78 @@
+# passerelle - uniform access to multiple data sources and services
+# Copyright (C) 2023 Entr'ouvert
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+
+import xmltodict
+from cryptography import x509
+from cryptography.hazmat.primitives import serialization
+from django.db import models
+from django.utils.translation import gettext_lazy as _
+
+from passerelle.base.models import BaseResource, HTTPResource
+from passerelle.utils.api import endpoint
+
+
+class SNE(BaseResource, HTTPResource):
+ wsdl_url = models.URLField(
+ max_length=400, verbose_name=_('WSDL URL'), help_text=_('URL of the WSDL file')
+ )
+ certificate_name = models.CharField(max_length=128, verbose_name=_('Client certificate name'))
+ category = _('Business Process Connectors')
+
+ class Meta:
+ verbose_name = _('SNE')
+
+ @classmethod
+ def get_manager_form_class(cls, **kwargs):
+ form_class = super().get_manager_form_class(**kwargs)
+ form_class.base_fields['client_certificate'].required = True
+ return form_class
+
+ @property
+ def cert_public_bytes(self):
+ with self.client_certificate.open('rb') as f:
+ certs = x509.load_pem_x509_certificates(f.read())
+ cert = certs[0]
+ return cert.public_bytes(encoding=serialization.Encoding.PEM)
+
+ def check_status(self):
+ response = self.requests.get(self.wsdl_url)
+ response.raise_for_status()
+
+ @endpoint(
+ name='get-demande-logement',
+ description=_('Get informations on housing demand'),
+ parameters={
+ 'demand_id': {
+ 'example_value': '1',
+ }
+ },
+ )
+ def get_demande_logement(self, request, demand_id, **kwargs):
+ client = self.soap_client(wsdl_url=self.wsdl_url, api_error=True)
+ cert_type = client.get_type('{http://ws.metier.nuu.application.i2/}base64Binary')
+ cert = cert_type(_value_1=self.cert_public_bytes)
+ res = client.service.getDemandeLogement(
+ numUnique=demand_id, nomCertificat=self.certificate_name, certificat=cert
+ )
+ namespaces = {
+ 'http://nuu.application.i2/': None,
+ }
+ return {
+ 'data': xmltodict.parse(
+ res['fichierDemande']['_value_1'], process_namespaces=True, namespaces=namespaces
+ )
+ }
diff --git a/passerelle/settings.py b/passerelle/settings.py
index a45d953c..1223c7de 100644
--- a/passerelle/settings.py
+++ b/passerelle/settings.py
@@ -179,6 +179,7 @@ INSTALLED_APPS = (
'passerelle.apps.signal_arretes',
'passerelle.apps.sivin',
'passerelle.apps.smsfactor',
+ 'passerelle.apps.sne',
'passerelle.apps.soap',
'passerelle.apps.solis',
'passerelle.apps.twilio',
diff --git a/setup.py b/setup.py
index 8b0b0ca2..a2f5d75e 100755
--- a/setup.py
+++ b/setup.py
@@ -172,6 +172,8 @@ setup(
'python-ldap',
'pyOpenSSL',
'roman',
+ 'cryptography',
+ 'xmltodict',
],
cmdclass={
'build': build,
diff --git a/tests/data/sne/DemandeLogementImplService.wsdl b/tests/data/sne/DemandeLogementImplService.wsdl
new file mode 100644
index 00000000..3839b561
--- /dev/null
+++ b/tests/data/sne/DemandeLogementImplService.wsdl
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/data/sne/DemandeLogementImplService.xsd b/tests/data/sne/DemandeLogementImplService.xsd
new file mode 100644
index 00000000..0cb9bff0
--- /dev/null
+++ b/tests/data/sne/DemandeLogementImplService.xsd
@@ -0,0 +1,154 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/data/sne/DemandeLogementImplService1.wsdl b/tests/data/sne/DemandeLogementImplService1.wsdl
new file mode 100644
index 00000000..c0cd96ba
--- /dev/null
+++ b/tests/data/sne/DemandeLogementImplService1.wsdl
@@ -0,0 +1,72 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/data/sne/cert.pem b/tests/data/sne/cert.pem
new file mode 100644
index 00000000..b2578548
--- /dev/null
+++ b/tests/data/sne/cert.pem
@@ -0,0 +1,74 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 4c:aa:29:4f:be:82:16:5b:15:70:8e:c5:02:29:5a:60:b2:0b:cf:4a
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: CN = localhost.entrouvert.org
+ Validity
+ Not Before: Dec 5 16:59:24 2018 GMT
+ Not After : Dec 2 16:59:24 2028 GMT
+ Subject: CN = localhost.entrouvert.org
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:e3:1b:0b:9f:6e:72:8a:db:5b:8e:4e:5b:70:ab:
+ d8:d8:8f:33:3e:2b:cb:e9:85:a7:d8:c2:62:99:a4:
+ 10:48:dc:c2:c3:ff:0f:3a:65:4f:2e:63:08:6f:79:
+ 5c:66:3c:9d:15:16:9d:f9:14:f1:65:f9:ee:bc:20:
+ 89:ae:71:ab:1b:b8:4e:b0:93:5e:9d:32:cb:53:4b:
+ a0:37:fc:90:d7:ce:bd:70:cd:3b:6b:db:63:4b:32:
+ fb:34:2c:fd:1f:53:48:2b:5f:90:70:1b:13:13:17:
+ 3b:ed:d3:96:a7:05:88:f5:38:ea:61:84:4b:fb:37:
+ b1:de:69:7c:71:da:9c:43:b0:50:51:0c:40:bc:0e:
+ 14:53:ad:c3:33:61:be:e4:ed:dd:13:b9:7c:ba:fc:
+ 81:51:4d:e4:5b:fb:21:1f:28:5f:c1:e5:8d:5c:ef:
+ 08:5d:72:23:bc:37:cf:62:43:ac:8d:ce:9e:9f:69:
+ 05:32:bf:ac:10:d4:94:2a:07:c3:2a:3c:ff:53:3a:
+ b2:4b:b2:c1:6f:8b:64:24:02:d9:33:c4:0f:e7:a5:
+ 5e:40:01:b5:25:91:76:27:6f:f2:d5:1a:81:7c:81:
+ 91:20:49:18:a1:a1:dc:8f:a5:00:4d:96:b5:c5:c6:
+ 63:32:e6:cb:cf:7a:2d:44:d6:1c:45:72:89:31:85:
+ 86:6d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Alternative Name:
+ DNS:localhost.entrouvert.org
+ Signature Algorithm: sha256WithRSAEncryption
+ 15:53:da:bc:16:a1:98:88:e0:9d:38:ea:e9:96:c5:c5:74:cd:
+ 25:6d:13:ae:a3:7c:a2:56:ca:27:a4:9f:c1:65:64:d0:8f:29:
+ c7:b3:ce:7a:41:5a:5d:df:9e:82:c1:49:95:66:32:1f:da:b8:
+ 1e:42:a8:b5:d7:51:61:8a:d6:a1:77:0f:8a:87:4d:7d:46:be:
+ 6d:19:e1:d9:66:25:da:b1:06:31:6e:5d:6c:17:ff:31:80:83:
+ b6:ce:bc:73:74:a8:03:b2:48:60:9f:30:d1:06:46:02:48:a3:
+ 28:db:55:30:bd:3f:16:b2:ee:25:66:df:14:10:16:1f:c2:41:
+ e3:a0:5c:02:0f:fc:7e:56:cc:1c:05:0b:cd:5d:c5:d6:0f:ef:
+ d0:0d:c5:1b:76:3f:f5:f5:6b:fa:53:79:aa:cf:8d:07:d3:57:
+ 20:c7:5f:e7:05:eb:98:94:18:46:3a:2d:c8:e8:d4:3b:ac:93:
+ 16:a0:c5:be:a1:3b:0c:4a:4a:40:3e:61:9e:fa:89:a1:70:b3:
+ cd:84:12:d4:38:c7:d6:a4:91:73:9b:c2:f0:8e:d8:94:9a:30:
+ 09:f9:c8:f4:cb:04:20:24:ab:b2:4e:f9:0e:14:f4:f7:56:89:
+ 0b:65:d7:f7:7a:38:ce:17:cd:c6:63:b9:2a:3d:bc:84:ff:3b:
+ 18:78:0d:22
+-----BEGIN CERTIFICATE-----
+MIIDBjCCAe6gAwIBAgIUTKopT76CFlsVcI7FAilaYLILz0owDQYJKoZIhvcNAQEL
+BQAwIzEhMB8GA1UEAwwYbG9jYWxob3N0LmVudHJvdXZlcnQub3JnMB4XDTE4MTIw
+NTE2NTkyNFoXDTI4MTIwMjE2NTkyNFowIzEhMB8GA1UEAwwYbG9jYWxob3N0LmVu
+dHJvdXZlcnQub3JnMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4xsL
+n25yittbjk5bcKvY2I8zPivL6YWn2MJimaQQSNzCw/8POmVPLmMIb3lcZjydFRad
++RTxZfnuvCCJrnGrG7hOsJNenTLLU0ugN/yQ1869cM07a9tjSzL7NCz9H1NIK1+Q
+cBsTExc77dOWpwWI9TjqYYRL+zex3ml8cdqcQ7BQUQxAvA4UU63DM2G+5O3dE7l8
+uvyBUU3kW/shHyhfweWNXO8IXXIjvDfPYkOsjc6en2kFMr+sENSUKgfDKjz/Uzqy
+S7LBb4tkJALZM8QP56VeQAG1JZF2J2/y1RqBfIGRIEkYoaHcj6UATZa1xcZjMubL
+z3otRNYcRXKJMYWGbQIDAQABozIwMDAJBgNVHRMEAjAAMCMGA1UdEQQcMBqCGGxv
+Y2FsaG9zdC5lbnRyb3V2ZXJ0Lm9yZzANBgkqhkiG9w0BAQsFAAOCAQEAFVPavBah
+mIjgnTjq6ZbFxXTNJW0TrqN8olbKJ6SfwWVk0I8px7POekFaXd+egsFJlWYyH9q4
+HkKotddRYYrWoXcPiodNfUa+bRnh2WYl2rEGMW5dbBf/MYCDts68c3SoA7JIYJ8w
+0QZGAkijKNtVML0/FrLuJWbfFBAWH8JB46BcAg/8flbMHAULzV3F1g/v0A3FG3Y/
+9fVr+lN5qs+NB9NXIMdf5wXrmJQYRjotyOjUO6yTFqDFvqE7DEpKQD5hnvqJoXCz
+zYQS1DjH1qSRc5vC8I7YlJowCfnI9MsEICSrsk75DhT091aJC2XX93o4zhfNxmO5
+Kj28hP87GHgNIg==
+-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/tests/data/sne/response_does_not_exist b/tests/data/sne/response_does_not_exist
new file mode 100644
index 00000000..52d1c37d
--- /dev/null
+++ b/tests/data/sne/response_does_not_exist
@@ -0,0 +1,7 @@
+--uuid:7902e9bd-21a8-4632-8760-d79a67eb89a1
+Content-Id:
+Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml"
+Content-Transfer-Encoding: binary
+
+http://www.w3.org/2005/08/addressing/faultuuid:1e213982-4a8d-4722-bb24-a2c5f48dd5c7urn:uuid:93d3b273-c123-4277-b288-24a77b1e90dchttp://www.w3.org/2005/08/addressing/anonymousS:ReceiverLa demande de logement n'existe pas dans le système.
+--uuid:7902e9bd-21a8-4632-8760-d79a67eb89a1--
\ No newline at end of file
diff --git a/tests/data/sne/response_ok b/tests/data/sne/response_ok
new file mode 100644
index 00000000..f1856afc
--- /dev/null
+++ b/tests/data/sne/response_ok
@@ -0,0 +1,13 @@
+--uuid:db03b44e-f563-4ce9-b06d-545faf9b26c0
+Content-Id:
+Content-Type: application/xop+xml;charset=utf-8;type="application/soap+xml"
+Content-Transfer-Encoding: binary
+
+http://ws.metier.nuu.application.i2/DemandeLogementPortType/getDemandeLogementResponseuuid:1764a9fe-6a19-4833-b104-e253e7c9d6bfurn:uuid:ad0713e6-cfb4-43f5-a69a-12db65925ee3http://www.w3.org/2005/08/addressing/anonymousDEMG1318-202305311447-000001.xml
+--uuid:db03b44e-f563-4ce9-b06d-545faf9b26c0
+Content-Id:
+Content-Type: text/xml
+Content-Transfer-Encoding: binary
+
+RET2023-05-31T14:47:28.201+02:00GUIPJ0690221008931G31632021-02-11falsefalsetest02/0315000falsetruetruefalse2023-05-28T00:24:11.102+02:00truetruetruetrue695009231Quartile 3694008932Quartile 3691009231Quartile 3697609231Quartile 3693009231Quartile 36974011247Quartile 2693309231Quartile 36948010836Quartile 2TESTDDO-totot-titiTESTDDO-totot-titiTOTO-PEL TOTOPOC TITI26923XXXXXXXXXX1960-02-03falsefalsefalse125db255bataillon test69150false69100false2000false125db255bataillon test69150azztestenfant-azz1994-03-11azztestenfant-azz2015-06-26azztestparent-azz1965-03-28autresenfantazzgigi-toto fifi2019-02-12autresenfantazzmimi-azz1994-02-03azzMonsieurazzMonsieurMister-toto Pelafcrtest Thdz25905XXXXXXXXXX1959-02-05false69009false1500Autre bailleur950566570truefalse6910021965-03-28falsefalsetruefalsefalsetestazzfalse2015-06-26falsefalsetruefalsefalseazz69150 DECINES CHARPIEUfalse
+--uuid:db03b44e-f563-4ce9-b06d-545faf9b26c0--
\ No newline at end of file
diff --git a/tests/data/sne/xmlmime.xsd b/tests/data/sne/xmlmime.xsd
new file mode 100644
index 00000000..766a07bd
--- /dev/null
+++ b/tests/data/sne/xmlmime.xsd
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/test_sne.py b/tests/test_sne.py
new file mode 100644
index 00000000..d6d5455b
--- /dev/null
+++ b/tests/test_sne.py
@@ -0,0 +1,97 @@
+import os
+
+import pytest
+import responses
+from django.contrib.contenttypes.models import ContentType
+from django.core.files import File
+
+from passerelle.apps.sne.models import SNE
+from passerelle.base.models import AccessRight, ApiUser
+
+
+@pytest.fixture()
+def connector(db):
+ with open('%s/tests/data/sne/cert.pem' % os.getcwd()) as f:
+ api = ApiUser.objects.create(username='all', keytype='', key='')
+ connector = SNE.objects.create(
+ wsdl_url='https://sne-ws-2.site-ecole.din.developpement-durable.gouv.invalid/services/DemandeLogementImplService/?wsdl',
+ slug='test',
+ client_certificate=File(f, 'cert.pem'),
+ certificate_name='CERG1318-202209062200.XXX',
+ )
+ obj_type = ContentType.objects.get_for_model(connector)
+ AccessRight.objects.create(
+ codename='can_access', apiuser=api, resource_type=obj_type, resource_pk=connector.pk
+ )
+ return connector
+
+
+def setup_(rsps, settings):
+ settings.CONNECTORS_SETTINGS = {
+ "sne/test": {
+ 'requests_substitutions': [
+ {
+ 'url': 'https://sne-ws-2.site-ecole.din.developpement-durable.gouv.invalid/',
+ 'search': 'http://sne2-ws.j2ee.eco.edcs.fr:80',
+ 'replace': 'https://sne-ws-2.site-ecole.din.developpement-durable.gouv.invalid',
+ }
+ ]
+ }
+ }
+ with open('%s/tests/data/sne/DemandeLogementImplService.wsdl' % os.getcwd(), 'rb') as f:
+ rsps.get(
+ 'https://sne-ws-2.site-ecole.din.developpement-durable.gouv.invalid/services/DemandeLogementImplService/?wsdl',
+ status=200,
+ body=f.read(),
+ )
+ with open('%s/tests/data/sne/DemandeLogementImplService1.wsdl' % os.getcwd(), 'rb') as f:
+ rsps.get(
+ 'https://sne-ws-2.site-ecole.din.developpement-durable.gouv.invalid/services/DemandeLogementImplService?wsdl=1',
+ status=200,
+ body=f.read(),
+ )
+ with open('%s/tests/data/sne/DemandeLogementImplService.xsd' % os.getcwd(), 'rb') as f:
+ rsps.get(
+ 'https://sne-ws-2.site-ecole.din.developpement-durable.gouv.invalid/services/DemandeLogementImplService?xsd=1',
+ status=200,
+ body=f.read(),
+ )
+ with open('%s/tests/data/sne/xmlmime.xsd' % os.getcwd(), 'rb') as f:
+ rsps.get('http://www.w3.org/2005/05/xmlmime', status=200, body=f.read())
+
+
+def test_get_demande_logement(app, connector, settings):
+ with responses.RequestsMock() as rsps:
+ setup_(rsps, settings)
+ with open('%s/tests/data/sne/response_ok' % os.getcwd(), 'rb') as f:
+ rsps.post(
+ 'https://sne-ws-2.site-ecole.din.developpement-durable.gouv.invalid/services/DemandeLogementImplService',
+ status=200,
+ body=f.read(),
+ content_type='multipart/related; start=""; type="application/xop+xml";'
+ ' boundary="uuid:db03b44e-f563-4ce9-b06d-545faf9b26c0"; start-info="application/soap+xml"',
+ )
+ resp = app.get('/sne/test/get-demande-logement?demand_id=0690221008931G3163')
+ json_resp = resp.json
+ assert json_resp['err'] == 0
+ assert json_resp['data']['interfaceNuu']['demande']['demandeLogement']['anru'] == 'false'
+
+
+def test_get_demande_logement_does_not_exist(app, connector, settings):
+ with responses.RequestsMock() as rsps:
+ setup_(rsps, settings)
+ with open('%s/tests/data/sne/response_does_not_exist' % os.getcwd(), 'rb') as f:
+ rsps.post(
+ 'https://sne-ws-2.site-ecole.din.developpement-durable.gouv.invalid/services/DemandeLogementImplService',
+ status=200,
+ body=f.read(),
+ content_type='multipart/related; start=""; type="application/xop+xml";'
+ ' boundary="uuid:7902e9bd-21a8-4632-8760-d79a67eb89a1"; start-info="application/soap+xml"',
+ )
+ resp = app.get('/sne/test/get-demande-logement?demand_id=0690221008931G3164')
+ json_resp = resp.json
+ assert json_resp['err'] == 1
+ assert (
+ json_resp['data']['soap_fault']['message']
+ == "La demande de logement n'existe pas dans le système."
+ )