193 lines
5.9 KiB
Python
193 lines
5.9 KiB
Python
# passerelle - uniform access to multiple data sources and services
|
|
# Copyright (C) 2021 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 <http://www.gnu.org/licenses/>.
|
|
|
|
import functools
|
|
import io
|
|
import zipfile
|
|
|
|
import httmock
|
|
import lxml.etree as ET
|
|
import pytest
|
|
import utils
|
|
from test_manager import login
|
|
|
|
from passerelle.contrib.toulouse_smart.models import ToulouseSmartResource
|
|
from passerelle.utils.xml import to_json
|
|
|
|
|
|
@pytest.fixture
|
|
def smart(db):
|
|
return utils.make_resource(
|
|
ToulouseSmartResource,
|
|
title='Test',
|
|
slug='test',
|
|
description='Test',
|
|
webservice_base_url='https://smart.example.com/',
|
|
basic_auth_username='username',
|
|
basic_auth_password='password',
|
|
)
|
|
|
|
|
|
def mock_response(*path_contents):
|
|
def decorator(func):
|
|
@httmock.urlmatch()
|
|
def error(url, request):
|
|
assert False, 'request to %s' % (url,)
|
|
|
|
@functools.wraps(func)
|
|
def wrapper(*args, **kwargs):
|
|
handlers = []
|
|
for row in path_contents:
|
|
path, content = row
|
|
|
|
@httmock.urlmatch(path=path)
|
|
def handler(url, request):
|
|
return content
|
|
|
|
handlers.append(handler)
|
|
handlers.append(error)
|
|
|
|
with httmock.HTTMock(*handlers):
|
|
return func(*args, **kwargs)
|
|
|
|
return wrapper
|
|
|
|
return decorator
|
|
|
|
|
|
@mock_response(['/v1/type-intervention', b'<List></List>'])
|
|
def test_empty_intervention_types(smart):
|
|
assert smart.get_intervention_types() == []
|
|
|
|
|
|
INTERVENTION_TYPES = '''<List>
|
|
<item>
|
|
<id>1234</id>
|
|
<name>coin</name>
|
|
<properties>
|
|
<properties>
|
|
<name>FIELD1</name>
|
|
<type>string</type>
|
|
<required>false</required>
|
|
<defaultValue>Ne sait pas</defaultValue>
|
|
<restrictedValues>
|
|
<restrictedValues>Candélabre</restrictedValues>
|
|
<restrictedValues>Mât</restrictedValues>
|
|
<restrictedValues>Ne sait pas</restrictedValues>
|
|
</restrictedValues>
|
|
</properties>
|
|
<properties>
|
|
<name>FIELD2</name>
|
|
<type>int</type>
|
|
<required>true</required>
|
|
</properties>
|
|
</properties>
|
|
</item>
|
|
</List>'''.encode()
|
|
|
|
|
|
@mock_response(['/v1/type-intervention', INTERVENTION_TYPES])
|
|
def test_model_intervention_types(smart):
|
|
assert smart.get_intervention_types() == [
|
|
{
|
|
'id': '1234',
|
|
'name': 'coin',
|
|
'order': 1,
|
|
'properties': [
|
|
{
|
|
'name': 'FIELD1',
|
|
'required': False,
|
|
'type': 'item',
|
|
'defaultValue': 'Ne sait pas',
|
|
'restrictedValues': ['Candélabre', 'Mât', 'Ne sait pas'],
|
|
},
|
|
{'name': 'FIELD2', 'required': True, 'type': 'int'},
|
|
],
|
|
},
|
|
]
|
|
|
|
|
|
URL = '/toulouse-smart/test/'
|
|
|
|
|
|
@mock_response(['/v1/type-intervention', INTERVENTION_TYPES])
|
|
def test_endpoint_intervention_types(app, smart):
|
|
resp = app.get(URL + 'type-intervention')
|
|
assert resp.json == {'data': [{'id': 'coin', 'text': 'coin', 'uuid': '1234'}], 'err': 0}
|
|
|
|
|
|
@mock_response()
|
|
def test_endpoint_intervention_types_unavailable(app, smart):
|
|
resp = app.get(URL + 'type-intervention')
|
|
assert resp.json == {'data': [{'id': '', 'text': 'Service is unavailable', 'disabled': True}], 'err': 0}
|
|
|
|
|
|
@mock_response(['/v1/type-intervention', INTERVENTION_TYPES])
|
|
def test_manage_intervention_types(app, smart, admin_user):
|
|
login(app)
|
|
resp = app.get('/manage' + URL + 'type-intervention/')
|
|
assert [[td.text for td in tr.cssselect('td,th')] for tr in resp.pyquery('tr')] == [
|
|
["Nom du type d'intervention", 'Nom', 'Type', 'Requis', 'Valeur par défaut'],
|
|
['1 - coin'],
|
|
[None, 'FIELD1', 'item («Candélabre», «Mât», «Ne sait pas»)', '✘', 'Ne sait pas'],
|
|
[None, 'FIELD2', 'int', '✔', None],
|
|
]
|
|
resp = resp.click('Export to blocks')
|
|
with zipfile.ZipFile(io.BytesIO(resp.body)) as zip_file:
|
|
assert zip_file.namelist() == ['block-coin.wcs']
|
|
with zip_file.open('block-coin.wcs') as fd:
|
|
content = ET.tostring(ET.fromstring(fd.read()), pretty_print=True).decode()
|
|
assert (
|
|
content
|
|
== '''<block id="1234">
|
|
<name>coin</name>
|
|
<slug>coin</slug>
|
|
<fields>
|
|
<field>
|
|
<id>038a8c2e-14de-4d4f-752f-496eb7fe90d7</id>
|
|
<label>FIELD1</label>
|
|
<type>item</type>
|
|
<required>False</required>
|
|
<varname>field1</varname>
|
|
<display_locations>
|
|
<display_location>validation</display_location>
|
|
<display_location>summary</display_location>
|
|
</display_locations>
|
|
<items>
|
|
<item>Candélabre</item>
|
|
<item>Mât</item>
|
|
<item>Ne sait pas</item>
|
|
</items>
|
|
</field>
|
|
<field>
|
|
<id>e72f251a-5eef-5b78-c35a-94b549510029</id>
|
|
<label>FIELD2</label>
|
|
<type>string</type>
|
|
<required>True</required>
|
|
<varname>field2</varname>
|
|
<display_locations>
|
|
<display_location>validation</display_location>
|
|
<display_location>summary</display_location>
|
|
</display_locations>
|
|
<validation>
|
|
<type>digits</type>
|
|
</validation>
|
|
</field>
|
|
</fields>
|
|
</block>
|
|
'''
|
|
)
|