passerelle/tests/test_maelis.py

555 lines
20 KiB
Python

import json
import os
from unittest import mock
import pytest
from django.test import override_settings
from django.utils.dateparse import parse_date
import tests.utils
from passerelle.apps.maelis.models import Link, Maelis
from passerelle.apps.maelis.utils import (
decompose_event,
get_school_year,
month_range,
week_boundaries_datetimes,
)
pytestmark = pytest.mark.django_db
TEST_BASE_DIR = os.path.join(os.path.dirname(__file__), 'data', 'maelis')
def get_xml_file(filename):
with open(os.path.join(TEST_BASE_DIR, filename), 'rb') as desc:
return desc.read()
def get_json_file(filename):
with open(os.path.join(TEST_BASE_DIR, "%s.json" % filename)) as fd:
return json.load(fd)
@pytest.fixture
def family_service_wsdl():
return get_xml_file('FamilyService.wsdl')
@pytest.fixture
def invoice_service_wsdl():
return get_xml_file('InvoiceService.wsdl')
@pytest.fixture
def activity_service_wsdl():
return get_xml_file('ActivityService.wsdl')
@pytest.fixture
def catalog_mocked_get(activity_service_wsdl, family_service_wsdl):
return (
tests.utils.FakedResponse(
content=family_service_wsdl, status_code=200, headers={'Content-Type': 'text/xml'}
),
tests.utils.FakedResponse(
content=activity_service_wsdl, status_code=200, headers={'Content-Type': 'text/xml'}
),
)
@pytest.fixture
def catalog_mocked_post():
return (
tests.utils.FakedResponse(
content=get_xml_file('readFamily.xml'), status_code=200, headers={'Content-Type': 'text/xml'}
),
tests.utils.FakedResponse(
content=get_xml_file('readActivityListResponse.xml'),
status_code=200,
headers={'Content-Type': 'text/xml'},
),
)
@pytest.fixture
def connector(db):
return tests.utils.setup_access_rights(
Maelis.objects.create(
slug='test', login='foo', password='bar', base_url='http://www3.sigec.fr/entrouvertws/services/'
)
)
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_link_unlink(mocked_post, mocked_get, family_service_wsdl, connector, app):
mocked_get.return_value = mock.Mock(content=family_service_wsdl)
mocked_post.return_value = mock.Mock(
content=get_xml_file('readFamilyByPasswordError.xml'),
status_code=500,
headers={'Content-Type': 'text/xml'},
)
assert Link.objects.count() == 0
resp = app.post_json(
'/maelis/test/link?NameID=local',
params={'family_id': '3264', 'password': 'wrong', 'school_year': '2020'},
)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'E204 : Le mot de passe est incorrect'
assert not resp.json['data']
assert Link.objects.count() == 0
resp = app.post_json('/maelis/test/unlink?NameID=local')
assert resp.json['err']
assert resp.json['err_desc'] == 'User not linked to family'
mocked_post.return_value = mock.Mock(
content=get_xml_file('readFamilyByPasswordResult.xml'),
status_code=200,
headers={'Content-Type': 'text/xml'},
)
resp = app.post_json('/maelis/test/link?NameID=local', params={'family_id': '3264', 'password': '123456'})
assert resp.json['err'] == 0
assert resp.json['data']
assert resp.json['data']['number'] == 3264
assert Link.objects.count() == 1
link = Link.objects.get()
assert link.name_id == 'local'
assert link.family_id == '3264'
resp = app.post_json('/maelis/test/unlink?NameID=local')
assert resp.json['err'] == 0
assert Link.objects.count() == 0
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_family_info(mocked_post, mocked_get, family_service_wsdl, connector, app):
mocked_get.return_value = mock.Mock(content=family_service_wsdl)
mocked_post.return_value = mock.Mock(
content=get_xml_file('readFamily.xml'), status_code=200, headers={'Content-Type': 'text/xml'}
)
assert Link.objects.count() == 0
Link.objects.create(resource=connector, family_id='3264', name_id='local')
resp = app.get('/maelis/test/family-info?NameID=local')
assert resp.json['data']["number"] == 3264
assert resp.json['data']['rl1InfoBean']
assert resp.json['data']['childInfoList']
resp = app.get('/maelis/test/children-info?NameID=local')
assert resp.json['data']
for child in resp.json['data']:
assert child['id']
assert child['text']
resp = app.get('/maelis/test/child-info?NameID=local&childID=21293')
assert resp.json['data']
assert resp.json['data']['num'] == '21293'
assert resp.json['data']['id'] == '21293'
resp = app.get('/maelis/test/adults-info?NameID=local')
assert resp.json['data']
for child in resp.json['data']:
assert child['id']
assert child['text']
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_activity_list(mocked_post, mocked_get, catalog_mocked_get, catalog_mocked_post, connector, app):
mocked_get.side_effect = catalog_mocked_get
mocked_post.side_effect = catalog_mocked_post
Link.objects.create(resource=connector, family_id='3264', name_id='local')
resp = app.get('/maelis/test/activity-list?NameID=local&personID=21293')
assert resp.json['data']
for activity in resp.json['data']:
assert activity['id']
assert activity['text']
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_update_coordinates(mocked_post, mocked_get, family_service_wsdl, connector, app):
mocked_get.return_value = mock.Mock(content=family_service_wsdl)
mocked_post.side_effect = (
tests.utils.FakedResponse(
content=get_xml_file('updateCoordinatesResponse.xml'),
status_code=200,
headers={'Content-Type': 'text/xml'},
),
tests.utils.FakedResponse(
content=get_xml_file('updateCoordinatesError.xml'),
status_code=200,
headers={'Content-Type': 'text/xml'},
),
)
Link.objects.create(resource=connector, family_id='3264', name_id='local')
resp = app.post_json(
'/maelis/test/update-coordinates?NameID=local&personID=21293', params={'mail': 'foo@example.com'}
)
assert resp.content is not None
resp = app.post_json(
'/maelis/test/update-coordinates?NameID=local&personID=21293', params={'town': 'Paris', 'num': '169'}
)
assert resp.json['err']
assert resp.json['err_desc'] == 'E16 : Le code postal est obligatoire'
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_list_invoices(mocked_post, mocked_get, invoice_service_wsdl, connector, app):
mocked_get.return_value = mock.Mock(content=invoice_service_wsdl)
mocked_post.return_value = mock.Mock(
content=get_xml_file('readInvoicesResponse.xml'),
status_code=200,
headers={'Content-Type': 'text/xml'},
)
Link.objects.create(resource=connector, family_id='3264', name_id='local')
resp = app.get('/maelis/test/regie/1/invoices?NameID=local')
assert resp.json['data']
for invoice in resp.json['data']:
assert invoice['display_id']
assert invoice['label']
assert invoice['total_amount']
assert not invoice['paid']
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_get_invoice_details(mocked_post, mocked_get, invoice_service_wsdl, connector, app):
mocked_get.return_value = mock.Mock(content=invoice_service_wsdl)
mocked_post.return_value = mock.Mock(
content=get_xml_file('readInvoicesResponse.xml'),
status_code=200,
headers={'Content-Type': 'text/xml'},
)
Link.objects.create(resource=connector, family_id='3264', name_id='local')
resp = app.get('/maelis/test/regie/1/invoice/3264-53186?NameID=local')
assert resp.json['data']
assert resp.json['data']['label'] == 'FACTURATION SEPTEMBRE 2014'
assert resp.json['data']['display_id'] == '53186'
@pytest.mark.parametrize(
'date, schoolyear',
[
('2020-07-30', 2019),
('2020-07-31', 2020),
],
)
def test_get_school_year(date, schoolyear):
date = parse_date(date)
assert schoolyear == get_school_year(date)
@pytest.mark.parametrize(
'date, monday, sunday',
[
('2020-12-27', '2020-12-21', '2020-12-27'),
('2020-12-28', '2020-12-28', '2021-01-03'),
('2020-12-31', '2020-12-28', '2021-01-03'),
('2021-01-01', '2020-12-28', '2021-01-03'),
('2021-01-03', '2020-12-28', '2021-01-03'),
],
)
def test_week_boundaries(date, monday, sunday):
start, end = week_boundaries_datetimes(date)
assert start.strftime('%Y-%m-%d') == monday
assert end.strftime('%Y-%m-%d') == sunday
@pytest.mark.parametrize(
'start, end, items',
[
('2020-10-22', '2020-12-06', ['2020-10-01', '2020-11-01', '2020-12-01']),
('2020-12-31', '2021-01-03', ['2020-12-01', '2021-01-01']),
('2020-12-28', '2020-12-28', ['2020-12-01']),
('2021-01-03', '2020-12-01', []),
],
)
def test_month_range(start, end, items):
start = parse_date(start)
end = parse_date(end)
assert [x.strftime('%Y-%m-%d') for x in month_range(start, end)] == items
def test_decompose_event():
resp = get_json_file('child_planning_before_decomposition')
data = resp['data']
assert len(data) == 43
assert data[22]['text'] == 'JOURNEE'
assert data[23]['text'] == 'MATIN'
assert data[24]['text'] == 'MATIN ET REPAS'
assert data[25]['text'] == 'APRES MIDI'
assert not [x for x in data if x['text'] == 'REPAS']
# unit is break down into its components
assert [x['text'] for x in decompose_event(data[22])] == ['Matinée', 'Repas', 'Après-midi']
assert [x['text'] for x in decompose_event(data[23])] == ['Matinée']
assert [x['text'] for x in decompose_event(data[24])] == ['Matinée', 'Repas']
assert [x['text'] for x in decompose_event(data[25])] == ['Après-midi']
# child_planning function use a dict to remove dupplicated components
data = {x['id']: x for e in data for x in decompose_event(e)}.values()
assert len(data) == 42
assert len([x for x in data if x['text'] == 'Repas']) == 1
assert len([x for x in data if 'EO0001' in x['slot_id']]) == 3
@override_settings(TIME_ZONE='Europe/Paris')
@pytest.mark.parametrize(
'legacy, nb_events, nb_booked, response',
[
('please', 43, 9, 'child_planning_before_decomposition'),
('', 42, 9, 'child_planning_after_decomposition'),
],
)
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_get_child_planning(
mocked_post,
mocked_get,
legacy,
nb_events,
nb_booked,
response,
family_service_wsdl,
activity_service_wsdl,
app,
connector,
):
mocked_get.side_effect = (
tests.utils.FakedResponse(
content=family_service_wsdl, status_code=200, headers={'Content-Type': 'text/xml'}
),
tests.utils.FakedResponse(
content=activity_service_wsdl, status_code=200, headers={'Content-Type': 'text/xml'}
),
tests.utils.FakedResponse(
content=activity_service_wsdl, status_code=200, headers={'Content-Type': 'text/xml'}
),
)
mocked_post.side_effect = (
tests.utils.FakedResponse(
content=get_xml_file('readFamily.xml'), status_code=200, headers={'Content-Type': 'text/xml'}
),
tests.utils.FakedResponse(
content=get_xml_file('child_planning_readActivityListResponse.xml'),
status_code=200,
headers={'Content-Type': 'text/xml'},
),
tests.utils.FakedResponse(
content=get_xml_file('child_planning_readChildMonthPlanningResponse.xml'),
status_code=200,
headers={'Content-Type': 'text/xml'},
),
)
Link.objects.create(resource=connector, family_id='3264', name_id='local')
url = '/maelis/test/child-planning?NameID=local&childID=21293&start_date=2020-12-19'
url += '&legacy=' + legacy
resp = app.get(url)
data = resp.json['data']
previous_event = {}
for event in data:
assert event['category']
assert event['text']
if previous_event:
assert previous_event['id'] <= event['id']
previous_event = event
assert len(data) == nb_events
assert len([s for s in data if s['user_booking_status'] == 'booked']) == nb_booked
assert resp.json == get_json_file(response)
@pytest.mark.parametrize(
'parameters, nb_subscribed, nb_not_subscribed',
[
('&subscribingStatus=', 2, 18),
('&subscribingStatus=subscribed', 2, 0),
('&subscribingStatus=not-subscribed', 0, 17),
('&subscribePublication=ELN', 2, 48),
],
)
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_child_activities(
mocked_post,
mocked_get,
parameters,
nb_subscribed,
nb_not_subscribed,
catalog_mocked_get,
catalog_mocked_post,
connector,
app,
):
mocked_get.side_effect = catalog_mocked_get
mocked_post.side_effect = catalog_mocked_post
Link.objects.create(resource=connector, family_id='3264', name_id='local')
url = '/maelis/test/child-activities?NameID=local&childID=21293&queryDate=2020-12-15'
url += parameters
resp = app.get(url)
if parameters == '&subscribingStatus=':
assert resp.json == get_json_file('child_activities')
status = [x['user_subscribing_status'] for x in resp.json['data']]
assert len([x for x in status if x == 'subscribed']) == nb_subscribed
assert len([x for x in status if x == 'not-subscribed']) == nb_not_subscribed
@pytest.mark.parametrize(
'parameters, err_desc',
[
('&childID=99999', 'Child not found'),
('&subscribingStatus=not-a-status', 'wrong value for subscribingStatus'),
("&queryDate=2020-02-31", 'not a valid date'),
("&queryDate=not-a-date", 'YYYY-MM-DD expected'),
],
)
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_child_activities_errors(
mocked_post, mocked_get, parameters, err_desc, catalog_mocked_get, catalog_mocked_post, connector, app
):
mocked_get.side_effect = catalog_mocked_get
mocked_post.side_effect = catalog_mocked_post
Link.objects.create(resource=connector, family_id='3264', name_id='local')
url = '/maelis/test/child-activities?NameID=local&childID=21293'
url += parameters
resp = app.get(url)
assert resp.json['err']
assert err_desc in resp.json['err_desc']
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_subscribe(mocked_post, mocked_get, family_service_wsdl, connector, app):
mocked_get.return_value = mock.Mock(content=family_service_wsdl)
mocked_post.side_effect = (
tests.utils.FakedResponse(
content=get_xml_file('readFamily.xml'), status_code=200, headers={'Content-Type': 'text/xml'}
),
tests.utils.FakedResponse(
content=get_xml_file('subscribeActivityResult.xml'),
status_code=200,
headers={'Content-Type': 'text/xml'},
),
)
Link.objects.create(resource=connector, family_id='3264', name_id='local')
resp = app.get(
'/maelis/test/subscribe/?NameID=local&childID=21293'
+ '&activityID=A10003123507&unitID=A10003123507&placeID=A10000000211'
+ '&weeklyPlanning=XX1XX11&start_date=2020-08-01&end_date=2021-07-31'
)
assert not resp.json['err']
assert resp.json['data']['state'] == {"idState": "1", "isWaitState": False, "libelle": "Confirmé"}
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_unsubscribe(mocked_post, mocked_get, family_service_wsdl, connector, app):
mocked_get.return_value = mock.Mock(content=family_service_wsdl)
mocked_post.side_effect = (
tests.utils.FakedResponse(
content=get_xml_file('readFamily.xml'), status_code=200, headers={'Content-Type': 'text/xml'}
),
tests.utils.FakedResponse(
content=get_xml_file('deletesubscribe.xml'), status_code=200, headers={'Content-Type': 'text/xml'}
),
)
Link.objects.create(resource=connector, family_id='3264', name_id='local')
resp = app.get(
'/maelis/test/unsubscribe/?NameID=local&childID=21293'
+ '&activityID=A10003121692&start_date=2020-08-01'
)
assert not resp.json['err']
assert resp.json['data'] is None
@pytest.mark.parametrize(
'parameters, nb_bus_lines',
[
('&direction=', 2),
('&direction=Aller', 1),
('&direction=retour', 1),
],
)
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_bus_lines(
mocked_post, mocked_get, parameters, nb_bus_lines, catalog_mocked_get, catalog_mocked_post, connector, app
):
mocked_get.side_effect = catalog_mocked_get
mocked_post.side_effect = catalog_mocked_post
Link.objects.create(resource=connector, family_id='3264', name_id='local')
url = '/maelis/test/bus-lines?NameID=local&childID=21293'
url += '&activityID=A10003131850&unitID=A10003131903&queryDate=2020-12-15'
url += parameters
resp = app.get(url)
if parameters == '&direction=':
assert resp.json == get_json_file('bus_lines')
assert len(resp.json['data']) == nb_bus_lines
@pytest.mark.parametrize(
'parameters, err_desc',
[
('&childID=99999', 'Child not found'),
('&queryDate=2020-02-31', 'not a valid date'),
('&queryDate=not-a-date', 'YYYY-MM-DD expected'),
('&direction=heaven', 'wrong value for direction'),
],
)
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_bus_lines_errors(
mocked_post, mocked_get, parameters, err_desc, catalog_mocked_get, catalog_mocked_post, connector, app
):
mocked_get.side_effect = catalog_mocked_get
mocked_post.side_effect = catalog_mocked_post
Link.objects.create(resource=connector, family_id='3264', name_id='local')
url = '/maelis/test/bus-lines?NameID=local&childID=21293&activityID=1&unitID=2'
url += parameters
resp = app.get(url)
assert resp.json['err']
assert err_desc in resp.json['err_desc']
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_bus_stops(mocked_post, mocked_get, catalog_mocked_get, catalog_mocked_post, connector, app):
mocked_get.side_effect = catalog_mocked_get
mocked_post.side_effect = catalog_mocked_post
Link.objects.create(resource=connector, family_id='3264', name_id='local')
url = '/maelis/test/bus-stops?NameID=local&childID=21293'
url += '&busActivityID=A10003151396&busUnitID=A10003151402&queryDate=2020-12-15'
resp = app.get(url)
assert resp.json == get_json_file('bus_stops')
assert len(resp.json['data']) == 6
@pytest.mark.parametrize(
'parameters, err_desc',
[
('&childID=99999', 'Child not found'),
('&queryDate=2020-02-31', 'not a valid date'),
('&queryDate=not-a-date', 'YYYY-MM-DD expected'),
('&busActivityID=1', 'Bus activity not found: 1'),
('&busUnitID=2', 'Bus unit not found: 2'),
],
)
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_bus_stops_errors(
mocked_post, mocked_get, parameters, err_desc, catalog_mocked_get, catalog_mocked_post, connector, app
):
mocked_get.side_effect = catalog_mocked_get
mocked_post.side_effect = catalog_mocked_post
Link.objects.create(resource=connector, family_id='3264', name_id='local')
url = '/maelis/test/bus-stops?NameID=local&childID=21293'
url += '&busActivityID=A10003151396&busUnitID=A10003151402'
url += parameters
resp = app.get(url)
assert resp.json['err']
assert err_desc in resp.json['err_desc']