Projet

Général

Profil

0001-signal_arretes-create-signal-arretes-connector-65822.patch

Corentin Séchet, 15 juin 2022 09:39

Télécharger (25,6 ko)

Voir les différences:

Subject: [PATCH] signal_arretes: create signal arretes connector (#65822)

 passerelle/apps/signal_arretes/__init__.py    |   0
 .../signal_arretes/migrations/0001_initial.py |  40 +++
 .../signal_arretes/migrations/__init__.py     |   0
 passerelle/apps/signal_arretes/models.py      | 266 ++++++++++++++++
 passerelle/settings.py                        |   1 +
 tests/test_signal_arretes.py                  | 301 ++++++++++++++++++
 6 files changed, 608 insertions(+)
 create mode 100644 passerelle/apps/signal_arretes/__init__.py
 create mode 100644 passerelle/apps/signal_arretes/migrations/0001_initial.py
 create mode 100644 passerelle/apps/signal_arretes/migrations/__init__.py
 create mode 100644 passerelle/apps/signal_arretes/models.py
 create mode 100644 tests/test_signal_arretes.py
passerelle/apps/signal_arretes/migrations/0001_initial.py
1
# Generated by Django 2.2.26 on 2022-06-13 14:13
2

  
3
from django.db import migrations, models
4

  
5

  
6
class Migration(migrations.Migration):
7

  
8
    initial = True
9

  
10
    dependencies = [
11
        ('base', '0029_auto_20210202_1627'),
12
    ]
13

  
14
    operations = [
15
        migrations.CreateModel(
16
            name='SignalArretes',
17
            fields=[
18
                (
19
                    'id',
20
                    models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
21
                ),
22
                ('title', models.CharField(max_length=50, verbose_name='Title')),
23
                ('slug', models.SlugField(unique=True, verbose_name='Identifier')),
24
                ('description', models.TextField(verbose_name='Description')),
25
                ('base_url', models.URLField(verbose_name='Base API URL')),
26
                (
27
                    'users',
28
                    models.ManyToManyField(
29
                        blank=True,
30
                        related_name='_signalarretes_users_+',
31
                        related_query_name='+',
32
                        to='base.ApiUser',
33
                    ),
34
                ),
35
            ],
36
            options={
37
                'verbose_name': 'Signal Arrêtés',
38
            },
39
        ),
40
    ]
passerelle/apps/signal_arretes/models.py
1
# -*- coding: utf-8 -*-
2
# Copyright (C) 2020 Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17

  
18
import binascii
19
import json
20
import re
21
from base64 import b64decode
22
from datetime import datetime
23

  
24
from django.db import models
25
from django.http import HttpResponse
26
from django.utils.text import slugify
27
from django.utils.translation import ugettext_lazy as _
28
from requests import RequestException
29

  
30
from passerelle.base.models import BaseResource
31
from passerelle.utils.api import endpoint
32
from passerelle.utils.jsonresponse import APIError
33

  
34
REQUEST_SCHEMA = {
35
    'type': 'object',
36
    '$schema': 'http://json-schema.org/draft-04/schema#',
37
    'title': 'Signal Arretes',
38
    'description': 'Public Occupation Request Schema',
39
    'required': [
40
        'declarant_organisation',
41
        'declarant_quality',
42
        'declarant_civility',
43
        'declarant_name',
44
        'declarant_surname',
45
        'declarant_email',
46
        'occupation_lane',
47
        'occupation_city',
48
        'occupation_type',
49
        'occupation_start_date',
50
        'occupation_end_date',
51
    ],
52
    'properties': {
53
        'declarant_organisation': {
54
            'description': _('"Individual" or enterprise name'),
55
            'type': 'string',
56
        },
57
        'declarant_siret': {
58
            'description': _('Entreprise SIRET number'),
59
            'type': ['string', 'null'],
60
            'pattern': '\\d{14}',
61
            'pattern_description': _('14-digits siret number'),
62
        },
63
        'declarant_quality': {
64
            'description': _('Declarant quality'),
65
            'type': 'string',
66
            'enum': ['Particulier', 'Entreprise', 'Association'],
67
        },
68
        'file_number': {'description': _('Declarant reference'), 'type': ['string', 'null']},
69
        'declarant_civility': {
70
            'description': _('Declarant civility'),
71
            'type': 'string',
72
            'enum': ['MONSIEUR', 'MADAME'],
73
        },
74
        'declarant_name': {'description': _('Declarant name'), 'type': 'string'},
75
        'declarant_surname': {'description': _('Declarant surname'), 'type': 'string'},
76
        'declarant_address': {'description': _('Declarant address'), 'type': ['string', 'null']},
77
        'declarant_zip': {'description': _('Declarant ZIP code'), 'type': ['string', 'null']},
78
        'declarant_city': {'description': _('Declarant city'), 'type': ['string', 'null']},
79
        'declarant_email': {'description': _('Declarant email address'), 'type': 'string'},
80
        'declarant_phone': {'description': _('Declarant phone number'), 'type': ['string', 'null']},
81
        'occupation_lane': {'description': _('Occupation lane'), 'type': 'string'},
82
        'occupation_number': {'description': _('Occupation lane number'), 'type': ['string', 'null']},
83
        'occupation_city': {'description': _('Occupation city'), 'type': 'string'},
84
        'occupation_type': {'description': _('Occupation type'), 'type': 'string'},
85
        'occupation_start_date': {
86
            'description': _('Occupation start date'),
87
            'type': 'string',
88
            'format': 'date',
89
        },
90
        'occupation_end_date': {'description': _('Occupation end date'), 'type': 'string', 'format': 'date'},
91
    },
92
}
93

  
94

  
95
class SignalArretes(BaseResource):
96
    base_url = models.URLField(_('Base API URL'))
97
    category = _('Business Process Connectors')
98

  
99
    class Meta:
100
        verbose_name = 'Signal Arrêtés'
101

  
102
    @classmethod
103
    def get_verbose_name(cls):
104
        return cls._meta.verbose_name
105

  
106
    @endpoint(
107
        description=_('Create a public domain occupation request'),
108
        perm='can_access',
109
        post={'request_body': {'schema': {'application/json': REQUEST_SCHEMA}}},
110
    )
111
    def create_request(self, request, post_data):
112
        def _format_date(date_string):
113
            return datetime.strptime(date_string, '%d/%m/%Y').strftime('%Y-%m-%d')
114

  
115
        query_data = {
116
            'organisationDeclarante': post_data['declarant_organisation'],
117
            'qualite': post_data['declarant_quality'],
118
            'contact': {
119
                'civilite': post_data['declarant_civility'],
120
                'nom': post_data['declarant_name'],
121
                'prenom': post_data['declarant_surname'],
122
                'email': post_data['declarant_email'],
123
            },
124
            'localisation': {
125
                'nomVoie': post_data['occupation_lane'],
126
                'commune': post_data['occupation_city'],
127
                'natureOccupation': post_data['occupation_type'],
128
                'dateDebut': _format_date(post_data['occupation_start_date']),
129
                'dateFin': _format_date(post_data['occupation_end_date']),
130
            },
131
        }
132

  
133
        def _update_data(key, target, target_key):
134
            if key in post_data and post_data[key]:
135
                target[target_key] = post_data[key]
136

  
137
        _update_data('declarant_siret', query_data, 'SIRET')
138
        _update_data('file_number', query_data, 'numeroDossier')
139

  
140
        _update_data('declarant_address', query_data['contact'], 'adresseLigne1')
141
        _update_data('declarant_zip', query_data['contact'], 'CP')
142
        _update_data('declarant_city', query_data['contact'], 'ville')
143
        _update_data('declarant_phone', query_data['contact'], 'telephone')
144
        _update_data('occupation_number', query_data['localisation'], 'numeroVoie')
145

  
146
        result_string = self._call('CreationDODP', query_data)
147
        if not isinstance(result_string, str):
148
            raise APIError('Expected a string')
149

  
150
        try:
151
            result = json.loads(result_string)
152
        except ValueError:
153
            raise APIError('Returned string should be valid json')
154

  
155
        if not isinstance(result, dict) or len(result) != 1:
156
            raise APIError('Expected a dictionary with one element')
157

  
158
        return {'request_id': list(result.keys())[0]}
159

  
160
    @endpoint(
161
        description=_('Get cities available in Signal Arrêtés'),
162
        perm='can_access',
163
    )
164
    def get_cities(self, request, **kwargs):
165
        return self._get_list('GetCommunes')
166

  
167
    @endpoint(
168
        description=_('Get lanes available in Signal Arrêtés'),
169
        perm='can_access',
170
        parameters={
171
            'city': {'description': _('Get lanes for this city')},
172
        },
173
    )
174
    def get_lanes(self, request, city):
175
        return self._get_list('GetVoies', {'Commune': city})
176

  
177
    @endpoint(
178
        description=_('Get available occupation types in Signal Arrêtés'),
179
        perm='can_access',
180
    )
181
    def get_occupation_types(self, request):
182
        return self._get_list('GetNaturesOccupation')
183

  
184
    @endpoint(
185
        description=_('Get status of given request in signal arrêtés'),
186
        perm='can_access',
187
        parameters={
188
            'request_id': {'description': _('The occupation request id returned by create_request')},
189
        },
190
    )
191
    def get_request_status(self, request, request_id):
192
        return {'request_status': self._get_value('GetStatutDemande', request_id=request_id)}
193

  
194
    @endpoint(
195
        description=_('Get document associated with given request in signal arrêtés'),
196
        perm='can_access',
197
        parameters={
198
            'request_id': {'description': _('The occupation request id returned by create_request')},
199
        },
200
    )
201
    def get_request_document(self, request, request_id):
202
        result = self._get_value('GetDocumentDemande', request_id=request_id)
203

  
204
        filename = result['name']
205
        content_type = result['contentType']
206

  
207
        try:
208
            content = b64decode(result['content'], validate=True)
209
        except binascii.Error as e:
210
            raise APIError(f'Corrupted base64 content {e}')
211

  
212
        response = HttpResponse(content, content_type=content_type)
213
        response['Content-Disposition'] = f'attachment; filename="{filename}"'
214

  
215
        return response
216

  
217
    def _call(self, endpoint, post_data=None):
218
        url = f'{self.base_url}/CreationDemandeService.svc/{endpoint}'
219
        if not post_data:
220
            response = self.requests.get(url)
221
        else:
222
            response = self.requests.post(url, json=post_data)
223

  
224
        try:
225
            response.raise_for_status()
226
        except RequestException as e:
227
            if response.status_code == 400:
228
                error_msg_match = re.search(
229
                    'Le message d\'exception est \'(.*)\'\\. Pour plus d\'informations', response.text
230
                )
231
                if error_msg_match:
232
                    error_message = error_msg_match.group(1)
233
                    raise APIError(
234
                        'An error occured during the request to Signal Arrêtés: %s' % error_message
235
                    )
236

  
237
            raise APIError('An error occured during the request to Signal Arrêtés: %s' % e)
238

  
239
        try:
240
            return response.json()
241
        except ValueError:
242
            raise APIError('Expected valid json')
243

  
244
    def _get_value(self, endpoint, post_data=None, request_id=None):
245
        if request_id:
246
            url = f"{endpoint}/{request_id}"
247
        else:
248
            url = endpoint
249

  
250
        response = self._call(url, post_data=post_data)
251
        result_key = f'{endpoint}Result'
252
        if not isinstance(response, dict) or result_key not in response:
253
            raise APIError('Expected a dictionary with a %s key' % result_key)
254

  
255
        result_str = response[result_key]
256
        try:
257
            return json.loads(result_str)
258
        except ValueError:
259
            raise APIError('Expected valid json string at %s key' % result_key)
260

  
261
    def _get_list(self, endpoint, post_data=None):
262
        result = self._get_value(endpoint, post_data=post_data)
263
        if not isinstance(result, list):
264
            raise APIError('Expected a list')
265

  
266
        return {'data': [{'id': slugify(it), 'text': it} for it in result]}
passerelle/settings.py
163 163
    'passerelle.apps.plone_restapi',
164 164
    'passerelle.apps.sector',
165 165
    'passerelle.apps.sfr_dmc',
166
    'passerelle.apps.signal_arretes',
166 167
    'passerelle.apps.soap',
167 168
    'passerelle.apps.solis',
168 169
    'passerelle.apps.sp_fr',
tests/test_signal_arretes.py
1
# passerelle - uniform access to multiple data sources and services
2
# Copyright (C) 2022  Entr'ouvert
3
#
4
# This program is free software: you can redistribute it and/or modify it
5
# under the terms of the GNU Affero General Public License as published
6
# by the Free Software Foundation, either version 3 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU Affero General Public License for more details.
13
#
14
# You should have received a copy of the GNU Affero General Public License
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16

  
17
import json
18
from base64 import standard_b64encode
19

  
20
import httmock
21
import pytest
22

  
23
from passerelle.apps.signal_arretes.models import SignalArretes
24
from tests.utils import generic_endpoint_url, setup_access_rights
25

  
26

  
27
@pytest.fixture()
28
def connector(db):
29
    return setup_access_rights(
30
        SignalArretes.objects.create(slug='test', base_url='http://test.signalarretes.net')
31
    )
32

  
33

  
34
REQUIRED_PARAMETERS = {
35
    'declarant_organisation': 'ACME',
36
    'declarant_siret': None,
37
    'declarant_quality': 'Entreprise',
38
    'file_number': None,
39
    'declarant_civility': 'MADAME',
40
    'declarant_name': 'John',
41
    'declarant_surname': 'Doe',
42
    'declarant_address': None,
43
    'declarant_zip': None,
44
    'declarant_city': None,
45
    'declarant_email': 'john@doe.net',
46
    'declarant_phone': None,
47
    'occupation_lane': 'Sesame Street',
48
    'occupation_number': '10',
49
    'occupation_city': 'Melun',
50
    'occupation_type': 'Base de vie',
51
    'occupation_start_date': '02/06/2022',
52
    'occupation_end_date': '03/06/2022',
53
}
54

  
55
ALL_PARAMETERS = dict(REQUIRED_PARAMETERS)
56
ALL_PARAMETERS.update(
57
    {
58
        'declarant_siret': '00000000000000',
59
        'file_number': 'reference_dossier',
60
        'declarant_address': '6 Proudhon street',
61
        'declarant_zip': '42 42420',
62
        'declarant_city': 'Melun',
63
        'declarant_phone': '0636656565',
64
        'occupation_lane': 'Sesame Street',
65
    }
66
)
67

  
68

  
69
def _check_required_parameters(data):
70
    assert data['organisationDeclarante'] == 'ACME'
71
    assert data['qualite'] == 'Entreprise'
72
    assert data['contact']['civilite'] == 'MADAME'
73
    assert data['contact']['nom'] == 'John'
74
    assert data['contact']['prenom'] == 'Doe'
75
    assert data['contact']['email'] == 'john@doe.net'
76
    assert data['localisation']['nomVoie'] == 'Sesame Street'
77
    assert data['localisation']['commune'] == 'Melun'
78
    assert data['localisation']['natureOccupation'] == 'Base de vie'
79
    assert data['localisation']['dateDebut'] == '2022-06-02'
80
    assert data['localisation']['dateFin'] == '2022-06-03'
81
    assert data['localisation']['numeroVoie'] == '10'
82

  
83

  
84
def test_create_request(app, connector):
85
    @httmock.all_requests
86
    def create_request_mock(url, request):
87
        assert request.path_url == '/CreationDemandeService.svc/CreationDODP'
88
        data = json.loads(request.body)
89

  
90
        _check_required_parameters(data)
91

  
92
        assert data['SIRET'] == '00000000000000'
93
        assert data['numeroDossier'] == 'reference_dossier'
94
        assert data['contact']['adresseLigne1'] == '6 Proudhon street'
95
        assert data['contact']['CP'] == '42 42420'
96
        assert data['contact']['ville'] == 'Melun'
97
        assert data['contact']['telephone'] == '0636656565'
98

  
99
        result_json = json.dumps({'D0000_DOT': 'Enregistré'})
100
        return httmock.response(200, json.dumps(result_json))
101

  
102
    endpoint = generic_endpoint_url('signal-arretes', 'create_request', slug=connector.slug)
103

  
104
    with httmock.HTTMock(create_request_mock):
105
        response = app.post_json(endpoint, params=ALL_PARAMETERS)
106

  
107
    result = response.json
108
    assert result['request_id'] == 'D0000_DOT'
109

  
110

  
111
def test_create_request_optional_parameters(app, connector):
112
    @httmock.all_requests
113
    def create_request_mock(url, request):
114
        data = json.loads(request.body)
115

  
116
        _check_required_parameters(data)
117

  
118
        assert 'SIRET' not in data
119
        assert 'numeroDossier' not in data
120
        assert 'adresseLigne1' not in data['contact']
121
        assert 'CP' not in data['contact']
122
        assert 'ville' not in data['contact']
123
        assert 'telephone' not in data['contact']
124

  
125
        result_json = json.dumps({'D0000_DOT': 'Enregistré'})
126
        return httmock.response(200, json.dumps(result_json))
127

  
128
    endpoint = generic_endpoint_url('signal-arretes', 'create_request', slug=connector.slug)
129

  
130
    with httmock.HTTMock(create_request_mock):
131
        response = app.post_json(
132
            endpoint,
133
            params=REQUIRED_PARAMETERS,
134
        )
135

  
136
    result = response.json
137
    assert result['request_id'] == 'D0000_DOT'
138

  
139

  
140
def test_create_request_errors(app, connector):
141
    def _test_error(response, error_message):
142
        @httmock.all_requests
143
        def bad_json_response(url, request):
144
            return httmock.response(200, response)
145

  
146
        endpoint = generic_endpoint_url('signal-arretes', 'create_request', slug=connector.slug)
147

  
148
        with httmock.HTTMock(bad_json_response):
149
            response = app.post_json(endpoint, params=REQUIRED_PARAMETERS)
150

  
151
        json_response = response.json
152
        assert 'err' in json_response
153
        assert json_response['err']
154
        assert error_message in json_response['err_desc']
155

  
156
    _test_error('Not valid json', 'Expected valid json')
157
    _test_error('[]', 'Expected a string')
158
    _test_error('"Invalid json"', 'Returned string should be valid json')
159
    _test_error('"[]"', 'Expected a dictionary with one element')
160
    _test_error('"{}"', 'Expected a dictionary with one element')
161

  
162

  
163
def test_get_cities(app, connector):
164
    @httmock.all_requests
165
    def get_cities_mock(url, request):
166
        assert request.path_url == '/CreationDemandeService.svc/GetCommunes'
167
        result_json = json.dumps(["Clapotis Les Canards", "Grosboule Les Bains"])
168
        return httmock.response(200, json.dumps({'GetCommunesResult': result_json}))
169

  
170
    endpoint = generic_endpoint_url('signal-arretes', 'get_cities', slug=connector.slug)
171

  
172
    with httmock.HTTMock(get_cities_mock):
173
        response = app.get(endpoint)
174

  
175
    assert response.json['data'] == [
176
        {'id': 'clapotis-les-canards', 'text': 'Clapotis Les Canards'},
177
        {'id': 'grosboule-les-bains', 'text': 'Grosboule Les Bains'},
178
    ]
179

  
180

  
181
def test_get_lanes(app, connector):
182
    @httmock.all_requests
183
    def get_lanes_mock(url, request):
184
        assert request.path_url == '/CreationDemandeService.svc/GetVoies'
185
        data = json.loads(request.body)
186
        assert data == {'Commune': 'Clapotis Les Canards'}
187
        result_json = json.dumps(["Rue Sacco", "Rue Vanzetti"])
188
        return httmock.response(200, json.dumps({'GetVoiesResult': result_json}))
189

  
190
    endpoint = generic_endpoint_url('signal-arretes', 'get_lanes', slug=connector.slug)
191

  
192
    with httmock.HTTMock(get_lanes_mock):
193
        response = app.get(endpoint, params={'city': 'Clapotis Les Canards'})
194

  
195
    assert response.json['data'] == [
196
        {'id': 'rue-sacco', 'text': 'Rue Sacco'},
197
        {'id': 'rue-vanzetti', 'text': 'Rue Vanzetti'},
198
    ]
199

  
200

  
201
def test_get_occupation_types(app, connector):
202
    @httmock.all_requests
203
    def get_occupation_types_mock(url, request):
204
        assert request.path_url == '/CreationDemandeService.svc/GetNaturesOccupation'
205
        result_json = json.dumps(["Déménagement", "Festival"])
206
        return httmock.response(200, json.dumps({'GetNaturesOccupationResult': result_json}))
207

  
208
    endpoint = generic_endpoint_url('signal-arretes', 'get_occupation_types', slug=connector.slug)
209

  
210
    with httmock.HTTMock(get_occupation_types_mock):
211
        response = app.get(endpoint)
212

  
213
    assert response.json['data'] == [
214
        {'id': 'demenagement', 'text': 'Déménagement'},
215
        {'id': 'festival', 'text': 'Festival'},
216
    ]
217

  
218

  
219
def test_get_request_status(app, connector):
220
    @httmock.all_requests
221
    def get_occupation_types_mock(url, request):
222
        assert request.path_url == '/CreationDemandeService.svc/GetStatutDemande/test_request_id'
223
        result_json = json.dumps("Enregistré")
224
        return httmock.response(200, json.dumps({'GetStatutDemandeResult': result_json}))
225

  
226
    endpoint = generic_endpoint_url('signal-arretes', 'get_request_status', slug=connector.slug)
227

  
228
    with httmock.HTTMock(get_occupation_types_mock):
229
        response = app.get(endpoint, params={'request_id': 'test_request_id'})
230

  
231
    assert not response.json['err']
232
    assert response.json['request_status'] == 'Enregistré'
233

  
234

  
235
def test_get_request_document(app, connector):
236
    test_content = 'Test file content'.encode('utf-8')
237

  
238
    @httmock.all_requests
239
    def get_document_mock(url, request):
240
        assert request.path_url == '/CreationDemandeService.svc/GetDocumentDemande/test_request_id'
241
        content = standard_b64encode(test_content).decode('utf-8')
242
        result_json = json.dumps(
243
            {'contentType': 'text/test-data', 'name': 'test_filename', 'content': content}
244
        )
245
        return httmock.response(200, json.dumps({'GetDocumentDemandeResult': result_json}))
246

  
247
    endpoint = generic_endpoint_url('signal-arretes', 'get_request_document', slug=connector.slug)
248

  
249
    with httmock.HTTMock(get_document_mock):
250
        response = app.get(endpoint, params={'request_id': 'test_request_id'})
251

  
252
    assert response.headers['Content-Type'] == 'text/test-data'
253
    assert response.headers['Content-Disposition'] == 'attachment; filename="test_filename"'
254
    assert response.body == test_content
255

  
256
    @httmock.all_requests
257
    def corrupted_content_mock(url, request):
258
        result_json = json.dumps(
259
            {'contentType': 'text/test-data', 'name': 'test_filename', 'content': '$$$$$$$'}
260
        )
261
        return httmock.response(200, json.dumps({'GetDocumentDemandeResult': result_json}))
262

  
263
    with httmock.HTTMock(corrupted_content_mock):
264
        response = app.get(endpoint, params={'request_id': 'test_request_id'})
265

  
266
    json_response = response.json
267

  
268
    assert 'err' in json_response
269
    assert json_response['err']
270
    assert 'Corrupted base64 content' in json_response['err_desc']
271

  
272

  
273
def test_error_handling(app, connector):
274
    def _test_error(status_code, body, expected_message):
275
        @httmock.all_requests
276
        def error_mock(url, request):
277
            return httmock.response(status_code, body)
278

  
279
        endpoint = generic_endpoint_url('signal-arretes', 'get_cities', slug=connector.slug)
280
        with httmock.HTTMock(error_mock):
281
            response = app.get(endpoint)
282

  
283
        json_response = response.json
284
        assert 'err' in json_response
285
        assert json_response['err']
286
        assert expected_message in json_response['err_desc']
287

  
288
    _test_error(
289
        400,
290
        'Le message d\'exception est \'Test error message\'. Pour plus d\'informations thanks a lot to use HTML as return of a json api.',
291
        'An error occured during the request to Signal Arrêtés: Test error message',
292
    )
293

  
294
    _test_error(500, 'Unmatched message', 'An error occured during the request to Signal Arrêtés')
295
    _test_error(200, 'Invalid json', 'Expected valid json')
296
    _test_error(200, '[]', 'Expected a dictionary with a GetCommunesResult key')
297
    _test_error(200, '{}', 'Expected a dictionary with a GetCommunesResult key')
298
    _test_error(
299
        200, '{"GetCommunesResult": "Invalid json"}', 'Expected valid json string at GetCommunesResult key'
300
    )
301
    _test_error(200, '{"GetCommunesResult": "{}"}', 'Expected a list')
0
-