0001-esirius-add-e-sirius-connector-51365.patch
passerelle/apps/esirius/migrations/0001_initial.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
# Generated by Django 1.11.18 on 2021-02-26 14:14 |
|
3 |
from __future__ import unicode_literals |
|
4 | ||
5 |
from django.db import migrations, models |
|
6 | ||
7 | ||
8 |
class Migration(migrations.Migration): |
|
9 | ||
10 |
initial = True |
|
11 | ||
12 |
dependencies = [ |
|
13 |
('base', '0029_auto_20210202_1627'), |
|
14 |
] |
|
15 | ||
16 |
operations = [ |
|
17 |
migrations.CreateModel( |
|
18 |
name='ESirius', |
|
19 |
fields=[ |
|
20 |
( |
|
21 |
'id', |
|
22 |
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), |
|
23 |
), |
|
24 |
('title', models.CharField(max_length=50, verbose_name='Title')), |
|
25 |
('slug', models.SlugField(unique=True, verbose_name='Identifier')), |
|
26 |
('description', models.TextField(verbose_name='Description')), |
|
27 |
( |
|
28 |
'basic_auth_username', |
|
29 |
models.CharField( |
|
30 |
blank=True, max_length=128, verbose_name='Basic authentication username' |
|
31 |
), |
|
32 |
), |
|
33 |
( |
|
34 |
'basic_auth_password', |
|
35 |
models.CharField( |
|
36 |
blank=True, max_length=128, verbose_name='Basic authentication password' |
|
37 |
), |
|
38 |
), |
|
39 |
( |
|
40 |
'client_certificate', |
|
41 |
models.FileField( |
|
42 |
blank=True, null=True, upload_to='', verbose_name='TLS client certificate' |
|
43 |
), |
|
44 |
), |
|
45 |
( |
|
46 |
'trusted_certificate_authorities', |
|
47 |
models.FileField(blank=True, null=True, upload_to='', verbose_name='TLS trusted CAs'), |
|
48 |
), |
|
49 |
('verify_cert', models.BooleanField(default=True, verbose_name='TLS verify certificates')), |
|
50 |
( |
|
51 |
'http_proxy', |
|
52 |
models.CharField(blank=True, max_length=128, verbose_name='HTTP and HTTPS proxy'), |
|
53 |
), |
|
54 |
( |
|
55 |
'secret_id', |
|
56 |
models.CharField(blank=True, max_length=128, verbose_name='Application identifier'), |
|
57 |
), |
|
58 |
('secret_key', models.CharField(blank=True, max_length=128, verbose_name='Secret Key')), |
|
59 |
( |
|
60 |
'base_url', |
|
61 |
models.CharField( |
|
62 |
help_text='example: https://HOST/ePlanning/webservices/api/', |
|
63 |
max_length=256, |
|
64 |
verbose_name='ePlanning webservices URL', |
|
65 |
), |
|
66 |
), |
|
67 |
( |
|
68 |
'users', |
|
69 |
models.ManyToManyField( |
|
70 |
blank=True, related_name='_esirius_users_+', related_query_name='+', to='base.ApiUser' |
|
71 |
), |
|
72 |
), |
|
73 |
], |
|
74 |
options={ |
|
75 |
'verbose_name': 'eSirius', |
|
76 |
}, |
|
77 |
), |
|
78 |
] |
passerelle/apps/esirius/models.py | ||
---|---|---|
1 |
# passerelle - uniform access to multiple data sources and services |
|
2 |
# Copyright (C) 2021 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 base64 |
|
19 |
from datetime import datetime |
|
20 |
from urllib.parse import urljoin |
|
21 | ||
22 |
from Cryptodome.Cipher import DES |
|
23 |
from Cryptodome.Util.Padding import pad |
|
24 | ||
25 |
from django.db import models |
|
26 |
from django.utils.encoding import force_bytes |
|
27 |
from django.utils.translation import ugettext_lazy as _ |
|
28 | ||
29 |
from passerelle.base.models import BaseResource, HTTPResource |
|
30 |
from passerelle.utils.api import endpoint |
|
31 |
from passerelle.utils.jsonresponse import APIError |
|
32 | ||
33 | ||
34 |
CREATE_APPOINTMENT_SCHEMA = { |
|
35 |
'$schema': 'http://json-schema.org/draft-04/schema#', |
|
36 |
"type": "object", |
|
37 |
'properties': { |
|
38 |
'idSys': {'type': 'string', 'pattern': '^[0-9]*$'}, |
|
39 |
'CodeRDV': {'type': 'string'}, |
|
40 |
'beginDate': {'type': 'string', 'pattern': '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'}, |
|
41 |
'beginTime': {'type': 'string', 'pattern': '^[0-9]{2}:[0-9]{2}$'}, |
|
42 |
'endDate': {'type': 'string', 'pattern': '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'}, |
|
43 |
'endTime': {'type': 'string', 'pattern': '^[0-9]{2}:[0-9]{2}$'}, |
|
44 |
'comment': {'type': 'string'}, |
|
45 |
'isoLanguage': {'description': 'ex: fr', 'type': 'string'}, |
|
46 |
'needsConfirmation': {'description': 'boolean expected', 'type': 'string'}, |
|
47 |
'rdvChannel': {'description': 'ex: EAPP0', 'type': 'string'}, |
|
48 |
'receptionChannel': {'type': 'string'}, |
|
49 |
'owner': {'type': 'object', 'properties': {'key': {'type': 'string'}, 'value': {'type': 'string'}}}, |
|
50 |
'user': { |
|
51 |
'type': 'object', |
|
52 |
'properties': { |
|
53 |
'idSys': {'type': 'string', 'pattern': '^[0-9]*$'}, |
|
54 |
'personalIdentity': {'type': 'string'}, |
|
55 |
'additionalPersonalIdentity': {"type": "array", "items": {'type': 'string'}}, |
|
56 |
'lastName': {'type': 'string'}, |
|
57 |
'civility': {'type': 'string'}, |
|
58 |
'firstName': {'type': 'string'}, |
|
59 |
'birthday': {'type': 'string'}, |
|
60 |
'email': {'type': 'string'}, |
|
61 |
'fixPhone': {'type': 'string'}, |
|
62 |
'phone': {'type': 'string'}, |
|
63 |
'address': { |
|
64 |
'type': 'object', |
|
65 |
'properties': { |
|
66 |
'line1': {'type': 'string'}, |
|
67 |
'line2': {'type': 'string'}, |
|
68 |
'zipCode': {'type': 'string'}, |
|
69 |
'city': {'type': 'string'}, |
|
70 |
'country': {'type': 'string'}, |
|
71 |
}, |
|
72 |
}, |
|
73 |
}, |
|
74 |
}, |
|
75 |
'serviceId': {'type': 'string'}, |
|
76 |
'siteCode': {'type': 'string'}, |
|
77 |
"resources": { |
|
78 |
'type': 'object', |
|
79 |
'properties': { |
|
80 |
'id': {'type': 'string', 'pattern': '^[0-9]*$'}, |
|
81 |
'key': {'type': 'string'}, |
|
82 |
'type': {'type': 'string'}, |
|
83 |
'name': {'type': 'string'}, |
|
84 |
'station': { |
|
85 |
'type': 'object', |
|
86 |
'properties': { |
|
87 |
'id': {'type': 'string', 'pattern': '^[0-9]*$'}, |
|
88 |
'key': {'type': 'string'}, |
|
89 |
'name': {'type': 'string'}, |
|
90 |
}, |
|
91 |
}, |
|
92 |
}, |
|
93 |
}, |
|
94 |
'motives': { |
|
95 |
"type": "array", |
|
96 |
"items": { |
|
97 |
'type': 'object', |
|
98 |
'properties': { |
|
99 |
'id': {'type': 'string', 'pattern': '^[0-9]*$'}, |
|
100 |
'name': {'type': 'string'}, |
|
101 |
'shortName': {'type': 'string'}, |
|
102 |
'processingTime': {'type': 'string', 'pattern': '^[0-9]*$'}, |
|
103 |
'externalModuleAccess': {'type': 'string', 'pattern': '^[0-9]*$'}, |
|
104 |
'quantity': {'type': 'string', 'pattern': '^[0-9]*$'}, |
|
105 |
'usePremotiveQuantity': {'description': 'boolean expected', 'type': 'string'}, |
|
106 |
}, |
|
107 |
}, |
|
108 |
}, |
|
109 |
}, |
|
110 |
'unflatten': True, |
|
111 |
} |
|
112 | ||
113 | ||
114 |
class ESirius(BaseResource, HTTPResource): |
|
115 |
secret_id = models.CharField(max_length=128, verbose_name=_('Application identifier'), blank=True) |
|
116 |
secret_key = models.CharField(max_length=128, verbose_name=_('Secret Key'), blank=True) |
|
117 |
base_url = models.CharField( |
|
118 |
max_length=256, |
|
119 |
blank=False, |
|
120 |
verbose_name=_('ePlanning webservices URL'), |
|
121 |
help_text=_('example: https://HOST/ePlanning/webservices/api/'), |
|
122 |
) |
|
123 | ||
124 |
category = _('Business Process Connectors') |
|
125 | ||
126 |
class Meta: |
|
127 |
verbose_name = _('eSirius') |
|
128 | ||
129 |
def request(self, uri, method='get', payload=None): |
|
130 |
url = urljoin(self.base_url, uri) |
|
131 | ||
132 |
des_key = pad(force_bytes(self.secret_key), 8)[:8] |
|
133 |
cipher = DES.new(des_key, DES.MODE_ECB) |
|
134 |
epoch = int(datetime.now().strftime('%s%f')[:-3]) |
|
135 |
plaintext = '{"caller":"%s","createInfo":%i}' % (self.secret_id, epoch) |
|
136 |
msg = cipher.encrypt(pad(force_bytes(plaintext), 8)) |
|
137 |
headers = { |
|
138 |
'Accept': 'application/json; charset=utf-8', |
|
139 |
'token_info_caller': base64.b64encode(msg), |
|
140 |
} |
|
141 | ||
142 |
if method == 'get': |
|
143 |
response = self.requests.get(url, headers=headers, params=payload) |
|
144 |
elif method == 'post': |
|
145 |
response = self.requests.post(url, headers=headers, json=payload) |
|
146 |
else: |
|
147 |
response = self.requests.delete(url, headers=headers) |
|
148 |
if response.status_code == 304: |
|
149 |
raise APIError('Booking not found') |
|
150 | ||
151 |
if response.status_code != 200: |
|
152 |
try: |
|
153 |
json_content = response.json() |
|
154 |
except ValueError: |
|
155 |
json_content = None |
|
156 |
raise APIError( |
|
157 |
'error status:%s %r, content:%r' |
|
158 |
% (response.status_code, response.reason, response.text[:1024]), |
|
159 |
data={'status_code': response.status_code, 'json_content': json_content}, |
|
160 |
) |
|
161 |
return response |
|
162 | ||
163 |
def check_status(self): |
|
164 |
""" |
|
165 |
Raise an exception if something goes wrong. |
|
166 |
""" |
|
167 |
self.request('sites/') |
|
168 | ||
169 |
@endpoint( |
|
170 |
display_category=_('Appointment'), |
|
171 |
name='create-appointment', |
|
172 |
perm='can_access', |
|
173 |
methods=['post'], |
|
174 |
description=_('Create appointment'), |
|
175 |
post={'request_body': {'schema': {'application/json': CREATE_APPOINTMENT_SCHEMA}}}, |
|
176 |
) |
|
177 |
def create_appointment(self, request, post_data): |
|
178 |
# address dict is required |
|
179 |
if not post_data.get('user'): |
|
180 |
post_data['user'] = {} |
|
181 |
if not post_data['user'].get('address'): |
|
182 |
post_data['user']['address'] = {} |
|
183 | ||
184 |
response = self.request('appointments/', 'post', payload=post_data) |
|
185 |
return {'data': {'booking_id': response.text}} |
|
186 | ||
187 |
@endpoint( |
|
188 |
display_category=_('Appointment'), |
|
189 |
name='get-appointment', |
|
190 |
perm='can_access', |
|
191 |
methods=['get'], |
|
192 |
description=_('Get appointment'), |
|
193 |
parameters={ |
|
194 |
'booking_id': {'description': _('id returned by create-appointment endpoint')}, |
|
195 |
}, |
|
196 |
) |
|
197 |
def get_appointment(self, request, booking_id): |
|
198 |
response = self.request('appointments/%s/' % booking_id) |
|
199 |
return {'data': response.json()} |
|
200 | ||
201 |
@endpoint( |
|
202 |
display_category=_('Appointment'), |
|
203 |
name='delete-appointment', |
|
204 |
perm='can_access', |
|
205 |
methods=['post'], |
|
206 |
description=_('Delete appointment'), |
|
207 |
parameters={ |
|
208 |
'booking_id': {'description': _('id returned by create-appointment endpoint')}, |
|
209 |
}, |
|
210 |
) |
|
211 |
def delete_appointment(self, request, booking_id): |
|
212 |
response = self.request('appointments/%s/' % booking_id, 'delete') |
|
213 |
return {'data': {}} |
passerelle/settings.py | ||
---|---|---|
131 | 131 |
'passerelle.apps.bdp', |
132 | 132 |
'passerelle.apps.cartads_cs', |
133 | 133 |
'passerelle.apps.choosit', |
134 | 134 |
'passerelle.apps.cityweb', |
135 | 135 |
'passerelle.apps.clicrdv', |
136 | 136 |
'passerelle.apps.cmis', |
137 | 137 |
'passerelle.apps.cryptor', |
138 | 138 |
'passerelle.apps.csvdatasource', |
139 |
'passerelle.apps.esirius', |
|
139 | 140 |
'passerelle.apps.family', |
140 | 141 |
'passerelle.apps.feeds', |
141 | 142 |
'passerelle.apps.gdc', |
142 | 143 |
'passerelle.apps.gesbac', |
143 | 144 |
'passerelle.apps.jsondatastore', |
144 | 145 |
'passerelle.apps.sp_fr', |
145 | 146 |
'passerelle.apps.maelis', |
146 | 147 |
'passerelle.apps.mdel', |
tests/test_esirius.py | ||
---|---|---|
1 |
# passerelle - uniform access to multiple data sources and services |
|
2 |
# Copyright (C) 2021 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 |
import httmock |
|
19 |
import pytest |
|
20 | ||
21 |
from passerelle.apps.esirius.models import ESirius |
|
22 |
from passerelle.utils.jsonresponse import APIError |
|
23 | ||
24 |
import utils |
|
25 | ||
26 | ||
27 |
CREATE_APPOINTMENT_PAYLOAD = { |
|
28 |
'beginDate': '2021-02-24', |
|
29 |
'beginTime': '16:40', |
|
30 |
'endDate': '2021-02-24', |
|
31 |
'endTime': '17:00', |
|
32 |
'comment': 'commentaire', |
|
33 |
'isoLanguage': 'fr', |
|
34 |
'needsConfirmation': 'False', |
|
35 |
'rdvChannel': 'WEBSERVICES', |
|
36 |
'receptionChannel': 'WS', |
|
37 |
'serviceId': '9', |
|
38 |
'siteCode': 'site1', |
|
39 |
'resources': { |
|
40 |
'id': '1', |
|
41 |
'key': '17', |
|
42 |
'type': 'STATION', |
|
43 |
}, |
|
44 |
} |
|
45 | ||
46 |
GET_APPOINTMENT_RESPONSE = ''' |
|
47 |
{ |
|
48 |
"beginDate" : "2021-02-26", |
|
49 |
"beginTime" : "16:40", |
|
50 |
"codeRDV" : "943A98", |
|
51 |
"comment" : "coucou", |
|
52 |
"endDate" : "2021-02-26", |
|
53 |
"endTime" : "17:00", |
|
54 |
"idSys" : 108840, |
|
55 |
"isoLanguage" : "fr", |
|
56 |
"motives" : [], |
|
57 |
"needsConfirmation" : false, |
|
58 |
"rdvChannel" : "EAPP0", |
|
59 |
"receptionChannel" : "WS", |
|
60 |
"resources" : { |
|
61 |
"id" : 29, |
|
62 |
"key" : "46", |
|
63 |
"name" : "C1", |
|
64 |
"type" : "STATION" |
|
65 |
}, |
|
66 |
"serviceId" : "39", |
|
67 |
"siteCode" : "site1", |
|
68 |
"siteIdSys" : 5, |
|
69 |
"user" : { |
|
70 |
"additionalPersonalIdentity" : [], |
|
71 |
"address" : {}, |
|
72 |
"civility" : "", |
|
73 |
"idSys" : "95897" |
|
74 |
} |
|
75 |
} |
|
76 |
''' |
|
77 | ||
78 | ||
79 |
@pytest.fixture |
|
80 |
def connector(db): |
|
81 |
return utils.setup_access_rights( |
|
82 |
ESirius.objects.create( |
|
83 |
slug='test', secret_id='xxx', secret_key='yyy', base_url='https://dummy-server.org' |
|
84 |
) |
|
85 |
) |
|
86 | ||
87 | ||
88 |
def get_endpoint(name): |
|
89 |
return utils.generic_endpoint_url('esirius', name) |
|
90 | ||
91 | ||
92 |
@pytest.mark.freeze_time('2021-01-26 15:13:6.880') |
|
93 |
def test_token(connector): |
|
94 |
connector.secret_id = 'eAppointment' |
|
95 |
connector.secret_key = 'ES2I Info Caller Http Encryption Key' |
|
96 |
connector.save() |
|
97 | ||
98 |
@httmock.all_requests |
|
99 |
def sigerly_mock(url, request): |
|
100 |
assert ( |
|
101 |
request.headers['token_info_caller'] |
|
102 |
== b'yM4zYAxT67Qvjd20riG3j0eu0t0Ku+HLlttj17Gul7zkruFaXX1J8BJ6sV2Ldgw40axfWh+ESAY=' |
|
103 |
) |
|
104 |
return httmock.response(200) |
|
105 | ||
106 |
with httmock.HTTMock(sigerly_mock): |
|
107 |
connector.request('an/uri/', payload="somes") |
|
108 | ||
109 | ||
110 |
def test_pre_request(connector): |
|
111 |
@httmock.urlmatch(netloc='dummy-server.org', path='/an/uri/', method='GET') |
|
112 |
def sigerly_mock(url, request): |
|
113 |
assert request.headers['Accept'] == 'application/json; charset=utf-8' |
|
114 |
assert request.headers['token_info_caller'][:42] == b'f3G6sjRZETBam6vcdrAxmvJQTX5hh6OjZ8XlUO6SMo' |
|
115 |
return httmock.response(200) |
|
116 | ||
117 |
with httmock.HTTMock(sigerly_mock): |
|
118 |
connector.request('an/uri/', payload="somes") |
|
119 | ||
120 | ||
121 |
@pytest.mark.parametrize( |
|
122 |
'status_code, content, a_dict', |
|
123 |
[ |
|
124 |
(400, '{"message": "help"}', {'message': 'help'}), |
|
125 |
(500, 'not json', None), |
|
126 |
], |
|
127 |
) |
|
128 |
def test_post_request(connector, status_code, content, a_dict): |
|
129 |
@httmock.urlmatch(netloc='dummy-server.org', path='/an/uri/', method='GET') |
|
130 |
def sigerly_mock(url, request): |
|
131 |
return httmock.response(status_code, content) |
|
132 | ||
133 |
with pytest.raises(APIError) as exc: |
|
134 |
with httmock.HTTMock(sigerly_mock): |
|
135 |
connector.request('an/uri/', payload="somes") |
|
136 | ||
137 |
assert exc.value.err |
|
138 |
assert exc.value.data['status_code'] == status_code |
|
139 |
assert exc.value.data['json_content'] == a_dict |
|
140 | ||
141 | ||
142 |
@pytest.mark.parametrize( |
|
143 |
'status_code, content, is_up', |
|
144 |
[ |
|
145 |
(200, 'wathever', True), |
|
146 |
(500, '{"message": "help"}', False), |
|
147 |
], |
|
148 |
) |
|
149 |
def test_check_status(app, connector, status_code, content, is_up): |
|
150 |
@httmock.all_requests |
|
151 |
def sigerly_mock(url, request): |
|
152 |
return httmock.response(status_code, content) |
|
153 | ||
154 |
if is_up: |
|
155 |
with httmock.HTTMock(sigerly_mock): |
|
156 |
connector.check_status() |
|
157 |
else: |
|
158 |
with pytest.raises(APIError): |
|
159 |
with httmock.HTTMock(sigerly_mock): |
|
160 |
connector.check_status() |
|
161 | ||
162 | ||
163 |
def test_create_appointment(app, connector): |
|
164 |
endpoint = get_endpoint('create-appointment') |
|
165 | ||
166 |
@httmock.urlmatch(netloc='dummy-server.org', path='/appointments/', method='POST') |
|
167 |
def sigerly_mock(url, request): |
|
168 |
return httmock.response(200, b'94PEP4') |
|
169 | ||
170 |
with httmock.HTTMock(sigerly_mock): |
|
171 |
resp = app.post_json(endpoint, params=CREATE_APPOINTMENT_PAYLOAD) |
|
172 | ||
173 |
assert not resp.json['err'] |
|
174 |
assert resp.json['data'] == {'booking_id': '94PEP4'} |
|
175 | ||
176 | ||
177 |
def test_create_appointment_error_404(app, connector): |
|
178 |
endpoint = get_endpoint('create-appointment') |
|
179 | ||
180 |
# payload not providing or probiding an unconfigured serviceId |
|
181 |
payload = CREATE_APPOINTMENT_PAYLOAD |
|
182 |
del payload['serviceId'] |
|
183 | ||
184 |
@httmock.urlmatch(netloc='dummy-server.org', path='/appointments/', method='POST') |
|
185 |
def sigerly_mock(url, request): |
|
186 |
return httmock.response( |
|
187 |
404, |
|
188 |
{ |
|
189 |
'code': 'Not Found', |
|
190 |
'type': 'com.es2i.planning.api.exception.NoService4RDVException', |
|
191 |
'message': "Le rendez-vous {0} n'a pas créé", |
|
192 |
}, |
|
193 |
) |
|
194 | ||
195 |
with httmock.HTTMock(sigerly_mock): |
|
196 |
resp = app.post_json(endpoint, params=payload) |
|
197 | ||
198 |
assert resp.json['err'] |
|
199 |
assert resp.json['data']['status_code'] == 404 |
|
200 |
assert resp.json['data']['json_content'] == { |
|
201 |
'code': 'Not Found', |
|
202 |
'type': 'com.es2i.planning.api.exception.NoService4RDVException', |
|
203 |
'message': "Le rendez-vous {0} n'a pas créé", |
|
204 |
} |
|
205 | ||
206 | ||
207 |
def test_create_appointment_error_500(app, connector): |
|
208 |
endpoint = get_endpoint('create-appointment') |
|
209 | ||
210 |
# payload not providing beginTime |
|
211 |
payload = {'beginDate': '2021-02-23'} |
|
212 | ||
213 |
@httmock.urlmatch(netloc='dummy-server.org', path='/appointments/', method='POST') |
|
214 |
def sigerly_mock(url, request): |
|
215 |
return httmock.response(500, 'java stack') |
|
216 | ||
217 |
with httmock.HTTMock(sigerly_mock): |
|
218 |
resp = app.post_json(endpoint, params=payload) |
|
219 | ||
220 |
assert resp.json['err'] |
|
221 |
assert resp.json['err_class'] == 'passerelle.utils.jsonresponse.APIError' |
|
222 |
assert resp.json['err_desc'] == "error status:500 None, content:'java stack'" |
|
223 |
assert resp.json['data']['status_code'] == 500 |
|
224 |
assert resp.json['data']['json_content'] is None |
|
225 | ||
226 | ||
227 |
def test_get_appointment(app, connector): |
|
228 |
endpoint = get_endpoint('get-appointment') |
|
229 | ||
230 |
@httmock.urlmatch(netloc='dummy-server.org', path='/appointments/', method='GET') |
|
231 |
def sigerly_mock(url, request): |
|
232 |
return httmock.response(200, GET_APPOINTMENT_RESPONSE) |
|
233 | ||
234 |
with httmock.HTTMock(sigerly_mock): |
|
235 |
resp = app.get(endpoint + '?booking_id=94PEP4') |
|
236 | ||
237 |
assert not resp.json['err'] |
|
238 |
assert resp.json['data']['codeRDV'] |
|
239 |
assert resp.json['data'] == json.loads(GET_APPOINTMENT_RESPONSE) |
|
240 | ||
241 | ||
242 |
def test_get_appointment_error(app, connector): |
|
243 |
endpoint = get_endpoint('get-appointment') |
|
244 | ||
245 |
@httmock.urlmatch(netloc='dummy-server.org', path='/appointments/', method='GET') |
|
246 |
def sigerly_mock(url, request): |
|
247 |
return httmock.response(404, '{"code":"Not Found","message":"Le rendez-vous {0} n\'existe pas"}') |
|
248 | ||
249 |
with httmock.HTTMock(sigerly_mock): |
|
250 |
resp = app.get(endpoint + '?booking_id=94PEP4') |
|
251 | ||
252 |
assert resp.json['err'] |
|
253 |
assert resp.json['err_class'] == 'passerelle.utils.jsonresponse.APIError' |
|
254 |
assert resp.json['data']['status_code'] == 404 |
|
255 |
assert resp.json['data']['json_content'] == { |
|
256 |
"code": "Not Found", |
|
257 |
"message": "Le rendez-vous {0} n'existe pas", |
|
258 |
} |
|
259 | ||
260 | ||
261 |
def test_delete_appointment(app, connector): |
|
262 |
endpoint = get_endpoint('delete-appointment') |
|
263 | ||
264 |
@httmock.urlmatch(netloc='dummy-server.org', path='/appointments/', method='DELETE') |
|
265 |
def sigerly_mock(url, request): |
|
266 |
return httmock.response(200, b'') |
|
267 | ||
268 |
with httmock.HTTMock(sigerly_mock): |
|
269 |
resp = app.post_json(endpoint + '?booking_id=94PEP4') |
|
270 | ||
271 |
assert not resp.json['err'] |
|
272 |
assert resp.json['data'] == {} |
|
273 | ||
274 | ||
275 |
def test_delete_appointment_error(app, connector): |
|
276 |
endpoint = get_endpoint('delete-appointment') |
|
277 | ||
278 |
@httmock.urlmatch(netloc='dummy-server.org', path='/appointments/', method='DELETE') |
|
279 |
def sigerly_mock(url, request): |
|
280 |
return httmock.response(304, b'') |
|
281 | ||
282 |
with httmock.HTTMock(sigerly_mock): |
|
283 |
resp = app.post_json(endpoint + '?booking_id=94PEP4') |
|
284 | ||
285 |
assert resp.json['err'] |
|
286 |
assert resp.json['err_desc'] == 'Booking not found' |
|
0 |
- |