# passerelle - uniform access to multiple data sources and services
# Copyright (C) 2020 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 copy
import datetime
import decimal
import json
import os
import xml.etree.ElementTree as ET
from contextlib import contextmanager
from unittest import mock

import freezegun
import jsonschema
import pytest
import xmlschema
from django.core.cache import cache

import tests.utils
from passerelle.contrib.toulouse_axel import schemas
from passerelle.contrib.toulouse_axel.models import Link, Lock, ToulouseAxel
from passerelle.contrib.toulouse_axel.utils import (
    csp_mapping,
    lien_parente_mapping,
    regime_mapping,
    situation_familiale_mapping,
    type_regime_mapping,
    upperize,
)
from passerelle.contrib.utils.axel import AxelError, OperationResult
from passerelle.utils.jsonresponse import APIError
from passerelle.utils.soap import SOAPError


@pytest.fixture
def resource(db):
    return tests.utils.make_resource(
        ToulouseAxel, slug='test', wsdl_url='http://example.net/AXEL_WS/AxelWS.php?wsdl'
    )


@pytest.fixture
def link_params():
    return {
        'IDDUI': 'XXX',
        'PRENOM': 'John',
        'NOM': 'Doe',
        'NAISSANCE': '2010-10-10',
    }


@pytest.fixture
def update_params():
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/update_family_info.json')
    with open(filepath) as jsonfile:
        content = jsonfile.read()
    return json.loads(content)


@pytest.fixture
def family_data():
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/family_info.xml')
    with open(filepath) as xml:
        content = xml.read()
    resp = (
        '''
    <?xml version="1.0"?>
    <PORTAILSERVICE>
      <RESULTAT>
        <TYPE>RefFamilleDui</TYPE>
        <STATUS>OK</STATUS>
        <DATE>10/10/2010 10:10:01</DATE>
        <COMMENTAIRES><![CDATA[]]></COMMENTAIRES>
      </RESULTAT>
      <DATA>
        %s
      </DATA>
    </PORTAILSERVICE>
    '''.strip()
        % content
    )
    return schemas.ref_famille_dui.response_converter.decode(ET.fromstring(resp))['DATA']['PORTAIL']['DUI']


@pytest.fixture
def child_activities_data():
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
    with open(filepath) as xml:
        content = xml.read()
    resp = (
        '''
    <?xml version="1.0"?>
    <PORTAILSERVICE>
      <RESULTAT>
        <TYPE>EnfantsActivites</TYPE>
        <STATUS>OK</STATUS>
        <DATE>10/10/2010 10:10:01</DATE>
        <COMMENTAIRES><![CDATA[]]></COMMENTAIRES>
      </RESULTAT>
      <DATA>
        %s
      </DATA>
    </PORTAILSERVICE>
    '''.strip()
        % content
    )
    return schemas.enfants_activites.response_converter.decode(ET.fromstring(resp))['DATA']['PORTAIL']['DUI']


@pytest.fixture
def flat_update_params():
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/flat_update_family_info.json')
    with open(filepath) as jsonfile:
        content = jsonfile.read()
    return json.loads(content)


@pytest.fixture
def booking_params():
    return {
        'booking_start_date': '2020-04-13',
        'booking_end_date': '2020-04-17',
        'booking_list_MAT': [
            '3535:MAT:A19P1M1:2020-04-13',
            '3535:MAT:A19P1M1:2020-04-14',
            '3535:MAT:A19P1M1:2020-04-16',
            '3535:MAT:A19P1M1:2020-04-17',
        ],
        'booking_list_MIDI': [
            '3535:MIDI:A19P1M2:2020-04-13',
            '3535:MIDI:A19P1M2:2020-04-14',
            '3535:MIDI:A19P1M2:2020-04-16',
            '3535:MIDI:A19P1M2:2020-04-17',
        ],
        'booking_list_SOIR': ['3535:SOIR:A19P1M3:2020-04-13'],
        'booking_list_GARD': ['3535:GARD:A19P1M4:2020-04-15'],
        'child_id': '3535',
        'regime': 'AV',
    }


@pytest.fixture
def annual_booking_params():
    return {
        'booking_list_MAT': [
            '3535:MAT:A19P1M1:monday',
            '3535:MAT:A19P1M1:tuesday',
            '3535:MAT:A19P1M1:thursday',
            '3535:MAT:A19P1M1:friday',
        ],
        'booking_list_MIDI': [
            '3535:MIDI:A19P1M2:monday',
            '3535:MIDI:A19P1M2:tuesday',
            '3535:MIDI:A19P1M2:thursday',
            '3535:MIDI:A19P1M2:friday',
        ],
        'booking_list_SOIR': ['3535:SOIR:A19P1M3:monday'],
        'booking_list_GARD': ['3535:GARD:A19P1M4:wednesday'],
        'child_id': '3535',
        'regime': 'AV',
        'booking_date': '2019-08-01',
    }


def test_lock(app, resource):
    resp = app.get('/toulouse-axel/test/lock?key=&locker=', status=400)
    assert resp.json['err_desc'] == "key is empty"
    assert resp.json['err'] == 'bad-request'

    assert Lock.objects.count() == 0
    resp = app.get('/toulouse-axel/test/lock?key=foobar&locker=')
    assert resp.json['err'] == 0
    assert resp.json['key'] == 'foobar'
    assert resp.json['locked'] is True
    assert resp.json['locker'] == ''
    assert resp.json['lock_date'] is not None
    lock_date = resp.json['lock_date']
    lock = Lock.objects.latest('pk')
    assert lock.resource == resource
    assert lock.key == 'foobar'
    assert lock.locker == ''

    # again
    resp = app.get('/toulouse-axel/test/lock?key=foobar&locker=')
    assert resp.json['err'] == 0
    assert resp.json['key'] == 'foobar'
    assert resp.json['locked'] is True
    assert Lock.objects.count() == 1
    assert resp.json['locker'] == ''
    assert resp.json['lock_date'] == lock_date


def test_lock_with_locker(app, resource):
    assert Lock.objects.count() == 0
    resp = app.get('/toulouse-axel/test/lock?key=foobar&locker=something')
    assert resp.json['err'] == 0
    assert resp.json['key'] == 'foobar'
    assert resp.json['locked'] is True
    assert resp.json['locker'] == 'something'
    assert resp.json['lock_date'] is not None
    lock_date = resp.json['lock_date']
    lock = Lock.objects.latest('pk')
    assert lock.resource == resource
    assert lock.key == 'foobar'
    assert lock.locker == 'something'

    # again
    resp = app.get('/toulouse-axel/test/lock?key=foobar&locker=')
    assert resp.json['err'] == 0
    assert resp.json['key'] == 'foobar'
    assert resp.json['locked'] is True
    assert Lock.objects.count() == 1
    assert resp.json['locker'] == 'something'
    assert resp.json['lock_date'] == lock_date


def test_unlock(app, resource):
    Lock.objects.create(resource=resource, key='foobar', locker='something')
    resp = app.get('/toulouse-axel/test/unlock?key=foobar')
    assert resp.json['err'] == 0
    assert resp.json['key'] == 'foobar'
    assert resp.json['locked'] is False
    assert resp.json['locker'] == 'something'
    assert resp.json['lock_date'] is not None
    assert Lock.objects.count() == 0

    # again
    resp = app.get('/toulouse-axel/test/unlock?key=foobar')
    assert resp.json['err'] == 0
    assert resp.json['key'] == 'foobar'
    assert resp.json['locked'] is False
    assert 'locker' not in resp.json
    assert 'lock_date' not in resp.json
    assert Lock.objects.count() == 0


def test_locked(app, resource):
    assert Lock.objects.count() == 0
    resp = app.get('/toulouse-axel/test/locked?key=foobar')
    assert resp.json['err'] == 0
    assert resp.json['key'] == 'foobar'
    assert resp.json['locked'] is False
    assert 'locker' not in resp.json
    assert 'lock_date' not in resp.json

    Lock.objects.create(resource=resource, key='foobar', locker='something')
    resp = app.get('/toulouse-axel/test/locked?key=foobar')
    assert resp.json['err'] == 0
    assert resp.json['key'] == 'foobar'
    assert resp.json['locked'] is True
    assert resp.json['locker'] == 'something'
    assert resp.json['lock_date'] is not None


def test_operation_status_error(resource):
    resp = '''
    <?xml version="1.0"?>
    <PORTAILSERVICE>
      <RESULTAT>
        <TYPE>RefVerifDui</TYPE>
        <STATUS>NOK</STATUS>
        <COMMENTAIRES><![CDATA[Foo reason]]></COMMENTAIRES>
      </RESULTAT>
      <DATA>
        <PORTAIL/>
      </DATA>
    </PORTAILSERVICE>
    '''.strip()
    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.soap_client') as client:
        client.return_value.service.getData.return_value = resp
        with pytest.raises(AxelError, match='Foo reason'):
            schemas.ref_verif_dui(
                resource,
                {
                    'PORTAIL': {
                        'DUI': {
                            'IDDUI': 'XXX',
                            'IDPERSONNE': '42',
                            'PRENOM': 'John',
                            'NOM': 'Doe',
                            'NAISSANCE': '2010-10-10',
                        }
                    }
                },
            )


@contextmanager
def mock_getdata(content, operation):
    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.soap_client') as client:
        resp = '''
        <?xml version="1.0"?>
        <PORTAILSERVICE>
          <RESULTAT>
            <TYPE>%s</TYPE>
            <STATUS>OK</STATUS>
            <DATE>10/10/2010 10:10:01</DATE>
            <COMMENTAIRES><![CDATA[]]></COMMENTAIRES>
          </RESULTAT>
          <DATA>
            %s
          </DATA>
        </PORTAILSERVICE>
        '''.strip() % (
            operation,
            content,
        )
        client.return_value.service.getData.return_value = resp
        yield


@pytest.mark.parametrize(
    'content',
    [
        '<PORTAIL><DUIDATEGESTION/></PORTAIL>',
    ],
)
def test_operation_ref_date_gestion_dui(resource, content):
    with mock_getdata(content, 'RefDateGestionDui'):
        with pytest.raises(AxelError):
            schemas.ref_date_gestion_dui(resource)


@pytest.mark.parametrize(
    'content',
    [
        '<PORTAIL><DUI/></PORTAIL>',
        '<PORTAIL><DUI><CODE>foo</CODE></DUI></PORTAIL>',
        '<PORTAIL><DUI><CODE>42</CODE></DUI></PORTAIL>',
    ],
)
def test_operation_ref_verif_dui(resource, content):
    with mock_getdata(content, 'RefVerifDui'):
        with pytest.raises(AxelError):
            schemas.ref_verif_dui(
                resource,
                {
                    'PORTAIL': {
                        'DUI': {
                            'PRENOM': 'John',
                            'NOM': 'Doe',
                            'NAISSANCE': '2010-10-10',
                        }
                    }
                },
            )


@pytest.mark.parametrize(
    'content',
    [
        '<PORTAIL><DUI/></PORTAIL>',
    ],
)
def test_operation_ref_famille_dui(resource, content):
    with mock_getdata(content, 'RefFamilleDui'):
        with pytest.raises(AxelError):
            schemas.ref_famille_dui(
                resource,
                {
                    'PORTAIL': {
                        'DUI': {
                            'IDDUI': 'XXX',
                        }
                    }
                },
            )


@pytest.mark.parametrize(
    'content',
    [
        '<PORTAIL><DUI/></PORTAIL>',
    ],
)
def test_operation_form_maj_famille_dui(resource, content):
    with mock_getdata(content, 'FormMajFamilleDui'):
        with pytest.raises(AxelError):
            schemas.form_maj_famille_dui(
                resource,
                {
                    'PORTAIL': {
                        'DUI': {
                            'IDDUI': 'XXX',
                        }
                    }
                },
            )


@pytest.mark.parametrize(
    'content',
    [
        '<PORTAIL><DUI/></PORTAIL>',
    ],
)
def test_operation_ref_facture_a_payer(resource, content):
    with mock_getdata(content, 'RefFactureAPayer'):
        with pytest.raises(AxelError):
            schemas.ref_facture_a_payer(
                resource,
                {
                    'PORTAIL': {
                        'DUI': {
                            'IDDUI': 'XXX',
                        }
                    }
                },
            )


@pytest.mark.parametrize(
    'content',
    [
        '<PORTAIL><LISTFACTURE/></PORTAIL>',
    ],
)
def test_operation_list_dui_factures(resource, content):
    with mock_getdata(content, 'ListeDuiFacturesPayeesRecettees'):
        with pytest.raises(AxelError):
            schemas.list_dui_factures(resource, {'LISTFACTURE': {'NUMDUI': 'XXX', 'DEBUT': '1970-01-01'}})


@pytest.mark.parametrize(
    'content',
    [
        "<PORTAIL><PDF FOO='BAR'></PDF></PORTAIL>",
    ],
)
def test_operation_ref_facture_pdf(resource, content):
    with mock_getdata(content, 'RefFacturePDF'):
        with pytest.raises(AxelError):
            schemas.ref_facture_pdf(
                resource,
                {
                    'PORTAIL': {
                        'FACTUREPDF': {
                            'IDFACTURE': 42,
                        }
                    }
                },
            )


@pytest.mark.parametrize(
    'content',
    [
        '<PORTAIL><DUI/></PORTAIL>',
    ],
)
def test_operation_form_paiement_dui(resource, content):
    with mock_getdata(content, 'FormPaiementDui'):
        with pytest.raises(AxelError):
            schemas.form_paiement_dui(
                resource,
                {
                    'PORTAIL': {
                        'DUI': {
                            'IDFACTURE': '42',
                            'IDREGIEENCAISSEMENT': '',
                            'MONTANTPAYE': '42.42',
                            'DATEPAIEMENT': '01/01/2020 12:12:12',
                            'REFERENCE': '42',
                        }
                    }
                },
            )


@pytest.mark.parametrize(
    'content',
    [
        '<PORTAIL><DUI/></PORTAIL>',
    ],
)
def test_operation_enfants_activites(resource, content):
    with mock_getdata(content, 'EnfantsActivites'):
        with pytest.raises(AxelError):
            schemas.enfants_activites(
                resource,
                {
                    'DUI': {
                        'IDDUI': 'XXX',
                        'ANNEEREFERENCE': '2042',
                        'TYPESACTIVITES': 'MAT,MIDI,SOIR,GARD',
                    }
                },
            )


@pytest.mark.parametrize(
    'content',
    [
        '<PORTAIL><DUI/></PORTAIL>',
    ],
)
def test_operation_reservation_periode(resource, content):
    with mock_getdata(content, 'ReservationPeriode'):
        with pytest.raises(AxelError):
            schemas.reservation_periode(
                resource,
                {
                    'PORTAIL': {
                        'DUI': {
                            'IDDUI': 'XXX',
                        }
                    }
                },
            )


def test_management_dates_endpoint_axel_error(app, resource):
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_date_gestion_dui') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get('/toulouse-axel/test/management_dates')
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'
    assert resp.json['data'] == {'xml_request': None, 'xml_response': None}


def test_management_dates_endpoint(app, resource):
    content = '''<PORTAIL>
    <DUIDATEGESTION>
        <REPORT-REVENUS>08/08/2019</REPORT-REVENUS>
        <EXTRACTION-FAMILLES>18/10/2019</EXTRACTION-FAMILLES>
        <EXTRACTION-CAFPRO>18/01/2020</EXTRACTION-CAFPRO>
    </DUIDATEGESTION>
</PORTAIL>'''
    with mock_getdata(content, 'RefDateGestionDui'):
        resp = app.get('/toulouse-axel/test/management_dates')
    assert set(resp.json.keys()) == {'err', 'data'}
    assert resp.json['err'] == 0
    assert set(resp.json['data'].keys()) == {
        'REPORT-REVENUS',
        'report_revenus',
        'EXTRACTION-FAMILLES',
        'extraction_familles',
        'EXTRACTION-CAFPRO',
        'extraction_cafpro',
    }

    # again - data are in cache
    resp = app.get('/toulouse-axel/test/management_dates')
    assert set(resp.json.keys()) == {'err', 'data'}
    assert resp.json['err'] == 0
    assert set(resp.json['data'].keys()) == {
        'REPORT-REVENUS',
        'report_revenus',
        'EXTRACTION-FAMILLES',
        'extraction_familles',
        'EXTRACTION-CAFPRO',
        'extraction_cafpro',
    }

    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_management_dates'
    ) as management_dates:
        management_dates.return_value = {'foo': 'bar'}
        resp = app.get('/toulouse-axel/test/management_dates')
    assert set(resp.json.keys()) == {'err', 'data'}
    assert resp.json['err'] == 0
    assert resp.json['data'] == {'foo': 'bar'}


def test_link_endpoint_nameid_empty(app, resource, link_params):
    resp = app.post_json('/toulouse-axel/test/link?NameID=', params=link_params, status=400)
    assert resp.json['err_desc'] == "NameID is empty"
    assert resp.json['err'] == 'bad-request'


def test_link_endpoint_axel_error(app, resource, link_params):
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_verif_dui') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'
    assert resp.json['data'] == {'xml_request': None, 'xml_response': None}

    # test xml_request and xml_response only for this endpoint
    xml_request = """<PORTAIL>
  <DUI>
    <IDDUI>XXX</IDDUI>
    <IDPERSONNE />
    <NOM>Doe</NOM>
    <PRENOM>John</PRENOM>
    <NAISSANCE>10/10/2010</NAISSANCE>
  </DUI>
</PORTAIL>
"""

    with mock_getdata('', 'RefVerifDui'):
        with mock.patch('xmlschema.XMLSchema.validate') as xml_validate:
            xml_validate.side_effect = xmlschema.XMLSchemaValidationError(None, None)
            resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
    assert resp.json['err_desc'].startswith("Axel error: invalid request")
    assert resp.json['err'] == 'error'
    assert resp.json['data']['xml_request'] == xml_request
    assert resp.json['data']['xml_response'] is None

    xml_response = """<PORTAILSERVICE>
  <RESULTAT>
    <TYPE>RefVerifDui</TYPE>
    <STATUS>NOK</STATUS>
    <COMMENTAIRES>Foo reason</COMMENTAIRES>
  </RESULTAT>
  <DATA>
    <PORTAIL />
  </DATA>
</PORTAILSERVICE>"""
    response = """
    <?xml version="1.0"?>
    <PORTAILSERVICE>
      <RESULTAT>
        <TYPE>RefVerifDui</TYPE>
        <STATUS>NOK</STATUS>
        <COMMENTAIRES><![CDATA[Foo reason]]></COMMENTAIRES>
      </RESULTAT>
      <DATA>
        <PORTAIL/>
      </DATA>
    </PORTAILSERVICE>
    """.strip()
    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.soap_client') as client:
        client.return_value.service.getData.return_value = response
        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
    assert resp.json['err_desc'] == 'Axel error: Foo reason'
    assert resp.json['err'] == 'error'
    assert resp.json['data']['xml_request'] == xml_request
    assert resp.json['data']['xml_response'] == xml_response

    content = """<PORTAIL>
      <DUI>
        <IDDUI>XXX</IDDUI>
        <IDPERSONNE>42</IDPERSONNE>
        <CODE>2</CODE>
      </DUI>
    </PORTAIL>"""
    xml_response = (
        """<PORTAILSERVICE>
  <RESULTAT>
    <TYPE>RefVerifDui</TYPE>
    <STATUS>OK</STATUS>
    <DATE>10/10/2010 10:10:01</DATE>
    <COMMENTAIRES />
  </RESULTAT>
  <DATA>
    %s
  </DATA>
</PORTAILSERVICE>"""
        % content
    )
    with mock_getdata(content, 'RefVerifDui'):
        with mock.patch('passerelle.contrib.utils.axel.AxelSchema.decode') as decode:
            decode.side_effect = xmlschema.XMLSchemaValidationError(None, None)
            resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
    assert resp.json['err_desc'].startswith("Axel error: invalid response")
    assert resp.json['err'] == 'error'
    assert resp.json['data']['xml_request'] == xml_request
    assert resp.json['data']['xml_response'] == xml_response

    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.soap_client') as client:
        client.side_effect = SOAPError('SOAP service is down')
        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
    assert resp.json['err_desc'] == "SOAP service is down"


@pytest.mark.parametrize(
    'xml_response',
    [
        '<IDDUI>XXX</IDDUI><IDPERSONNE/><CODE>0</CODE>',
        '<IDDUI>XXX</IDDUI><IDPERSONNE>42</IDPERSONNE><CODE>1</CODE>',
        '<IDDUI>XXX</IDDUI><IDPERSONNE>42</IDPERSONNE><CODE>4</CODE>',
    ],
)
def test_link_endpoint_no_result(app, resource, link_params, xml_response):
    content = (
        '''<PORTAIL>
    <DUI>
        %s
    </DUI>
</PORTAIL>'''
        % xml_response
    )
    with mock_getdata(content, 'RefVerifDui'):
        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'


def test_link_endpoint_conflict(app, resource, link_params):
    content = '''<PORTAIL>
    <DUI>
        <IDDUI>XXX</IDDUI>
        <IDPERSONNE>42</IDPERSONNE>
        <CODE>2</CODE>
    </DUI>
</PORTAIL>'''
    # existing link but dui is wrong
    link = Link.objects.create(resource=resource, name_id='yyy', dui='YYY', person_id='42')
    with mock_getdata(content, 'RefVerifDui'):
        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
    assert resp.json['err_desc'] == "Data conflict"
    assert resp.json['err'] == 'conflict'

    # existing link but person_id is wrong
    link.dui = 'XXX'
    link.person_id = '35'
    link.save()
    with mock_getdata(content, 'RefVerifDui'):
        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
    assert resp.json['err_desc'] == "Data conflict"
    assert resp.json['err'] == 'conflict'


@pytest.mark.parametrize('code', [2, 3])
def test_link_endpoint(app, resource, link_params, code):
    content = (
        '''<PORTAIL>
    <DUI>
        <IDDUI>XXX</IDDUI>
        <IDPERSONNE>42</IDPERSONNE>
        <CODE>%s</CODE>
    </DUI>
</PORTAIL>'''
        % code
    )
    with mock_getdata(content, 'RefVerifDui'):
        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
    assert set(resp.json.keys()) == {'err', 'link', 'created', 'dui', 'data'}
    assert resp.json['err'] == 0
    assert resp.json['dui'] == 'XXX'
    assert resp.json['created'] is True
    assert 'xml_request' in resp.json['data']
    assert 'xml_response' in resp.json['data']

    # again
    with mock_getdata(content, 'RefVerifDui'):
        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
    assert set(resp.json.keys()) == {'err', 'link', 'created', 'dui', 'data'}
    assert resp.json['err'] == 0
    assert resp.json['dui'] == 'XXX'
    assert resp.json['created'] is False  # link already exists
    assert 'xml_request' in resp.json['data']
    assert 'xml_response' in resp.json['data']


def test_unlink_endpoint_no_result(app, resource):
    resp = app.post('/toulouse-axel/test/unlink?NameID=yyy')
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'


def test_unlink_endpoint(app, resource):
    link = Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    resp = app.post('/toulouse-axel/test/unlink?NameID=yyy')
    assert Link.objects.exists() is False
    assert resp.json['err'] == 0
    assert resp.json['link'] == link.pk
    assert resp.json['dui'] == 'XXX'
    assert resp.json['deleted'] is True


def test_active_dui_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_famille_dui') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get('/toulouse-axel/test/active_dui?NameID=yyy')
    assert resp.json['err_desc'] == "No family info"
    assert resp.json['err'] == 'no-family-info'

    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/family_info.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFamilleDui'):
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_verif_dui') as operation:
            operation.side_effect = AxelError('FooBar')
            resp = app.get('/toulouse-axel/test/active_dui?NameID=yyy')
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


def test_active_dui_endpoint_no_result(app, resource, family_data):
    resp = app.get('/toulouse-axel/test/active_dui?NameID=yyy')
    assert resp.json['err_desc'] == "Unknown NameID"
    assert resp.json['err'] == 'unknown'


def test_active_dui_endpoint_wrong_rl(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='1234')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/family_info.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFamilleDui'):
        resp = app.get('/toulouse-axel/test/active_dui?NameID=yyy')
    assert resp.json['err_desc'] == "No corresponding RL"
    assert resp.json['err'] == 'no-rl'


@pytest.mark.parametrize(
    'xml_response,code',
    [
        ('<IDDUI>XXX</IDDUI><IDPERSONNE/><CODE>0</CODE>', 0),
        ('<IDDUI>XXX</IDDUI><IDPERSONNE>42</IDPERSONNE><CODE>1</CODE>', 1),
        ('<IDDUI>XXX</IDDUI><IDPERSONNE>42</IDPERSONNE><CODE>4</CODE>', 4),
    ],
)
def test_active_dui_endpoint_wrong_dui_code(app, resource, family_data, xml_response, code):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    content = (
        '''<PORTAIL>
    <DUI>
        %s
    </DUI>
</PORTAIL>'''
        % xml_response
    )
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        with mock_getdata(content, 'RefVerifDui'):
            resp = app.get('/toulouse-axel/test/active_dui?NameID=yyy')
    assert resp.json['err_desc'] == "Wrong DUI status"
    assert resp.json['err'] == 'dui-code-error-%s' % code


@pytest.mark.parametrize('code', [2, 3])
def test_active_dui_endpoint(app, resource, family_data, code):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    content = (
        '''<PORTAIL>
    <DUI>
        <IDDUI>XXX</IDDUI>
        <IDPERSONNE>42</IDPERSONNE>
        <CODE>%s</CODE>
    </DUI>
</PORTAIL>'''
        % code
    )
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        with mock_getdata(content, 'RefVerifDui'):
            resp = app.get('/toulouse-axel/test/active_dui?NameID=yyy')
    assert resp.json['err'] == 0
    assert set(resp.json['data'].keys()) == {
        'ADRESSE',
        'CODEMISEAJOUR',
        'DEMATFACTURES',
        'ENFANT',
        'IDDUI',
        'NBENFANTACTIF',
        'NBRLACTIF',
        'REACTUALISATIONENLIGNE',
        'REVENUS',
        'RL1',
        'RL2',
        'SITUATIONFAMILIALE',
        'TELFIXE',
    }


def test_referential_endpoint_no_result(app, resource):
    resp = app.get('/toulouse-axel/test/referential/foo/')
    assert resp.json['err_desc'] == "Referential not found"
    assert resp.json['err'] == 'not-found'


@pytest.mark.parametrize(
    'code, mapping',
    [
        ('situation_familiale', situation_familiale_mapping),
        ('csp', csp_mapping),
        ('lien_parente', lien_parente_mapping),
        ('type_regime', type_regime_mapping),
        ('regime', regime_mapping),
    ],
)
def test_referential_endpoint(app, resource, code, mapping):
    resp = app.get('/toulouse-axel/test/referential/%s/' % code)
    expected = [{'id': k, 'text': v} for k, v in mapping.items()]
    assert resp.json['data'] == expected


def test_family_info_endpoint_axel_error(app, resource, family_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_famille_dui') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get('/toulouse-axel/test/family_info?NameID=yyy')
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'

    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/family_info.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_management_dates'
    ) as management_dates:
        management_dates.side_effect = APIError('Axel error: FooBar')
        with mock_getdata(content, 'RefFamilleDui'):
            resp = app.get('/toulouse-axel/test/family_info?NameID=yyy')
    assert resp.json['err_desc'] == "Axel error: FooBar"


def test_family_info_endpoint_no_result(app, resource):
    resp = app.get('/toulouse-axel/test/family_info?NameID=yyy')
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'

    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_famille_dui') as operation:
        operation.return_value = OperationResult(
            json_response={'DATA': {'PORTAIL': None}}, xml_request='', xml_response=''
        )
        resp = app.get('/toulouse-axel/test/family_info?NameID=yyy')
    assert resp.json['err_desc'] == "Family not found"
    assert resp.json['err'] == 'not-found'


@freezegun.freeze_time('2020-06-30')
def test_family_info_endpoint(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/family_info.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_management_dates'
    ) as management_dates:
        management_dates.return_value = {'foo': 'bar'}
        with mock_getdata(content, 'RefFamilleDui'):
            resp = app.get('/toulouse-axel/test/family_info?NameID=yyy')
    assert resp.json['err'] == 0
    assert set(resp.json['data'].keys()) == {
        'ADRESSE',
        'CODEMISEAJOUR',
        'DEMATFACTURES',
        'ENFANT',
        'IDDUI',
        'NBENFANTACTIF',
        'NBRLACTIF',
        'REACTUALISATIONENLIGNE',
        'REVENUS',
        'RL1',
        'RL2',
        'SITUATIONFAMILIALE',
        'SITUATIONFAMILIALE_label',
        'TELFIXE',
        'management_dates',
        'annee_reference',
        'annee_reference_short',
        'annee_reference_label',
    }
    assert resp.json['data']['ENFANT'][0]['id'] == '4242'
    assert resp.json['data']['ENFANT'][0]['text'] == 'foo foo'
    assert resp.json['data']['ENFANT'][0]['CONTACT'][0]['id'] == 0
    assert resp.json['data']['ENFANT'][0]['CONTACT'][0]['text'] == 'foo foo'
    assert resp.json['data']['ENFANT'][0]['CONTACT'][1]['id'] == 1
    assert resp.json['data']['ENFANT'][0]['CONTACT'][1]['text'] == 'foo2 foo2'
    assert resp.json['data']['ENFANT'][0]['CONTACT'][2]['id'] == 2
    assert resp.json['data']['ENFANT'][0]['CONTACT'][2]['text'] == 'foo3 foo3'
    assert resp.json['data']['ENFANT'][1]['id'] == '3535'
    assert resp.json['data']['ENFANT'][1]['text'] == 'foo foo'
    assert resp.json['data']['management_dates'] == {'foo': 'bar'}
    assert resp.json['data']['annee_reference'] == 2019
    assert resp.json['data']['annee_reference_short'] == '19'
    assert resp.json['data']['annee_reference_label'] == '2019/2020'

    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.are_children_registered'
    ) as registered:
        registered.return_value = {'4242': True}
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_management_dates'
        ) as management_dates:
            management_dates.return_value = {'foo': 'bar'}
            with mock_getdata(content, 'RefFamilleDui'):
                resp = app.get('/toulouse-axel/test/family_info?NameID=yyy')
    assert resp.json['err'] == 0
    assert resp.json['data']['ENFANT'][0]['clae_cantine_current'] is True
    assert resp.json['data']['ENFANT'][1]['clae_cantine_current'] is None

    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.are_children_registered'
    ) as registered:
        registered.return_value = {}
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_management_dates'
        ) as management_dates:
            management_dates.return_value = {'foo': 'bar'}
            with mock_getdata(content, 'RefFamilleDui'):
                resp = app.get('/toulouse-axel/test/family_info?NameID=yyy')
    assert resp.json['err'] == 0
    assert resp.json['data']['ENFANT'][0]['clae_cantine_current'] is None
    assert resp.json['data']['ENFANT'][1]['clae_cantine_current'] is None

    assert resp.json['data']['SITUATIONFAMILIALE'] == 'S'
    assert resp.json['data']['SITUATIONFAMILIALE_label'] == 'Séparé (e)'
    assert resp.json['data']['RL1']['CSP'] == 'ETU'
    assert resp.json['data']['RL1']['CSP_label'] == 'Etudiants'
    assert resp.json['data']['RL2']['CSP'] == 'EMP'
    assert resp.json['data']['RL2']['CSP_label'] == 'Employés'
    assert resp.json['data']['ENFANT'][0]['CONTACT'][0]['LIENPARENTE'] == 'GRP1'
    assert resp.json['data']['ENFANT'][0]['CONTACT'][0]['LIENPARENTE_label'] == 'Grands-parents paternels'
    assert resp.json['data']['ENFANT'][0]['CONTACT'][1]['LIENPARENTE'] is None
    assert resp.json['data']['ENFANT'][0]['CONTACT'][1]['LIENPARENTE_label'] is None
    assert resp.json['data']['REVENUS']['TYPEREGIME'] == 'GENE'
    assert resp.json['data']['REVENUS']['TYPEREGIME_label'] == 'Régime général'

    # a DUI with less data
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/family_info_light.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_management_dates'
    ) as management_dates:
        management_dates.return_value = {'foo': 'bar'}
        with mock_getdata(content, 'RefFamilleDui'):
            resp = app.get('/toulouse-axel/test/family_info?NameID=yyy')
    assert resp.json['err'] == 0
    assert set(resp.json['data'].keys()) == {
        'ADRESSE',
        'CODEMISEAJOUR',
        'DEMATFACTURES',
        'ENFANT',
        'IDDUI',
        'NBENFANTACTIF',
        'NBRLACTIF',
        'REACTUALISATIONENLIGNE',
        'REVENUS',
        'RL1',
        'SITUATIONFAMILIALE',
        'SITUATIONFAMILIALE_label',
        'TELFIXE',
        'management_dates',
        'annee_reference',
        'annee_reference_short',
        'annee_reference_label',
    }
    assert resp.json['data']['ADRESSE'] is None


def test_children_info_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_famille_dui') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get('/toulouse-axel/test/children_info?NameID=yyy')
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


def test_children_info_endpoint_no_result(app, resource):
    resp = app.get('/toulouse-axel/test/children_info?NameID=yyy')
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'


def test_children_info_endpoint(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/family_info.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFamilleDui'):
        resp = app.get('/toulouse-axel/test/children_info?NameID=yyy')
    assert resp.json['err'] == 0
    assert len(resp.json['data']) == 2
    assert resp.json['data'][0]['id'] == '4242'
    assert resp.json['data'][0]['text'] == 'foo foo'
    assert resp.json['data'][0]['CONTACT'][0]['id'] == 0
    assert resp.json['data'][0]['CONTACT'][0]['text'] == 'foo foo'
    assert resp.json['data'][0]['CONTACT'][1]['id'] == 1
    assert resp.json['data'][0]['CONTACT'][1]['text'] == 'foo2 foo2'
    assert resp.json['data'][0]['CONTACT'][2]['id'] == 2
    assert resp.json['data'][0]['CONTACT'][2]['text'] == 'foo3 foo3'
    assert resp.json['data'][1]['id'] == '3535'
    assert resp.json['data'][1]['text'] == 'foo foo'


def test_child_info_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_famille_dui') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get('/toulouse-axel/test/child_info?NameID=yyy&idpersonne=zzz')
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


def test_child_info_endpoint_no_result(app, resource):
    resp = app.get('/toulouse-axel/test/child_info?NameID=yyy&idpersonne=zzz')
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'

    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/family_info.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFamilleDui'):
        resp = app.get('/toulouse-axel/test/child_info?NameID=yyy&idpersonne=zzz')
    assert resp.json['err_desc'] == "Child not found"
    assert resp.json['err'] == 'not-found'


def test_child_info_endpoint(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/family_info.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFamilleDui'):
        resp = app.get('/toulouse-axel/test/child_info?NameID=yyy&idpersonne=4242')
    assert resp.json['err'] == 0
    assert set(resp.json['data'].keys()) == {
        'id',
        'text',
        'ASSURANCE',
        'CONTACT',
        'DATENAISSANCE',
        'IDPERSONNE',
        'PRENOM',
        'PRENOMMERE',
        'PRENOMPERE',
        'NOM',
        'NOMMERE',
        'NOMPERE',
        'RATTACHEAUTREDUI',
        'SANITAIRE',
        'SEXE',
        'clae_cantine_current',
    }
    assert resp.json['data']['id'] == '4242'
    assert resp.json['data']['text'] == 'foo foo'
    assert resp.json['data']['CONTACT'][0]['id'] == 0
    assert resp.json['data']['CONTACT'][0]['text'] == 'foo foo'
    assert resp.json['data']['CONTACT'][1]['id'] == 1
    assert resp.json['data']['CONTACT'][1]['text'] == 'foo2 foo2'
    assert resp.json['data']['CONTACT'][2]['id'] == 2
    assert resp.json['data']['CONTACT'][2]['text'] == 'foo3 foo3'

    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.are_children_registered'
    ) as registered:
        registered.return_value = {'4242': True}
        with mock_getdata(content, 'RefFamilleDui'):
            resp = app.get('/toulouse-axel/test/child_info?NameID=yyy&idpersonne=4242')
    assert resp.json['err'] == 0
    assert resp.json['data']['clae_cantine_current'] is True

    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.are_children_registered'
    ) as registered:
        registered.return_value = {}
        with mock_getdata(content, 'RefFamilleDui'):
            resp = app.get('/toulouse-axel/test/child_info?NameID=yyy&idpersonne=4242')
    assert resp.json['err'] == 0
    assert resp.json['data']['clae_cantine_current'] is None

    assert resp.json['data']['CONTACT'][0]['LIENPARENTE'] == 'GRP1'
    assert resp.json['data']['CONTACT'][0]['LIENPARENTE_label'] == 'Grands-parents paternels'
    assert resp.json['data']['CONTACT'][1]['LIENPARENTE'] is None
    assert resp.json['data']['CONTACT'][1]['LIENPARENTE_label'] is None


def test_child_contacts_info_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_famille_dui') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get('/toulouse-axel/test/child_contacts_info?NameID=yyy&idpersonne=zzz')
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


def test_child_contacts_info_endpoint_no_result(app, resource):
    resp = app.get('/toulouse-axel/test/child_contacts_info?NameID=yyy&idpersonne=zzz')
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'

    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/family_info.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFamilleDui'):
        resp = app.get('/toulouse-axel/test/child_contacts_info?NameID=yyy&idpersonne=zzz')
    assert resp.json['err_desc'] == "Child not found"
    assert resp.json['err'] == 'not-found'


def test_child_contacts_info_endpoint(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/family_info.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFamilleDui'):
        resp = app.get('/toulouse-axel/test/child_contacts_info?NameID=yyy&idpersonne=4242')
    assert resp.json['err'] == 0
    assert len(resp.json['data']) == 3
    assert resp.json['data'][0]['id'] == 0
    assert resp.json['data'][0]['text'] == 'foo foo'
    assert resp.json['data'][1]['id'] == 1
    assert resp.json['data'][1]['text'] == 'foo2 foo2'
    assert resp.json['data'][2]['id'] == 2
    assert resp.json['data'][2]['text'] == 'foo3 foo3'
    with mock_getdata(content, 'RefFamilleDui'):
        resp = app.get('/toulouse-axel/test/child_contacts_info?NameID=yyy&idpersonne=3535')
    assert resp.json['err'] == 0
    assert len(resp.json['data']) == 1
    assert resp.json['data'][0]['id'] == 0
    assert resp.json['data'][0]['text'] == 'foo foo'


def test_children_contacts_info_endpoint(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/family_info.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFamilleDui'):
        resp = app.get('/toulouse-axel/test/children_contacts_info?NameID=yyy')
    assert resp.json['err'] == 0
    assert len(resp.json['data']) == 3
    assert {x['text'] for x in resp.json['data']} == {'foo foo', 'foo2 foo2', 'foo3 foo3'}
    assert {x['id'] for x in resp.json['data']} == {0, 1, 2}


def test_update_family_info_endpoint_axel_error(app, resource, update_params, family_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')

    with mock.patch('passerelle.contrib.toulouse_axel.schemas.form_maj_famille_dui') as operation:
        operation.side_effect = AxelError('FooBar')
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
        ):
            resp = app.post_json('/toulouse-axel/test/update_family_info?NameID=yyy', params=update_params)
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'
    assert 'error_post_data' in resp.json['data']


def test_update_family_info_endpoint_no_result(app, resource, update_params):
    resp = app.post_json('/toulouse-axel/test/update_family_info?NameID=yyy', params=update_params)
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'


def test_update_family_info_endpoint(app, resource, update_params, family_data):
    link = Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')

    content = "<PORTAIL/>"
    with mock_getdata(content, 'FormMajFamilleDui'):
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
        ):
            resp = app.post_json('/toulouse-axel/test/update_family_info?NameID=yyy', params=update_params)
    assert resp.json['err'] == 0
    assert resp.json['dui'] == 'XXX'
    assert resp.json['updated'] is True
    assert 'data' in resp.json
    assert 'xml_request' in resp.json['data']
    assert 'xml_response' in resp.json['data']

    with mock.patch('passerelle.contrib.toulouse_axel.schemas.form_maj_famille_dui') as operation:
        operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
        ):
            resp = app.post_json('/toulouse-axel/test/update_family_info?NameID=yyy', params=update_params)
    assert operation.call_args_list[0][0][1]['PORTAIL']['DUI']['IDDUI'] == 'XXX'
    assert operation.call_args_list[0][0][1]['PORTAIL']['DUI'][
        'DATEDEMANDE'
    ] == datetime.date.today().strftime('%Y-%m-%d')
    assert operation.call_args_list[0][0][1]['PORTAIL']['DUI']['QUIACTUALISEDUI'] == '1'

    link.person_id = '35'
    link.save()
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.form_maj_famille_dui') as operation:
        operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
        ):
            resp = app.post_json('/toulouse-axel/test/update_family_info?NameID=yyy', params=update_params)
    assert operation.call_args_list[0][0][1]['PORTAIL']['DUI']['IDDUI'] == 'XXX'
    assert operation.call_args_list[0][0][1]['PORTAIL']['DUI'][
        'DATEDEMANDE'
    ] == datetime.date.today().strftime('%Y-%m-%d')
    assert operation.call_args_list[0][0][1]['PORTAIL']['DUI']['QUIACTUALISEDUI'] == '2'


def test_update_family_info_flat_endpoint(app, resource, flat_update_params, family_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    content = "<PORTAIL/>"
    with mock_getdata(content, 'FormMajFamilleDui'):
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
        ):
            resp = app.post_json(
                '/toulouse-axel/test/update_family_info?NameID=yyy', params=flat_update_params
            )
    assert resp.json['err'] == 0
    assert resp.json['dui'] == 'XXX'
    assert resp.json['updated'] is True
    assert 'data' in resp.json
    assert 'xml_request' in resp.json['data']
    assert 'xml_response' in resp.json['data']


def test_sanitize_update_family_data_missing_rl_fields(app, resource, update_params, family_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    full_update_params = copy.deepcopy(update_params)
    for key in ['IDPERSONNE', 'NOM', 'PRENOM', 'NOMJEUNEFILLE', 'DATENAISSANCE', 'CIVILITE']:
        assert key not in full_update_params['RL1']
        assert key not in full_update_params['RL2']

    resource.pre_sanitize_update_family_data(post_data=full_update_params)
    jsonschema.validate(full_update_params, json_schema)
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        resource.sanitize_update_family_data(dui='XXX', post_data=full_update_params)
    for key in ['IDPERSONNE', 'NOM', 'PRENOM', 'NOMJEUNEFILLE', 'DATENAISSANCE', 'CIVILITE']:
        assert full_update_params['RL1'][key] == family_data['RL1'][key]
        assert full_update_params['RL2'][key] == family_data['RL2'][key]
    assert full_update_params['RL1']['INDICATEURRL'] == '1'
    assert full_update_params['RL2']['INDICATEURRL'] == '2'


def test_sanitize_update_family_data_missing_revenus_fields(app, resource, update_params, family_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    full_update_params = copy.deepcopy(update_params)
    assert 'NBENFANTSACHARGE' not in update_params['REVENUS']

    resource.pre_sanitize_update_family_data(post_data=full_update_params)
    jsonschema.validate(full_update_params, json_schema)
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        resource.sanitize_update_family_data(dui='XXX', post_data=full_update_params)
    assert full_update_params['REVENUS']['NBENFANTSACHARGE'] == family_data['REVENUS']['NBENFANTSACHARGE']

    # if revenus are not set in Axel
    full_update_params = copy.deepcopy(update_params)
    family_data.pop('REVENUS')
    resource.pre_sanitize_update_family_data(post_data=full_update_params)
    jsonschema.validate(full_update_params, json_schema)
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        resource.sanitize_update_family_data(dui='XXX', post_data=full_update_params)
    assert full_update_params['REVENUS']['NBENFANTSACHARGE'] is None


def test_pre_sanitize_update_upperize(app, resource, update_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    def check_upper(data):
        if isinstance(data, dict):
            for val in data.values():
                check_upper(val)
        if isinstance(data, list):
            for val in data:
                check_upper(val)
        if isinstance(data, str):
            assert data == data.upper()

    resource.pre_sanitize_update_family_data(post_data=update_params)
    jsonschema.validate(update_params, json_schema)
    check_upper(update_params)

    assert upperize('something with an accent é') == 'SOMETHING WITH AN ACCENT E'


def test_pre_sanitize_update_family_data_adresse(app, resource, update_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    # test maj:adresse
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:adresse'] = False
    for key in partial_update_params['ADRESSE'].keys():
        partial_update_params['ADRESSE'][key] = None  # reset fields
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    for value in partial_update_params['ADRESSE'].values():
        assert value is None


def test_pre_sanitize_update_family_data_revenus(app, resource, update_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    # test maj:revenus
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:revenus'] = False
    for key in partial_update_params['REVENUS'].keys():
        partial_update_params['REVENUS'][key] = None  # reset fields
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'REVENUS' not in partial_update_params

    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:revenus'] = False
    partial_update_params.pop('REVENUS')
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'REVENUS' not in partial_update_params


def test_pre_sanitize_update_family_data_rln(app, resource, update_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    # test maj:rln
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:rl1'] = False
    for key in partial_update_params['RL1'].keys():
        partial_update_params['RL1'][key] = None  # reset fields
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'RL1' not in partial_update_params
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:rl2'] = False
    for key in partial_update_params['RL2'].keys():
        partial_update_params['RL2'][key] = None  # reset fields
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'RL2' not in partial_update_params

    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:rl1'] = False
    partial_update_params['maj:rl2'] = False
    partial_update_params.pop('RL1')
    partial_update_params.pop('RL2')
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'RL1' not in partial_update_params
    assert 'RL2' not in partial_update_params


def test_pre_sanitize_update_family_data_rln_adresse_employeur(app, resource, update_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    # test maj:rln_adresse_employeur
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:rl1_adresse_employeur'] = False
    for key in partial_update_params['RL1']['ADREMPLOYEUR'].keys():
        partial_update_params['RL1']['ADREMPLOYEUR'][key] = None  # reset fields
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'ADREMPLOYEUR' not in partial_update_params['RL1']
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:rl2_adresse_employeur'] = False
    for key in partial_update_params['RL2']['ADREMPLOYEUR'].keys():
        partial_update_params['RL2']['ADREMPLOYEUR'][key] = None  # reset fields
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'ADREMPLOYEUR' not in partial_update_params['RL2']
    # combine with maj:rln
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:rl1'] = False
    partial_update_params['maj:rl1_adresse_employeur'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'RL1' not in partial_update_params
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:rl2'] = False
    partial_update_params['maj:rl2_adresse_employeur'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'RL2' not in partial_update_params

    # test maj:rln_adresse_employeur not set
    for val in [None, '']:
        partial_update_params = copy.deepcopy(update_params)
        partial_update_params['maj:rl1_adresse_employeur'] = val
        partial_update_params['RL1'].pop('ADREMPLOYEUR')
        resource.pre_sanitize_update_family_data(post_data=partial_update_params)
        jsonschema.validate(partial_update_params, json_schema)
        assert 'ADREMPLOYEUR' not in partial_update_params['RL1']
        partial_update_params = copy.deepcopy(update_params)
        partial_update_params['maj:rl2_adresse_employeur'] = val
        partial_update_params['RL2'].pop('ADREMPLOYEUR')
        resource.pre_sanitize_update_family_data(post_data=partial_update_params)
        jsonschema.validate(partial_update_params, json_schema)
        assert 'ADREMPLOYEUR' not in partial_update_params['RL2']


def test_pre_sanitize_update_family_data_enfant_n(app, resource, update_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    # test maj:enfant_n
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_0'] = False
    for key in partial_update_params['ENFANT'][0].keys():
        if key == 'IDPERSONNE':
            continue
        partial_update_params['ENFANT'][0][key] = None  # reset fields
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "3535"

    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    for key in partial_update_params['ENFANT'][1].keys():
        if key == 'IDPERSONNE':
            continue
        partial_update_params['ENFANT'][1][key] = None  # reset fields
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "4242"

    # do not fill IDPERSONNE for the removed child
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    for key in partial_update_params['ENFANT'][1].keys():
        partial_update_params['ENFANT'][1][key] = None  # reset fields
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "4242"

    # remove all children
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_0'] = False
    partial_update_params['maj:enfant_1'] = False
    partial_update_params.pop('ENFANT')
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'ENFANT' not in partial_update_params

    # unknown child
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_5'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert len(partial_update_params['ENFANT']) == 2

    # missing IDPERSONNE
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['ENFANT'][0].pop('IDPERSONNE')
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert len(partial_update_params['ENFANT']) == 1


def test_pre_sanitize_update_family_data_enfant_n_assurance(app, resource, update_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    # test maj:enfant_n_assurance
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1_assurance'] = False
    for key in partial_update_params['ENFANT'][1]['ASSURANCE'].keys():
        partial_update_params['ENFANT'][1]['ASSURANCE'][key] = None  # reset fields
    partial_update_params['maj:enfant_0'] = False  # check that ordering is not a problem
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "3535"
    assert 'ASSURANCE' not in partial_update_params['ENFANT'][0]
    # combine with maj:enfant_n
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    partial_update_params['maj:enfant_1_assurance'] = False
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'ENFANT' not in partial_update_params

    # test maj:enfant_n_assurance not set
    for val in [None, '']:
        partial_update_params = copy.deepcopy(update_params)
        partial_update_params['maj:enfant_1_assurance'] = val
        partial_update_params['maj:enfant_0'] = False
        partial_update_params['ENFANT'][0].pop('ASSURANCE')
        resource.pre_sanitize_update_family_data(post_data=partial_update_params)
        jsonschema.validate(partial_update_params, json_schema)
        assert 'ASSURANCE' not in partial_update_params['ENFANT'][0]


def test_pre_sanitize_update_family_data_enfant_n_contact(app, resource, update_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    # test maj:enfant_n_contact
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_0_contact'] = False
    for i, contact in enumerate(partial_update_params['ENFANT'][0]['CONTACT']):
        for key in contact.keys():
            partial_update_params['ENFANT'][0]['CONTACT'][i][key] = None  # reset fields
    partial_update_params['maj:enfant_1'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "4242"
    assert 'CONTACT' not in partial_update_params['ENFANT'][0]
    # combine with maj:enfant_n
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    partial_update_params['maj:enfant_1_contact'] = False
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'ENFANT' not in partial_update_params

    # test maj:enfant_n_contact not set
    for val in [None, '']:
        partial_update_params = copy.deepcopy(update_params)
        partial_update_params['maj:enfant_0_contact'] = val
        partial_update_params['ENFANT'][0].pop('CONTACT')
        partial_update_params['maj:enfant_1'] = False
        resource.pre_sanitize_update_family_data(post_data=partial_update_params)
        jsonschema.validate(partial_update_params, json_schema)
        assert 'CONTACT' not in partial_update_params['ENFANT'][0]

    # third contact of first child is not complete
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'CONTACT' in partial_update_params['ENFANT'][0]
    assert len(partial_update_params['ENFANT'][0]['CONTACT']) == 2


def test_pre_sanitize_update_family_data_enfant_n_sanitaire(app, resource, update_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    # test maj:enfant_n_sanitaire
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1_sanitaire'] = False
    for key in partial_update_params['ENFANT'][1]['SANITAIRE'].keys():
        partial_update_params['ENFANT'][1]['SANITAIRE'][key] = None  # reset fields
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "3535"
    assert 'SANITAIRE' not in partial_update_params['ENFANT'][0]
    # combine with maj:enfant_n
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    partial_update_params['maj:enfant_1_sanitaire'] = False
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'ENFANT' not in partial_update_params

    # test maj:enfant_n_sanitaire not set
    for val in [None, '']:
        partial_update_params = copy.deepcopy(update_params)
        partial_update_params['maj:enfant_0_sanitaire'] = val
        partial_update_params['maj:enfant_1_sanitaire'] = val
        partial_update_params['ENFANT'][0].pop('SANITAIRE')
        partial_update_params['ENFANT'][1].pop('SANITAIRE')
        resource.pre_sanitize_update_family_data(post_data=partial_update_params)
        jsonschema.validate(partial_update_params, json_schema)
        assert 'SANITAIRE' not in partial_update_params['ENFANT'][0]
        assert 'SANITAIRE' not in partial_update_params['ENFANT'][1]


def test_sanitize_update_family_data_enfant_n_sanitaire_allergie(app, resource, update_params, family_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    # test maj:enfant_n_sanitaire_allergie
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_0_sanitaire_allergie'] = False
    for key in partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE'].keys():
        partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE'][key] = None  # reset fields
    partial_update_params['maj:enfant_1'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert '_to_reset' in partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "4242"
    assert 'ALLERGIE' in partial_update_params['ENFANT'][0]['SANITAIRE']
    # fields were set with origin values found in Axel
    new_values = partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']
    old_values = family_data['ENFANT'][0]['SANITAIRE']['ALLERGIE']
    assert new_values == old_values
    # combine with maj:enfant_n_sanitaire
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1_sanitaire'] = False
    partial_update_params['maj:enfant_1_sanitaire_allergie'] = False
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "3535"
    assert 'SANITAIRE' not in partial_update_params['ENFANT'][0]
    # combine with maj:enfant_n
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    partial_update_params['maj:enfant_1_sanitaire_allergie'] = False
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
    assert 'ENFANT' not in partial_update_params

    # test maj:enfant_n_sanitaire_allergie not set
    for val in [None, '']:
        partial_update_params = copy.deepcopy(update_params)
        partial_update_params['maj:enfant_0_sanitaire_allergie'] = val
        partial_update_params['maj:enfant_1'] = False
        resource.pre_sanitize_update_family_data(post_data=partial_update_params)
        jsonschema.validate(partial_update_params, json_schema)
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
        ):
            resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
        new_values = partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']
        old_values = family_data['ENFANT'][0]['SANITAIRE']['ALLERGIE']
        assert new_values == old_values

    # allergie field is required
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_0_sanitaire_allergie'] = False
    partial_update_params['ENFANT'][0]['SANITAIRE'].pop('ALLERGIE')
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    with pytest.raises(jsonschema.exceptions.ValidationError, match="'ALLERGIE' is a required property"):
        jsonschema.validate(partial_update_params, json_schema)


def test_sanitize_update_family_data_enfant_n_sanitaire_allergie_values(
    app, resource, update_params, family_data
):
    json_schema = schemas.UPDATE_FAMILY_SCHEMA
    # check values
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['ASTHME'] = 'NON'
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['MEDICAMENTEUSES'] = '0'
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['ALIMENTAIRES'] = 'False'
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['AUTRES'] = ''
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
    assert 'ALLERGIE' not in partial_update_params['ENFANT'][0]['SANITAIRE']

    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['ASTHME'] = False
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['MEDICAMENTEUSES'] = ''
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['ALIMENTAIRES'] = None
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['AUTRES'] = ''
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
    assert 'ALLERGIE' not in partial_update_params['ENFANT'][0]['SANITAIRE']

    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['ASTHME'] = 'OUI'
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['MEDICAMENTEUSES'] = '1'
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['ALIMENTAIRES'] = 'True'
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['AUTRES'] = 'accariens'
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
    assert partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE'] == [
        {'TYPE': 'ASTHME', 'ALLERGIQUE': 'OUI', 'NOMALLERGIE': None},
        {'TYPE': 'MEDICAMENTEUSES', 'ALLERGIQUE': 'OUI', 'NOMALLERGIE': None},
        {'TYPE': 'ALIMENTAIRES', 'ALLERGIQUE': 'OUI', 'NOMALLERGIE': None},
        {'TYPE': 'AUTRES', 'ALLERGIQUE': 'OUI', 'NOMALLERGIE': 'ACCARIENS'},
    ]

    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['ASTHME'] = ''
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['MEDICAMENTEUSES'] = '1'
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['ALIMENTAIRES'] = 'True'
    partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['AUTRES'] = ''
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
    assert partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE'] == [
        {'TYPE': 'MEDICAMENTEUSES', 'ALLERGIQUE': 'OUI', 'NOMALLERGIE': None},
        {'TYPE': 'ALIMENTAIRES', 'ALLERGIQUE': 'OUI', 'NOMALLERGIE': None},
    ]


def test_pre_sanitize_update_family_data_enfant_n_sanitaire_medecin(app, resource, update_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    # test maj:enfant_n_sanitaire_medecin
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1_sanitaire_medecin'] = False
    for key in partial_update_params['ENFANT'][1]['SANITAIRE']['MEDECIN'].keys():
        partial_update_params['ENFANT'][1]['SANITAIRE']['MEDECIN'][key] = None  # reset fields
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "3535"
    assert 'MEDECIN' not in partial_update_params['ENFANT'][0]['SANITAIRE']
    # combine with maj:enfant_n_sanitaire
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1_sanitaire'] = False
    partial_update_params['maj:enfant_1_sanitaire_medecin'] = False
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "3535"
    assert 'SANITAIRE' not in partial_update_params['ENFANT'][0]
    # combine with maj:enfant_n
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    partial_update_params['maj:enfant_1_sanitaire_medecin'] = False
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'ENFANT' not in partial_update_params

    # test maj:enfant_n_sanitaire_medecin not set
    for val in [None, '']:
        partial_update_params = copy.deepcopy(update_params)
        partial_update_params['maj:enfant_0_sanitaire_medecin'] = val
        partial_update_params['maj:enfant_1_sanitaire_medecin'] = val
        partial_update_params['ENFANT'][0]['SANITAIRE'].pop('MEDECIN')
        partial_update_params['ENFANT'][1]['SANITAIRE'].pop('MEDECIN')
        resource.pre_sanitize_update_family_data(post_data=partial_update_params)
        jsonschema.validate(partial_update_params, json_schema)
        assert 'MEDECIN' not in partial_update_params['ENFANT'][0]['SANITAIRE']
        assert 'MEDECIN' not in partial_update_params['ENFANT'][1]['SANITAIRE']


def test_pre_sanitize_update_family_data_enfant_n_sanitaire_vaccin(app, resource, update_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    # test maj:enfant_n_sanitaire_vaccin
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1_sanitaire_vaccin'] = False
    for key in partial_update_params['ENFANT'][1]['SANITAIRE']['VACCIN'].keys():
        partial_update_params['ENFANT'][1]['SANITAIRE']['VACCIN'][key] = None  # reset fields
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "3535"
    assert 'VACCIN' not in partial_update_params['ENFANT'][0]['SANITAIRE']
    # combine with maj:enfant_n_sanitaire
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1_sanitaire'] = False
    partial_update_params['maj:enfant_1_sanitaire_vaccin'] = False
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "3535"
    assert 'SANITAIRE' not in partial_update_params['ENFANT'][0]
    # combine with maj:enfant_n
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    partial_update_params['maj:enfant_1_sanitaire_vaccin'] = False
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert 'ENFANT' not in partial_update_params

    # test maj:enfant_n_sanitaire_vaccin not set
    for val in [None, '']:
        partial_update_params = copy.deepcopy(update_params)
        partial_update_params['maj:enfant_0_sanitaire_vaccin'] = val
        partial_update_params['maj:enfant_1_sanitaire_vaccin'] = val
        partial_update_params['ENFANT'][0]['SANITAIRE'].pop('VACCIN')
        partial_update_params['ENFANT'][1]['SANITAIRE'].pop('VACCIN')
        resource.pre_sanitize_update_family_data(post_data=partial_update_params)
        jsonschema.validate(partial_update_params, json_schema)
        assert 'VACCIN' not in partial_update_params['ENFANT'][0]['SANITAIRE']
        assert 'VACCIN' not in partial_update_params['ENFANT'][1]['SANITAIRE']


def test_sanitize_update_family_data_enfant_n_sanitaire_handicap(app, resource, update_params, family_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    # test maj:enfant_n_sanitaire_handicap
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_0_sanitaire_handicap'] = False
    # reset fields
    handicap_fields = [
        'AUTREDIFFICULTE',
        'ECOLESPECIALISEE',
        'INDICATEURAUXILIAIREVS',
        'INDICATEURECOLE',
        'INDICATEURHANDICAP',
        'INDICATEURNOTIFMDPH',
    ]
    for key in handicap_fields:
        partial_update_params['ENFANT'][0]['SANITAIRE']['HANDICAP'][key] = None
    partial_update_params['maj:enfant_1'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    assert '_to_reset' in partial_update_params['ENFANT'][0]['SANITAIRE']['HANDICAP']
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "4242"
    # fields were set with origin values found in Axel
    new_values = partial_update_params['ENFANT'][0]['SANITAIRE']
    for key in handicap_fields:
        assert new_values[key] == family_data['ENFANT'][0]['SANITAIRE'][key]
    assert '_to_reset' not in new_values
    assert 'HANDICAP' not in new_values
    # combine with maj:enfant_n_sanitaire
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1_sanitaire'] = False
    partial_update_params['maj:enfant_1_sanitaire_handicap'] = False
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
    assert len(partial_update_params['ENFANT']) == 1
    assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "3535"
    assert 'SANITAIRE' not in partial_update_params['ENFANT'][0]
    # combine with maj:enfant_n
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_1'] = False
    partial_update_params['maj:enfant_1_sanitaire_handicap'] = False
    partial_update_params['maj:enfant_0'] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
    ):
        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
    assert 'ENFANT' not in partial_update_params

    # test maj:enfant_n_sanitaire_handicap not set
    for val in [None, '']:
        partial_update_params = copy.deepcopy(update_params)
        partial_update_params['maj:enfant_0_sanitaire_handicap'] = val
        partial_update_params['maj:enfant_1'] = False
        # reset fields
        for key in handicap_fields:
            partial_update_params['ENFANT'][0]['SANITAIRE']['HANDICAP'][key] = None
        resource.pre_sanitize_update_family_data(post_data=partial_update_params)
        jsonschema.validate(partial_update_params, json_schema)
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data
        ):
            resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
        new_values = partial_update_params['ENFANT'][0]['SANITAIRE']
        for key in handicap_fields:
            assert new_values[key] == family_data['ENFANT'][0]['SANITAIRE'][key]
        assert 'HANDICAP' not in new_values

    # handicap field is required
    partial_update_params = copy.deepcopy(update_params)
    partial_update_params['maj:enfant_0_sanitaire_handicap'] = False
    partial_update_params['ENFANT'][0]['SANITAIRE'].pop('HANDICAP')
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    with pytest.raises(jsonschema.exceptions.ValidationError, match="'HANDICAP' is a required property"):
        jsonschema.validate(partial_update_params, json_schema)


@pytest.mark.parametrize(
    'flags',
    [
        # get family data to fill handicap fields
        ['maj:enfant_0_sanitaire_handicap', 'maj:revenus', 'maj:rl1', 'maj:rl2'],
        # get family data to fill revenus fields
        ['maj:rl1', 'maj:rl2'],
        # get family data to fill rl1 fields
        ['maj:revenus', 'maj:rl2'],
        # get family data to fill rl2 fields
        ['maj:revenus', 'maj:rl1'],
    ],
)
def test_sanitize_update_family_data_axel_error(app, resource, update_params, flags):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    json_schema = schemas.UPDATE_FAMILY_SCHEMA

    partial_update_params = copy.deepcopy(update_params)
    for flag in flags:
        partial_update_params[flag] = False
    resource.pre_sanitize_update_family_data(post_data=partial_update_params)
    jsonschema.validate(partial_update_params, json_schema)
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_famille_dui') as operation:
        operation.side_effect = AxelError('FooBar')
        with pytest.raises(APIError, match='Axel error: FooBar'):
            resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)


def test_update_family_info_endpoint_sanitize_axel_error(app, resource, update_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.sanitize_update_family_data'
    ) as sanitize:
        sanitize.side_effect = APIError('Axel error: FooBar')
        resp = app.post_json('/toulouse-axel/test/update_family_info?NameID=yyy', params=update_params)
        assert resp.json['err_desc'] == "Axel error: FooBar"


def test_invoices_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_facture_a_payer') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices?NameID=yyy')
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'

    resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices?NameID=', status=400)
    assert 'InvalidParameterValue' in resp.json['err_class']
    assert resp.json['err'] == 1


def test_invoices_endpoint_no_result(app, resource):
    resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices?NameID=yyy')
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'


def test_invoices_endpoint_no_invoices(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    content = '''<PORTAIL>
    <DUI>
        <IDDUI>XXX</IDDUI>
        <CHOIXDEMAT>0</CHOIXDEMAT>
        <NBFACTURES>0</NBFACTURES>
    </DUI>
</PORTAIL>'''
    with mock_getdata(content, 'RefFactureAPayer'):
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices?NameID=yyy')
    assert resp.json['err'] == 0
    assert resp.json['data'] == []


def test_invoices_endpoint(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFactureAPayer'):
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices?NameID=yyy')
    assert resp.json['err'] == 0
    assert resp.json['data'] == [
        {
            'id': 'XXX-42',
            'display_id': '42',
            'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
            'amount': '4.94',
            'total_amount': '44.94',
            'amount_paid': '40.00',
            'online_payment': True,
            'created': '2019-11-12',
            'pay_limit_date': '2019-12-04',
            'has_pdf': True,
            'paid': False,
            'vendor': {
                'toulouse-axel': {
                    'IDFACTURATION': '4242-35AA',
                    'IDFACTURE': 42,
                    'IDREGIE': 'MAREGIE',
                    'DATEECHEANCE': '2019-12-04',
                    'DATEEMISSION': '2019-11-12',
                    'EXISTEPDF': '1',
                    'LIBELLE': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
                    'MONTANTTOTAL': '44.94',
                    'NUMFACTURE': 42,
                    'RESTEAPAYER': '4.94',
                }
            },
        },
        {
            'id': 'XXX-43',
            'display_id': '43',
            'label': 'PRESTATIONS PERISCOLAIRES NOVEMBRE 2019',
            'amount': '44.94',
            'total_amount': '44.94',
            'amount_paid': '',
            'online_payment': True,
            'created': '2019-12-12',
            'pay_limit_date': '2020-01-04',
            'has_pdf': False,
            'paid': False,
            'vendor': {
                'toulouse-axel': {
                    'IDFACTURATION': '4243-35AA',
                    'IDFACTURE': 43,
                    'DATEECHEANCE': '2020-01-04',
                    'DATEEMISSION': '2019-12-12',
                    'EXISTEPDF': '0',
                    'IDREGIE': 'MAREGIE',
                    'LIBELLE': 'PRESTATIONS PERISCOLAIRES NOVEMBRE 2019',
                    'MONTANTTOTAL': '44.94',
                    'NUMFACTURE': 43,
                    'RESTEAPAYER': '44.94',
                }
            },
        },
    ]
    with mock_getdata(content, 'RefFactureAPayer'):
        resp = app.get('/toulouse-axel/test/regie/AUTREREGIE/invoices?NameID=yyy')
    assert resp.json['err'] == 0
    assert resp.json['data'] == [
        {
            'id': 'XXX-44',
            'display_id': '44',
            'label': 'PRESTATIONS PERISCOLAIRES DECEMBRE 2019',
            'amount': '44.94',
            'total_amount': '44.94',
            'amount_paid': '',
            'online_payment': True,
            'created': '2020-01-12',
            'pay_limit_date': '2020-01-15',
            'has_pdf': True,
            'paid': False,
            'vendor': {
                'toulouse-axel': {
                    'IDFACTURATION': '4244-35AA',
                    'IDFACTURE': 44,
                    'IDREGIE': 'AUTREREGIE',
                    'DATEECHEANCE': '2020-01-15',
                    'DATEEMISSION': '2020-01-12',
                    'EXISTEPDF': '1',
                    'LIBELLE': 'PRESTATIONS PERISCOLAIRES DECEMBRE 2019',
                    'MONTANTTOTAL': '44.94',
                    'NUMFACTURE': 44,
                    'RESTEAPAYER': '44.94',
                }
            },
        }
    ]


def test_invoices_history_endpoint_empty_nameid_error(app, resource):
    resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices/history?NameID=', status=400)
    assert resp.json['err_desc'] == 'invalid value for parameter "NameID"'
    assert resp.json['err'] == 1


def test_invoices_history_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.list_dui_factures') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices/history?NameID=yyy')
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


def test_invoices_history_endpoint_no_result(app, resource):
    resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices/history?NameID=yyy')
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'


def test_invoices_history_endpoint_no_invoices(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    content = '''<PORTAIL>
    <LISTFACTURE>
        <NBFACTURERESTANTE>0</NBFACTURERESTANTE>
    </LISTFACTURE>
</PORTAIL>'''
    with mock_getdata(content, 'ListeDuiFacturesPayeesRecettees'):
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices/history?NameID=yyy')
    assert resp.json['err'] == 0
    assert resp.json['data'] == []


def test_invoices_history_endpoint(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices_history.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'ListeDuiFacturesPayeesRecettees'):
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices/history?NameID=yyy')
    assert resp.json['err'] == 0
    assert resp.json['data'] == [
        {
            'amount': 0,
            'created': '2017-03-23',
            'display_id': '42',
            'has_pdf': True,
            'id': 'historical-XXX-42',
            'label': 'PRESTATIONS SEPTEMBRE 2015',
            'online_payment': False,
            'paid': False,
            'pay_limit_date': '',
            'total_amount': '28.98',
            'vendor': {
                'toulouse-axel': {
                    'EMISSION': '2017-03-23',
                    'IDAXEL': 'AXEL',
                    'IDDIRECTION': 'DIR-A',
                    'IDFACTURE': 42,
                    'IDFAMILLE': 'XXX',
                    'IPDF': 'O',
                    'LIBDIRECTION': 'DIRECTION A',
                    'LIBELLE': 'PRESTATIONS SEPTEMBRE 2015',
                    'MONTANT': '28.98',
                    'NOFACTURE': 42,
                    'NUMDIRECTION': 10,
                }
            },
        },
        {
            'amount': 0,
            'created': '2017-03-23',
            'display_id': '43',
            'has_pdf': False,
            'id': 'historical-XXX-43',
            'label': 'PRESTATIONS OCTOBRE 2015',
            'online_payment': False,
            'paid': False,
            'pay_limit_date': '',
            'total_amount': '28.98',
            'vendor': {
                'toulouse-axel': {
                    'EMISSION': '2017-03-23',
                    'IDAXEL': 'AXEL',
                    'IDDIRECTION': 'DIR-B',
                    'IDFACTURE': 43,
                    'IDFAMILLE': 'XXX',
                    'IPDF': 'N',
                    'LIBDIRECTION': 'DIRECTION B',
                    'LIBELLE': 'PRESTATIONS OCTOBRE 2015',
                    'MONTANT': '28.98',
                    'NOFACTURE': 43,
                    'NUMDIRECTION': 11,
                }
            },
        },
    ]


def test_invoice_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_facture_a_payer') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42?NameID=yyy')
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


def test_invoice_endpoint_no_result(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFactureAPayer'):
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-35?NameID=yyy')
    assert resp.json['err_desc'] == "Invoice not found"
    assert resp.json['err'] == 'not-found'
    with mock_getdata(content, 'RefFactureAPayer'):
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-44?NameID=yyy')
    assert resp.json['err_desc'] == "Invoice not found"
    assert resp.json['err'] == 'not-found'


@pytest.mark.parametrize('dui', ['XXX', 'S-XXX'])
def test_invoice_endpoint(app, resource, dui):
    Link.objects.create(resource=resource, name_id='yyy', dui=dui, person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
    with open(filepath) as xml:
        content = xml.read().replace('XXX', dui)
    with mock_getdata(content, 'RefFactureAPayer'):
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/%s-42?NameID=yyy' % dui)
    assert resp.json['err'] == 0
    assert resp.json['data'] == {
        'id': '%s-42' % dui,
        'display_id': '42',
        'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
        'amount': '4.94',
        'total_amount': '44.94',
        'amount_paid': '40.00',
        'online_payment': True,
        'created': '2019-11-12',
        'pay_limit_date': '2019-12-04',
        'has_pdf': True,
        'paid': False,
        'vendor': {
            'toulouse-axel': {
                'IDFACTURATION': '4242-35AA',
                'IDFACTURE': 42,
                'IDREGIE': 'MAREGIE',
                'DATEECHEANCE': '2019-12-04',
                'DATEEMISSION': '2019-11-12',
                'EXISTEPDF': '1',
                'LIBELLE': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
                'MONTANTTOTAL': '44.94',
                'NUMFACTURE': 42,
                'RESTEAPAYER': '4.94',
            }
        },
    }

    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices_history.xml')
    with open(filepath) as xml:
        content = xml.read().replace('XXX', dui)
    with mock_getdata(content, 'ListeDuiFacturesPayeesRecettees'):
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/historical-%s-42?NameID=yyy' % dui)
    assert resp.json['err'] == 0
    assert resp.json['data'] == {
        'amount': 0,
        'created': '2017-03-23',
        'display_id': '42',
        'has_pdf': True,
        'id': 'historical-%s-42' % dui,
        'label': 'PRESTATIONS SEPTEMBRE 2015',
        'online_payment': False,
        'paid': False,
        'pay_limit_date': '',
        'total_amount': '28.98',
        'vendor': {
            'toulouse-axel': {
                'EMISSION': '2017-03-23',
                'IDAXEL': 'AXEL',
                'IDDIRECTION': 'DIR-A',
                'IDFACTURE': 42,
                'IDFAMILLE': dui,
                'IPDF': 'O',
                'LIBDIRECTION': 'DIRECTION A',
                'LIBELLE': 'PRESTATIONS SEPTEMBRE 2015',
                'MONTANT': '28.98',
                'NOFACTURE': 42,
                'NUMDIRECTION': 10,
            }
        },
    }


@pytest.mark.parametrize('dui', ['XXX', 'S-XXX'])
def test_invoice_endpoint_anonymous(app, resource, dui):
    Link.objects.create(resource=resource, name_id='yyy', dui=dui, person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
    with open(filepath) as xml:
        content = xml.read().replace('XXX', dui)
    with mock_getdata(content, 'RefFactureAPayer'):
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/%s-42' % dui)
    assert resp.json['err'] == 0
    assert resp.json['data'] == {
        'id': '%s-42' % dui,
        'display_id': '42',
        'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
        'amount': '4.94',
        'total_amount': '44.94',
        'amount_paid': '40.00',
        'online_payment': True,
        'created': '2019-11-12',
        'pay_limit_date': '2019-12-04',
        'has_pdf': True,
        'paid': False,
        'vendor': {
            'toulouse-axel': {
                'IDFACTURATION': '4242-35AA',
                'IDFACTURE': 42,
                'IDREGIE': 'MAREGIE',
                'DATEECHEANCE': '2019-12-04',
                'DATEEMISSION': '2019-11-12',
                'EXISTEPDF': '1',
                'LIBELLE': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
                'MONTANTTOTAL': '44.94',
                'NUMFACTURE': 42,
                'RESTEAPAYER': '4.94',
            }
        },
    }

    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices_history.xml')
    with open(filepath) as xml:
        content = xml.read().replace('XXX', dui)
    with mock_getdata(content, 'ListeDuiFacturesPayeesRecettees'):
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/historical-%s-42' % dui)
    assert resp.json['err'] == 0
    assert resp.json['data'] == {
        'amount': 0,
        'created': '2017-03-23',
        'display_id': '42',
        'has_pdf': True,
        'id': 'historical-%s-42' % dui,
        'label': 'PRESTATIONS SEPTEMBRE 2015',
        'online_payment': False,
        'paid': False,
        'pay_limit_date': '',
        'total_amount': '28.98',
        'vendor': {
            'toulouse-axel': {
                'EMISSION': '2017-03-23',
                'IDAXEL': 'AXEL',
                'IDDIRECTION': 'DIR-A',
                'IDFACTURE': 42,
                'IDFAMILLE': dui,
                'IPDF': 'O',
                'LIBDIRECTION': 'DIRECTION A',
                'LIBELLE': 'PRESTATIONS SEPTEMBRE 2015',
                'MONTANT': '28.98',
                'NOFACTURE': 42,
                'NUMDIRECTION': 10,
            }
        },
    }


def test_invoice_pdf_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_facture_a_payer') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pdf?NameID=yyy', status=404)
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'

    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFactureAPayer'):
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_facture_pdf') as operation:
            operation.side_effect = AxelError('FooBar')
            resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pdf?NameID=yyy', status=404)
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


def test_invoice_pdf_endpoint_no_result(app, resource):
    resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pdf?NameID=yyy', status=404)
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'

    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFactureAPayer'):
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-35/pdf?NameID=yyy', status=404)
    assert resp.json['err_desc'] == "Invoice not found"
    assert resp.json['err'] == 'not-found'

    with mock_getdata(content, 'RefFactureAPayer'):
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-44/pdf?NameID=yyy', status=404)
    assert resp.json['err_desc'] == "Invoice not found"
    assert resp.json['err'] == 'not-found'

    with mock_getdata(content, 'RefFactureAPayer'):
        resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-43/pdf?NameID=yyy', status=404)
    assert resp.json['err_desc'] == "PDF not available"
    assert resp.json['err'] == 'not-available'

    pdf_content = '''<PORTAIL>
    <PDF FILE=''></PDF>
</PORTAIL>'''
    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_invoice') as invoice:
        invoice.return_value = {'has_pdf': True, 'display_id': '42'}
        with mock_getdata(pdf_content, 'RefFacturePDF'):
            resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pdf?NameID=yyy', status=404)
    assert resp.json['err_desc'] == "PDF error"
    assert resp.json['err'] == 'error'


@pytest.mark.parametrize('dui', ['XXX', 'S-XXX'])
def test_invoice_pdf_endpoint(app, resource, dui):
    Link.objects.create(resource=resource, name_id='yyy', dui=dui, person_id='42')
    pdf_content = '''<PORTAIL>
    <PDF FILE='aGVsbG8gd29ybGQ='></PDF>
</PORTAIL>'''
    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_invoice') as invoice:
        invoice.return_value = {'has_pdf': True, 'display_id': '42'}
        with mock_getdata(pdf_content, 'RefFacturePDF'):
            app.get('/toulouse-axel/test/regie/MAREGIE/invoice/%s-42/pdf?NameID=yyy' % dui)
    assert invoice.call_args_list[0][1]['historical'] is False

    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_invoice') as invoice:
        invoice.return_value = {'has_pdf': True, 'display_id': '42'}
        with mock_getdata(pdf_content, 'RefFacturePDF'):
            app.get('/toulouse-axel/test/regie/MAREGIE/invoice/historical-%s-42/pdf?NameID=yyy' % dui)
    assert invoice.call_args_list[0][1]['historical'] is True


def test_pay_invoice_endpoint_axel_error(app, resource):
    payload = {
        'transaction_date': '2020-01-01T12:00:00',
        'transaction_id': 'foo',
    }
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.ref_facture_a_payer') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.post_json(
            '/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pay?NameID=yyy', params=payload
        )
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'

    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFactureAPayer'):
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.form_paiement_dui') as operation:
            operation.side_effect = AxelError('FooBar')
            resp = app.post_json(
                '/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pay?NameID=yyy', params=payload
            )
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


def test_pay_invoice_endpoint_no_result(app, resource):
    payload = {
        'transaction_date': '2020-01-01T12:00:00',
        'transaction_id': 'foo',
    }
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'RefFactureAPayer'):
        resp = app.post_json(
            '/toulouse-axel/test/regie/MAREGIE/invoice/XXX-35/pay?NameID=yyy', params=payload
        )
    assert resp.json['err_desc'] == "Invoice not found"
    assert resp.json['err'] == 'not-found'
    with mock_getdata(content, 'RefFactureAPayer'):
        resp = app.post_json(
            '/toulouse-axel/test/regie/MAREGIE/invoice/XXX-44/pay?NameID=yyy', params=payload
        )
    assert resp.json['err_desc'] == "Invoice not found"
    assert resp.json['err'] == 'not-found'


@pytest.mark.parametrize('dui', ['XXX', 'S-XXX'])
def test_pay_invoice_endpoint(app, resource, dui):
    payload = {
        'transaction_date': '2020-01-01T12:00:00',
        'transaction_id': 'foo',
    }
    Link.objects.create(resource=resource, name_id='yyy', dui=dui, person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
    with open(filepath) as xml:
        content = xml.read().replace('XXX', dui)
    with mock_getdata(content, 'RefFactureAPayer'):
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.form_paiement_dui') as operation:
            resp = app.post_json(
                '/toulouse-axel/test/regie/MAREGIE/invoice/%s-42/pay?NameID=yyy' % dui, params=payload
            )
    assert resp.json['err'] == 0
    assert resp.json['data'] is True
    assert operation.call_args_list[0][0][1] == {
        'PORTAIL': {
            'DUI': {
                'DATEPAIEMENT': '01/01/2020 13:00:00',
                'IDFACTURE': 42,
                'IDREGIEENCAISSEMENT': '',
                'MONTANTPAYE': decimal.Decimal('4.94'),
                'REFERENCE': 'foo',
            }
        }
    }


@freezegun.freeze_time('2020-06-30')
def test_clae_years_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.enfants_activites') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get('/toulouse-axel/test/clae_years?NameID=yyy&pivot_date=06-15')
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


@pytest.mark.parametrize('value', ['foo', '20/01/2020', '15/06', '06'])
def test_clae_years_endpoint_bad_date_format(app, resource, value):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    resp = app.get('/toulouse-axel/test/clae_years?NameID=yyy&pivot_date=%s' % value, status=400)
    assert resp.json['err_desc'] == "bad date format, should be MM-DD"
    assert resp.json['err'] == 'bad-request'


def test_clae_years_endpoint_no_result(app, resource):
    resp = app.get('/toulouse-axel/test/clae_years?NameID=yyy&pivot_date=06-15')
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'


@pytest.mark.parametrize(
    'today,pivot,next_year,next_date',
    [
        ('2019-08-01', '08-01', True, '2020-08-01'),
        ('2019-08-01', '08-02', False, None),
        ('2020-07-31', '07-31', True, '2021-07-31'),
        ('2020-07-30', '07-31', False, None),
        ('2020-06-14', '06-15', False, None),
        ('2020-06-15', '06-15', True, '2021-06-15'),
        ('2020-06-16', '06-15', True, '2021-06-16'),
        ('2020-02-29', '02-01', True, '2021-03-01'),
    ],
)
def test_clae_years_endpoint(app, resource, today, pivot, next_year, next_date):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'EnfantsActivites'):
        with freezegun.freeze_time(today):
            resp = app.get('/toulouse-axel/test/clae_years?NameID=yyy&pivot_date=%s' % pivot)
    assert resp.json['data'][0] == {'id': '2019', 'text': '2019/2020', 'type': 'encours', 'refdate': today}
    if next_year:
        assert len(resp.json['data']) == 2
        assert resp.json['data'][1] == {
            'id': '2020',
            'text': '2020/2021',
            'type': 'suivante',
            'refdate': next_date,
        }
    else:
        assert len(resp.json['data']) == 1


@freezegun.freeze_time('2020-06-30')
def test_clae_years_endpoint_noactivities(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_children_activities', return_value={}
    ) as mock_activities:
        resp = app.get('/toulouse-axel/test/clae_years?NameID=yyy&pivot_date=06-15')
    assert mock_activities.call_args_list == [mock.call(dui='XXX', reference_year=2020)]
    assert len(resp.json['data']) == 1


def test_clae_children_activities_info_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.enfants_activites') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get('/toulouse-axel/test/clae_children_activities_info?NameID=yyy&booking_date=2020-01-20')
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


@pytest.mark.parametrize('value', ['foo', '20/01/2020', '2020'])
def test_clae_children_activities_info_endpoint_bad_date_format(app, resource, value):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    resp = app.get(
        '/toulouse-axel/test/clae_children_activities_info?NameID=yyy&booking_date=%s' % value, status=400
    )
    assert resp.json['err_desc'] == "bad date format, should be YYYY-MM-DD"
    assert resp.json['err'] == 'bad-request'


def test_clae_children_activities_info_endpoint_no_result(app, resource):
    resp = app.get('/toulouse-axel/test/clae_children_activities_info?NameID=yyy&booking_date=2020-01-20')
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'


def test_clae_children_activities_info_endpoint(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'EnfantsActivites'):
        resp = app.get('/toulouse-axel/test/clae_children_activities_info?NameID=yyy&booking_date=2020-01-20')
    assert resp.json['err'] == 0
    assert resp.json['data'] == [
        {
            'ACTIVITE': [
                {
                    'COUTREVIENT': '99999',
                    'DATEDEBUT': '2019-08-01',
                    'DATEENTREE': '2019-08-01',
                    'DATEFIN': '2020-07-31',
                    'DATESORTIE': '2020-07-31',
                    'IDACTIVITE': 'A19P1M1',
                    'ISPAI': False,
                    'LIBELLEACTIVITE': 'Temps du matin',
                    'TARIF': '0.42',
                    'TYPEACTIVITE': 'MAT',
                },
                {
                    'COUTREVIENT': '99999',
                    'DATEDEBUT': '2019-08-01',
                    'DATEENTREE': '2019-08-01',
                    'DATEFIN': '2020-07-31',
                    'DATESORTIE': '2020-07-31',
                    'IDACTIVITE': 'A19P1M2',
                    'ISPAI': False,
                    'LIBELLEACTIVITE': 'Temps du midi',
                    'TARIF': '0.43',
                    'TYPEACTIVITE': 'MIDI',
                },
                {
                    'COUTREVIENT': '99999',
                    'DATEDEBUT': '2019-08-01',
                    'DATEENTREE': '2019-08-01',
                    'DATEFIN': '2020-07-31',
                    'DATESORTIE': '2020-07-31',
                    'IDACTIVITE': 'A19P1M3',
                    'ISPAI': False,
                    'LIBELLEACTIVITE': 'Temps du soir',
                    'TARIF': '0.44',
                    'TYPEACTIVITE': 'SOIR',
                },
                {
                    'COUTREVIENT': '99999',
                    'DATEDEBUT': '2019-08-01',
                    'DATEENTREE': '2019-08-01',
                    'DATEFIN': '2020-07-31',
                    'DATESORTIE': '2020-07-31',
                    'IDACTIVITE': 'A19P1M4',
                    'ISPAI': False,
                    'LIBELLEACTIVITE': 'Temps mercredi apr\xe8s midi',
                    'TARIF': '0.45',
                    'TYPEACTIVITE': 'GARD',
                },
            ],
            'DATENAISSANCE': '2019-01-01',
            'IDPERSONNE': '3535',
            'LIBELLEECOLE': 'MATERNELLE 1',
            'NOM': 'foo',
            'PRENOM': 'foo',
            'REGIME': 'SV',
            'REGIME_label': 'Menu sans viande',
            'id': '3535',
            'text': 'foo foo',
        }
    ]

    # again - data are in cache
    resp = app.get('/toulouse-axel/test/clae_children_activities_info?NameID=yyy&booking_date=2020-01-20')
    assert resp.json['err'] == 0


@pytest.mark.parametrize(
    'activities, expected',
    [
        # all activities except GARD - optional
        (['MAT', 'MIDI', 'SOIR'], True),
        # all activities
        (['MAT', 'MIDI', 'SOIR', 'GARD'], True),
        # duplicated activities
        (['MAT', 'MAT', 'MIDI', 'SOIR', 'GARD'], False),
        (['MAT', 'MIDI', 'MIDI', 'SOIR', 'GARD'], False),
        (['MAT', 'MIDI', 'SOIR', 'SOIR', 'GARD'], False),
        (['MAT', 'MIDI', 'SOIR', 'GARD', 'GARD'], False),
        # missing activity
        (['MIDI', 'SOIR'], False),
        (['MAT', 'SOIR'], False),
        (['MAT', 'MIDI'], False),
    ],
)
def test_clae_children_activities_info_check(app, resource, activities, expected):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    result = []
    for activity in activities:
        result.append({'IDACTIVITE': 'foo%s' % activity, 'TYPEACTIVITE': activity})
    result = {
        'DATA': {
            'PORTAIL': {
                'DUI': {
                    'ENFANT': [
                        {
                            'IDPERSONNE': '42',
                            'NOM': '',
                            'PRENOM': '',
                            'REGIME': '',
                            'LIBELLEECOLE': '',
                            'ACTIVITE': result,
                        }
                    ]
                }
            }
        }
    }
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.enfants_activites') as operation:
        operation.return_value = OperationResult(json_response=result, xml_request='', xml_response='')
        resp = app.get('/toulouse-axel/test/clae_children_activities_info?NameID=yyy&booking_date=2020-01-20')
    assert resp.json['err'] == 0
    assert len(resp.json['data']) == int(expected)


def test_clae_booking_activities_info_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.enfants_activites') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activities_info?NameID=yyy&idpersonne=3535&start_date=2020-01-20&end_date=2020-01-24'
        )
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'

    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'EnfantsActivites'):
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_periode') as operation:
            operation.side_effect = AxelError('FooBar')
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activities_info?NameID=yyy&idpersonne=3535&start_date=2020-01-20&end_date=2020-01-24'
            )
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


@pytest.mark.parametrize('value', ['foo', '20/01/2020', '2020'])
def test_clae_booking_activities_info_endpoint_bad_date_format(app, resource, value):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activities_info?NameID=yyy&idpersonne=3535&start_date=%s&end_date=2020-01-24'
        % value,
        status=400,
    )
    assert resp.json['err_desc'] == "bad date format, should be YYYY-MM-DD"
    assert resp.json['err'] == 'bad-request'
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activities_info?NameID=yyy&idpersonne=3535&start_date=2020-01-20&end_date=%s'
        % value,
        status=400,
    )
    assert resp.json['err_desc'] == "bad date format, should be YYYY-MM-DD"
    assert resp.json['err'] == 'bad-request'


def test_clae_booking_activities_info_endpoint_no_result(app, resource, child_activities_data):
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activities_info?NameID=yyy&idpersonne=3535&start_date=2020-01-20&end_date=2020-01-24'
    )
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'

    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'EnfantsActivites'):
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_periode') as operation:
            operation.side_effect = AxelError('FooBar')
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activities_info?NameID=yyy&idpersonne=4242&start_date=2020-01-20&end_date=2020-01-24'
            )
    assert resp.json['err_desc'] == "Child not found"
    assert resp.json['err'] == 'not-found'

    content = """<PORTAIL>
  <DUI>
    <IDDUI>XXX</IDDUI>
    <ENFANT>
      <IDPERSONNE>4242</IDPERSONNE>
      <ACTIVITE>
        <ANNEEREFERENCE>2019</ANNEEREFERENCE>
        <IDACTIVITE>A19P1M1</IDACTIVITE>
        <JOUR>00000</JOUR>
      </ACTIVITE>
    </ENFANT>
  </DUI>
</PORTAIL>"""
    activities = child_activities_data['ENFANT'][0]
    with mock_getdata(content, 'ReservationPeriode'):
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities',
            return_value=activities,
        ):
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activities_info?NameID=yyy&idpersonne=3535&start_date=2020-01-20&end_date=2020-01-24'
            )
    assert resp.json['err_desc'] == "Child not found"
    assert resp.json['err'] == 'not-found'


def test_clae_booking_activities_info_endpoint(app, resource, child_activities_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/booking_info.xml')
    with open(filepath) as xml:
        content = xml.read()
    activities = copy.deepcopy(child_activities_data['ENFANT'][0])
    with mock_getdata(content, 'ReservationPeriode'):
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities',
            return_value=activities,
        ):
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activities_info?NameID=yyy&idpersonne=3535&start_date=2020-01-20&end_date=2020-01-24'
            )
    assert resp.json['err'] == 0
    assert resp.json['data'] == [
        {
            'day': '2020-01-20',
            'activity_id': 'A19P1M1',
            'activity_type': 'MAT',
            'activity_label': 'Temps du matin',
            'booked': False,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-20',
            'activity_id': 'A19P1M2',
            'activity_type': 'MIDI',
            'activity_label': 'Temps du midi',
            'booked': True,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-20',
            'activity_id': 'A19P1M3',
            'activity_type': 'SOIR',
            'activity_label': 'Temps du soir',
            'booked': True,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-21',
            'activity_id': 'A19P1M1',
            'activity_type': 'MAT',
            'activity_label': 'Temps du matin',
            'booked': False,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-21',
            'activity_id': 'A19P1M2',
            'activity_type': 'MIDI',
            'activity_label': 'Temps du midi',
            'booked': True,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-21',
            'activity_id': 'A19P1M3',
            'activity_type': 'SOIR',
            'activity_label': 'Temps du soir',
            'booked': True,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-22',
            'activity_id': 'A19P1M1',
            'activity_type': 'MAT',
            'activity_label': 'Temps du matin',
            'booked': False,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-22',
            'activity_id': 'A19P1M2',
            'activity_type': 'MIDI',
            'activity_label': 'Temps du midi',
            'booked': True,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-22',
            'activity_id': 'A19P1M4',
            'activity_type': 'GARD',
            'activity_label': 'Temps mercredi après midi',
            'booked': True,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-23',
            'activity_id': 'A19P1M1',
            'activity_type': 'MAT',
            'activity_label': 'Temps du matin',
            'booked': False,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-23',
            'activity_id': 'A19P1M2',
            'activity_type': 'MIDI',
            'activity_label': 'Temps du midi',
            'booked': True,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-23',
            'activity_id': 'A19P1M3',
            'activity_type': 'SOIR',
            'activity_label': 'Temps du soir',
            'booked': True,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-24',
            'activity_id': 'A19P1M1',
            'activity_type': 'MAT',
            'activity_label': 'Temps du matin',
            'booked': False,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-24',
            'activity_id': 'A19P1M2',
            'activity_type': 'MIDI',
            'activity_label': 'Temps du midi',
            'booked': True,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-24',
            'activity_id': 'A19P1M3',
            'activity_type': 'SOIR',
            'activity_label': 'Temps du soir',
            'booked': True,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
    ]

    content = """<PORTAIL>
  <DUI>
    <IDDUI>XXX</IDDUI>
    <ENFANT>
      <IDPERSONNE>3535</IDPERSONNE>
      <ACTIVITE>
        <ANNEEREFERENCE>2019</ANNEEREFERENCE>
        <IDACTIVITE>A19P1M1</IDACTIVITE>
        <JOUR>10201</JOUR>
      </ACTIVITE>
    </ENFANT>
  </DUI>
</PORTAIL>"""
    cache.clear()
    activities = copy.deepcopy(child_activities_data['ENFANT'][0])
    activities['ACTIVITE'] = activities['ACTIVITE'][:1]
    with mock_getdata(content, 'ReservationPeriode'):
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities',
            return_value=activities,
        ):
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activities_info?NameID=yyy&idpersonne=3535&start_date=2020-01-20&end_date=2020-01-24'
            )
    assert resp.json['err'] == 0
    assert resp.json['data'] == [
        {
            'day': '2020-01-20',
            'activity_id': 'A19P1M1',
            'activity_type': 'MAT',
            'activity_label': 'Temps du matin',
            'booked': True,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-21',
            'activity_id': 'A19P1M1',
            'activity_type': 'MAT',
            'activity_label': 'Temps du matin',
            'booked': False,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-23',
            'activity_id': 'A19P1M1',
            'activity_type': 'MAT',
            'activity_label': 'Temps du matin',
            'booked': False,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
        {
            'day': '2020-01-24',
            'activity_id': 'A19P1M1',
            'activity_type': 'MAT',
            'activity_label': 'Temps du matin',
            'booked': True,
            'bookable': False,
            'week': 'week:2020-01-20:2020-01-24',
        },
    ]


@freezegun.freeze_time('2020-03-26')
@pytest.mark.parametrize(
    'start_date, end_date, last_date',
    [
        # end date is a not a friday
        ('2020-04-07', '2020-04-13', '2020-04-13'),
        # except if end date is saturday or sunday => ends on previous friday
        ('2020-04-07', '2020-04-18', '2020-04-17'),
        ('2020-04-07', '2020-04-19', '2020-04-17'),
        # start date is after end date, same week => result is empty
        ('2020-04-16', '2020-04-15', None),
        # start date is after end date, not the same week => result is empty
        ('2020-04-16', '2020-04-12', None),
    ],
)
def test_clae_booking_activities_info_period(app, resource, start_date, end_date, last_date):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')

    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_booking_data'
    ) as activities_info:
        activities_info.return_value = {
            'ACTIVITE': [
                {
                    'id': 'TOTO',
                    'booking': {
                        'days': {
                            'monday': True,
                            'tuesday': True,
                            'wednesday': True,
                            'thursday': True,
                            'friday': True,
                        }
                    },
                    'TYPEACTIVITE': 'MAT',
                    'LIBELLEACTIVITE': 'Matin',
                }
            ]
        }
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activities_info?NameID=yyy&idpersonne=3535'
            '&start_date=%s&end_date=%s' % (start_date, end_date)
        )
    if last_date is None:
        assert resp.json['data'] == []
    else:
        assert resp.json['data'][0]['day'] == start_date
        assert resp.json['data'][-1]['day'] == last_date


@freezegun.freeze_time('2020-04-15')
def test_clae_booking_activities_info_bookable(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')

    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_booking_data'
    ) as activities_info:
        activities_info.return_value = {
            'ACTIVITE': [
                {
                    'id': 'TOTO',
                    'booking': {
                        'days': {
                            'monday': True,
                            'tuesday': True,
                            'wednesday': True,
                            'thursday': True,
                            'friday': True,
                        }
                    },
                    'TYPEACTIVITE': 'MAT',
                    'LIBELLEACTIVITE': 'Matin',
                }
            ]
        }
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activities_info?NameID=yyy&idpersonne=3535'
            '&start_date=2020-04-01&end_date=2020-04-30'
        )

    activities = {a['day']: a['bookable'] for a in resp.json['data']}
    assert activities['2020-04-01'] is False
    assert activities['2020-04-02'] is False
    assert activities['2020-04-03'] is False
    assert activities['2020-04-06'] is False
    assert activities['2020-04-07'] is False
    assert activities['2020-04-08'] is False
    assert activities['2020-04-09'] is False
    assert activities['2020-04-10'] is False
    assert activities['2020-04-13'] is False
    assert activities['2020-04-14'] is False
    assert activities['2020-04-15'] is False
    assert activities['2020-04-16'] is False
    assert activities['2020-04-17'] is False
    assert activities['2020-04-20'] is False
    assert activities['2020-04-21'] is False
    assert activities['2020-04-22'] is False
    assert activities['2020-04-23'] is True
    assert activities['2020-04-24'] is True
    assert activities['2020-04-27'] is True
    assert activities['2020-04-28'] is True
    assert activities['2020-04-29'] is True
    assert activities['2020-04-30'] is True


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_activity_possible_days_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.enfants_activites') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
            '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
        )
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'

    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'EnfantsActivites'):
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_periode') as operation:
            operation.side_effect = AxelError('FooBar')
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
                '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
            )
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


@freezegun.freeze_time('2019-09-01')
@pytest.mark.parametrize('value', ['foo', '20/01/2020', '2020'])
def test_clae_booking_activity_possible_days_endpoint_bad_date_format(app, resource, value):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
        '&start_date=%s&end_date=2020-01-24&activity_type=MAT' % value,
        status=400,
    )
    assert resp.json['err_desc'] == "bad date format, should be YYYY-MM-DD"
    assert resp.json['err'] == 'bad-request'
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
        '&start_date=2020-02-20&end_date=%s&activity_type=MAT' % value,
        status=400,
    )
    assert resp.json['err_desc'] == "bad date format, should be YYYY-MM-DD"
    assert resp.json['err'] == 'bad-request'


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_activity_possible_days_endpoint_activity_type(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
        '&start_date=2020-01-20&end_date=2020-01-24&activity_type=FOO',
        status=400,
    )
    assert resp.json['err_desc'] == "bad activity_type, should be MAT, MIDI, SOIR or GARD"
    assert resp.json['err'] == 'bad-request'


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_activity_possible_days_endpoint_no_result(app, resource, child_activities_data):
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
        '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
    )
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'

    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'EnfantsActivites'):
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_periode') as operation:
            operation.side_effect = AxelError('FooBar')
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=4242'
                '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
            )
    assert resp.json['err_desc'] == "Child not found"
    assert resp.json['err'] == 'not-found'

    content = """<PORTAIL>
  <DUI>
    <IDDUI>XXX</IDDUI>
    <ENFANT>
      <IDPERSONNE>4242</IDPERSONNE>
      <ACTIVITE>
        <ANNEEREFERENCE>2019</ANNEEREFERENCE>
        <IDACTIVITE>A19P1M1</IDACTIVITE>
        <JOUR>00000</JOUR>
      </ACTIVITE>
    </ENFANT>
  </DUI>
</PORTAIL>"""
    activities = child_activities_data['ENFANT'][0]
    with mock_getdata(content, 'ReservationPeriode'):
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities',
            return_value=activities,
        ):
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
                '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
            )
    assert resp.json['err_desc'] == "Child not found"
    assert resp.json['err'] == 'not-found'

    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_booking_data'
    ) as activities_info:
        activities_info.return_value = {'ACTIVITE': []}
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
            '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
        )
    assert resp.json['err'] == 0
    assert resp.json['data'] == []


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_activity_possible_days_endpoint(app, resource, child_activities_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    content = """<PORTAIL>
  <DUI>
    <IDDUI>XXX</IDDUI>
    <ENFANT>
      <IDPERSONNE>3535</IDPERSONNE>
      <ACTIVITE>
        <ANNEEREFERENCE>2019</ANNEEREFERENCE>
        <IDACTIVITE>A19P1M1</IDACTIVITE>
        <JOUR>00000</JOUR>
      </ACTIVITE>
      <ACTIVITE>
        <ANNEEREFERENCE>2019</ANNEEREFERENCE>
        <IDACTIVITE>A19P1M2</IDACTIVITE>
        <JOUR>10201</JOUR>
      </ACTIVITE>
    </ENFANT>
  </DUI>
</PORTAIL>"""
    activities = child_activities_data['ENFANT'][0]
    with mock_getdata(content, 'ReservationPeriode'):
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities',
            return_value=activities,
        ):
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
                '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MIDI'
            )
    assert resp.json['err'] == 0
    assert len(resp.json['data']) == 5
    assert set(resp.json['data'][0].keys()) == {'id', 'text', 'disabled', 'prefill', 'details'}
    assert resp.json['data'][0]['id'] == '3535:MIDI:A19P1M2:2020-01-20'
    assert resp.json['data'][1]['id'] == '3535:MIDI:A19P1M2:2020-01-21'
    assert resp.json['data'][2]['id'] == '3535:MIDI:A19P1M2:2020-01-22'
    assert resp.json['data'][3]['id'] == '3535:MIDI:A19P1M2:2020-01-23'
    assert resp.json['data'][4]['id'] == '3535:MIDI:A19P1M2:2020-01-24'
    assert resp.json['data'][0]['text'] == 'Monday 20 January 2020'
    assert resp.json['data'][1]['text'] == 'Tuesday 21 January 2020'
    assert resp.json['data'][2]['text'] == 'Wednesday 22 January 2020'
    assert resp.json['data'][3]['text'] == 'Thursday 23 January 2020'
    assert resp.json['data'][4]['text'] == 'Friday 24 January 2020'
    assert resp.json['data'][0]['disabled'] is False
    assert resp.json['data'][1]['disabled'] is False
    assert resp.json['data'][2]['disabled'] is True
    assert resp.json['data'][3]['disabled'] is False
    assert resp.json['data'][4]['disabled'] is False
    assert resp.json['data'][0]['prefill'] is True
    assert resp.json['data'][1]['prefill'] is False
    assert resp.json['data'][2]['prefill'] is None
    assert resp.json['data'][3]['prefill'] is False
    assert resp.json['data'][4]['prefill'] is True

    # again - data are in cache
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities', return_value=activities
    ):
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
            '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
        )
    assert resp.json['err'] == 0
    assert len(resp.json['data']) == 5


@pytest.mark.parametrize(
    'today, start_date, first_date, end_date, last_date',
    [
        # today is start date -> + 8 days
        ('2020-03-26', '2020-03-26', '2020-04-03', '2020-04-17', '2020-04-17'),
        # start date is a friday => starts friday
        ('2020-03-26', '2020-04-03', '2020-04-03', '2020-04-17', '2020-04-17'),
        # start date is a saturday => starts on next monday
        ('2020-03-26', '2020-04-04', '2020-04-06', '2020-04-17', '2020-04-17'),
        # start date is a sunday => starts on next monday
        ('2020-03-26', '2020-04-05', '2020-04-06', '2020-04-17', '2020-04-17'),
        # start date is a monday => starts on monday
        ('2020-03-26', '2020-04-06', '2020-04-06', '2020-04-17', '2020-04-17'),
        # start date is a tuesday => starts on tuesday
        ('2020-03-26', '2020-04-07', '2020-04-07', '2020-04-17', '2020-04-17'),
        # end date is a not a friday
        ('2020-03-26', '2020-04-07', '2020-04-07', '2020-04-13', '2020-04-13'),
        # except if end date is saturday or sunday => ends on previous friday
        ('2020-03-26', '2020-04-07', '2020-04-07', '2020-04-18', '2020-04-17'),
        ('2020-03-26', '2020-04-07', '2020-04-07', '2020-04-19', '2020-04-17'),
        # start date is after end date, same week => result is empty
        ('2020-03-26', '2020-04-16', None, '2020-04-15', None),
        # start date is after end date, not the same week => result is empty
        ('2020-03-26', '2020-04-16', None, '2020-04-12', None),
    ],
)
def test_clae_booking_activity_possible_days_period(
    app, resource, today, start_date, first_date, end_date, last_date, child_activities_data
):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    activities = child_activities_data['ENFANT'][0]

    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities', return_value=activities
    ):
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_booking_data'
        ) as activities_info:
            activities_info.return_value = {
                'ACTIVITE': [
                    {
                        'id': 'TOTO',
                        'TYPEACTIVITE': 'MAT',
                        'booking': {
                            'days': {
                                'monday': True,
                                'tuesday': True,
                                'wednesday': True,
                                'thursday': True,
                                'friday': True,
                            }
                        },
                    }
                ]
            }
            with freezegun.freeze_time(today):
                resp = app.get(
                    '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
                    '&start_date=%s&end_date=%s&activity_type=MAT' % (start_date, end_date)
                )
    if first_date is None:
        assert resp.json['data'] == []
    else:
        assert resp.json['data'][0]['id'] == '3535:MAT:TOTO:%s' % first_date
        assert resp.json['data'][-1]['id'] == '3535:MAT:TOTO:%s' % last_date


def test_clae_booking_activity_possible_days_next_year(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with freezegun.freeze_time('2020-07-01'):
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities'
        ) as mock_activities:
            mock_activities.return_value = {}
            app.get(
                '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
                '&start_date=2020-09-01&end_date=2020-09-30&activity_type=MAT'
            )
    assert mock_activities.call_args_list == [
        mock.call(child_id='3535', dui='XXX', reference_year=2020),
        mock.call(child_id='3535', dui='XXX', reference_year=2020),
    ]


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_activity_possible_days_endpoint_dates(app, resource, child_activities_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    activities = child_activities_data['ENFANT'][0]
    # second activity starts after 2019-08-01
    activities['ACTIVITE'][1]['DATEENTREE'] = '2019-10-01'
    # last activity ends before 2020-07-31
    activities['ACTIVITE'][3]['DATESORTIE'] = '2020-06-30'
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_booking_data'
    ) as activities_info:
        activities_info.return_value = {
            'ACTIVITE': [
                {
                    'id': 'TOTO',
                    'TYPEACTIVITE': 'MAT',
                    'booking': {
                        'days': {
                            'monday': True,
                            'tuesday': True,
                            'wednesday': True,
                            'thursday': True,
                            'friday': True,
                        }
                    },
                }
            ]
        }
        # it is not possible to book before 2019-10-01
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities',
            return_value=activities,
        ):
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
                '&start_date=2019-09-01&end_date=2019-10-11&activity_type=MAT'
            )
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 9
        assert resp.json['data'][0]['id'] == '3535:MAT:TOTO:2019-10-01'
        assert resp.json['data'][-1]['id'] == '3535:MAT:TOTO:2019-10-11'
        # it is not possible to book after 2020-06-30
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities',
            return_value=activities,
        ):
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535'
                '&start_date=2020-06-20&end_date=2020-07-15&activity_type=MAT'
            )
        assert resp.json['err'] == 0
        assert len(resp.json['data']) == 7
        assert resp.json['data'][0]['id'] == '3535:MAT:TOTO:2020-06-22'
        assert resp.json['data'][-1]['id'] == '3535:MAT:TOTO:2020-06-30'


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_activity_annual_possible_days_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.enfants_activites') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activity_annual_possible_days?NameID=yyy&idpersonne=3535'
            '&activity_type=MAT&booking_date=2019-09-01'
        )
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


@freezegun.freeze_time('2019-09-01')
@pytest.mark.parametrize('value', ['foo', '20/01/2020', '2020'])
def test_clae_booking_activity_annual_possible_days_endpoint_bad_date_format(app, resource, value):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activity_annual_possible_days?NameID=yyy&idpersonne=3535'
        '&activity_type=MAT&booking_date=%s' % value,
        status=400,
    )
    assert resp.json['err_desc'] == "bad date format, should be YYYY-MM-DD"
    assert resp.json['err'] == 'bad-request'


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_activity_annual_possible_days_endpoint_activity_type(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activity_annual_possible_days?NameID=yyy&idpersonne=3535'
        '&activity_type=FOO&booking_date=2019-09-01',
        status=400,
    )
    assert resp.json['err_desc'] == "bad activity_type, should be MAT, MIDI, SOIR or GARD"
    assert resp.json['err'] == 'bad-request'


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_activity_annual_possible_days_endpoint_no_result(app, resource):
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activity_annual_possible_days?NameID=yyy&idpersonne=3535'
        '&activity_type=MAT&booking_date=2019-09-01'
    )
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'

    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'EnfantsActivites'):
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activity_annual_possible_days?NameID=yyy&idpersonne=4242'
            '&activity_type=MAT&booking_date=2019-09-01'
        )
    assert resp.json['err_desc'] == "Child not found"
    assert resp.json['err'] == 'not-found'

    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities',
        return_value={'ACTIVITE': []},
    ):
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activity_annual_possible_days?NameID=yyy&idpersonne=3535'
            '&activity_type=MAT&booking_date=2019-09-01'
        )
    assert resp.json['err'] == 0
    assert resp.json['data'] == []


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_activity_annual_possible_days_endpoint(app, resource, child_activities_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    activities = child_activities_data['ENFANT'][0]
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities', return_value=activities
    ):
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activity_annual_possible_days?NameID=yyy&idpersonne=3535'
            '&activity_type=MAT&booking_date=2019-09-01'
        )
    assert resp.json['err'] == 0
    assert resp.json['data'] == [
        {
            'id': '3535:MAT:A19P1M1:monday',
            'text': 'Monday',
            'disabled': False,
        },
        {
            'id': '3535:MAT:A19P1M1:tuesday',
            'text': 'Tuesday',
            'disabled': False,
        },
        {
            'id': '3535:MAT:A19P1M1:wednesday',
            'text': 'Wednesday',
            'disabled': False,
        },
        {
            'id': '3535:MAT:A19P1M1:thursday',
            'text': 'Thursday',
            'disabled': False,
        },
        {
            'id': '3535:MAT:A19P1M1:friday',
            'text': 'Friday',
            'disabled': False,
        },
    ]
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities', return_value=activities
    ):
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activity_annual_possible_days?NameID=yyy&idpersonne=3535'
            '&activity_type=MIDI&booking_date=2019-09-01'
        )
    assert resp.json['err'] == 0
    assert resp.json['data'] == [
        {
            'id': '3535:MIDI:A19P1M2:monday',
            'text': 'Monday',
            'disabled': False,
        },
        {
            'id': '3535:MIDI:A19P1M2:tuesday',
            'text': 'Tuesday',
            'disabled': False,
        },
        {
            'id': '3535:MIDI:A19P1M2:wednesday',
            'text': 'Wednesday',
            'disabled': False,
        },
        {
            'id': '3535:MIDI:A19P1M2:thursday',
            'text': 'Thursday',
            'disabled': False,
        },
        {
            'id': '3535:MIDI:A19P1M2:friday',
            'text': 'Friday',
            'disabled': False,
        },
    ]
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities', return_value=activities
    ):
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activity_annual_possible_days?NameID=yyy&idpersonne=3535'
            '&activity_type=SOIR&booking_date=2019-09-01'
        )
    assert resp.json['err'] == 0
    assert resp.json['data'] == [
        {
            'id': '3535:SOIR:A19P1M3:monday',
            'text': 'Monday',
            'disabled': False,
        },
        {
            'id': '3535:SOIR:A19P1M3:tuesday',
            'text': 'Tuesday',
            'disabled': False,
        },
        {
            'id': '3535:SOIR:A19P1M3:wednesday',
            'text': 'Wednesday',
            'disabled': True,
        },
        {
            'id': '3535:SOIR:A19P1M3:thursday',
            'text': 'Thursday',
            'disabled': False,
        },
        {
            'id': '3535:SOIR:A19P1M3:friday',
            'text': 'Friday',
            'disabled': False,
        },
    ]
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities', return_value=activities
    ):
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activity_annual_possible_days?NameID=yyy&idpersonne=3535'
            '&activity_type=GARD&booking_date=2019-09-01'
        )
    assert resp.json['err'] == 0
    assert resp.json['data'] == [
        {
            'id': '3535:GARD:A19P1M4:monday',
            'text': 'Monday',
            'disabled': True,
        },
        {
            'id': '3535:GARD:A19P1M4:tuesday',
            'text': 'Tuesday',
            'disabled': True,
        },
        {
            'id': '3535:GARD:A19P1M4:wednesday',
            'text': 'Wednesday',
            'disabled': False,
        },
        {
            'id': '3535:GARD:A19P1M4:thursday',
            'text': 'Thursday',
            'disabled': True,
        },
        {
            'id': '3535:GARD:A19P1M4:friday',
            'text': 'Friday',
            'disabled': True,
        },
    ]


def test_clae_booking_activity_annual_possible_days_next_year(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with freezegun.freeze_time('2020-07-01'):
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities'
        ) as mock_activities:
            mock_activities.return_value = {}
            app.get(
                '/toulouse-axel/test/clae_booking_activity_annual_possible_days?NameID=yyy&idpersonne=3535'
                '&activity_type=GARD&booking_date=2020-09-01'
            )
    assert mock_activities.call_args_list == [
        mock.call(child_id='3535', dui='XXX', reference_year=2020),
    ]


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_activity_prefill_endpoint_axel_error(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.enfants_activites') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activity_prefill?NameID=yyy&idpersonne=3535'
            '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
        )
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'

    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'EnfantsActivites'):
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_periode') as operation:
            operation.side_effect = AxelError('FooBar')
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activity_prefill?NameID=yyy&idpersonne=3535'
                '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
            )
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


@freezegun.freeze_time('2019-09-01')
@pytest.mark.parametrize('value', ['foo', '20/01/2020', '2020'])
def test_clae_booking_activity_prefill_endpoint_bad_date_format(app, resource, value):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activity_prefill?NameID=yyy&idpersonne=3535'
        '&start_date=%s&end_date=2020-01-24&activity_type=MAT' % value,
        status=400,
    )
    assert resp.json['err_desc'] == "bad date format, should be YYYY-MM-DD"
    assert resp.json['err'] == 'bad-request'
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activity_prefill?NameID=yyy&idpersonne=3535'
        '&start_date=2020-01-20&end_date=%s&activity_type=MAT' % value,
        status=400,
    )
    assert resp.json['err_desc'] == "bad date format, should be YYYY-MM-DD"
    assert resp.json['err'] == 'bad-request'


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_activity_prefill_endpoint_activity_type(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activity_prefill?NameID=yyy&idpersonne=3535'
        '&start_date=2020-01-20&end_date=2020-01-24&activity_type=FOO',
        status=400,
    )
    assert resp.json['err_desc'] == "bad activity_type, should be MAT, MIDI, SOIR or GARD"
    assert resp.json['err'] == 'bad-request'


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_activity_prefill_endpoint_no_result(app, resource, child_activities_data):
    resp = app.get(
        '/toulouse-axel/test/clae_booking_activity_prefill?NameID=yyy&idpersonne=3535'
        '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
    )
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'

    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'EnfantsActivites'):
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_periode') as operation:
            operation.side_effect = AxelError('FooBar')
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activity_prefill?NameID=yyy&idpersonne=4242'
                '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
            )
    assert resp.json['err_desc'] == "Child not found"
    assert resp.json['err'] == 'not-found'

    content = """<PORTAIL>
  <DUI>
    <IDDUI>XXX</IDDUI>
    <ENFANT>
      <IDPERSONNE>4242</IDPERSONNE>
      <ACTIVITE>
        <ANNEEREFERENCE>2019</ANNEEREFERENCE>
        <IDACTIVITE>A19P1M1</IDACTIVITE>
        <JOUR>00000</JOUR>
      </ACTIVITE>
    </ENFANT>
  </DUI>
</PORTAIL>"""
    activities = child_activities_data['ENFANT'][0]
    with mock_getdata(content, 'ReservationPeriode'):
        with mock.patch(
            'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities',
            return_value=activities,
        ):
            resp = app.get(
                '/toulouse-axel/test/clae_booking_activity_prefill?NameID=yyy&idpersonne=3535'
                '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
            )
    assert resp.json['err_desc'] == "Child not found"
    assert resp.json['err'] == 'not-found'

    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.clae_booking_activity_possible_days'
    ) as possible_days:
        possible_days.return_value = {'data': []}
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activity_prefill?NameID=yyy&idpersonne=3535'
            '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
        )
    assert resp.json['err'] == 0
    assert resp.json['data'] == []


def test_clae_booking_activity_prefill_endpoint(app, resource):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.clae_booking_activity_possible_days'
    ) as possible_days:
        possible_days.return_value = {
            'data': [
                {
                    'id': '3535:MAT:A19P1M1:2020-01-20',
                    'prefill': True,
                },
                {
                    'id': '3535:MAT:A19P1M1:2020-01-21',
                    'prefill': False,
                },
                {
                    'id': '3535:MAT:A19P1M1:2020-01-22',
                    'prefill': None,
                },
                {
                    'id': '3535:MAT:A19P1M1:2020-01-23',
                    'prefill': False,
                },
                {
                    'id': '3535:MAT:A19P1M1:2020-01-24',
                    'prefill': True,
                },
            ]
        }
        resp = app.get(
            '/toulouse-axel/test/clae_booking_activity_prefill?NameID=yyy&idpersonne=3535'
            '&start_date=2020-01-20&end_date=2020-01-24&activity_type=MAT'
        )
    assert resp.json['err'] == 0
    assert resp.json['data'] == ['3535:MAT:A19P1M1:2020-01-20', '3535:MAT:A19P1M1:2020-01-24']


def test_are_children_registered_axel_error(resource):
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.enfants_activites') as operation:
        operation.side_effect = AxelError('FooBar')
        assert resource.are_children_registered(dui='XXX') == {}


@pytest.mark.parametrize(
    'date_value, expected',
    [
        ('2019-08-01', {'3535': True}),
        ('2020-07-31', {'3535': True}),
    ],
)
def test_are_children_registered_reference_year(resource, date_value, expected):
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_children_activities'
    ) as get_children_activities:
        get_children_activities.return_value = {'3535': {'ACTIVITE': [{}]}}
        with freezegun.freeze_time(date_value):
            assert resource.are_children_registered(dui='XXX') == expected


@pytest.mark.parametrize(
    'activities, expected',
    [
        ({}, {}),
        ({'3535': {'ACTIVITE': []}}, {'3535': False}),
        ({'3535': {'ACTIVITE': [{}]}}, {'3535': True}),
        ({'3535': {'ACTIVITE': [{}]}, '4242': {}}, {'3535': True, '4242': False}),
    ],
)
def test_are_children_registered(resource, activities, expected):
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_children_activities'
    ) as get_children_activities:
        get_children_activities.return_value = activities
        assert resource.are_children_registered(dui='XXX') == expected


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_endpoint_axel_error(app, resource, booking_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.enfants_activites') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params)
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'

    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'EnfantsActivites'):
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.side_effect = AxelError('FooBar')
            resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params)
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


def test_clae_booking_endpoint_date_error(app, resource, booking_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    # DATEDEBUT must be before DATEDFIN
    booking_params['booking_start_date'] = '2020-02-01'
    booking_params['booking_end_date'] = '2020-01-01'
    with freezegun.freeze_time('2019-09-01'):
        resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params, status=400)
    assert resp.json['err_desc'] == "booking_start_date should be before booking_end_date"
    assert resp.json['err'] == 'bad-request'

    # today + 8j rule
    booking_params['booking_start_date'] = '2020-01-01'
    booking_params['booking_end_date'] = '2020-07-31'
    # date in the past
    with freezegun.freeze_time('2020-01-01'):
        resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params, status=400)
    assert resp.json['err_desc'] == "booking_start_date min value: 2020-01-09"
    assert resp.json['err'] == 'bad-request'
    # too soon
    with freezegun.freeze_time('2019-12-25'):
        resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params, status=400)
    assert resp.json['err_desc'] == "booking_start_date min value: 2020-01-02"
    assert resp.json['err'] == 'bad-request'
    with freezegun.freeze_time('2019-12-31'):
        resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params, status=400)
    assert resp.json['err_desc'] == "booking_start_date min value: 2020-01-08"
    assert resp.json['err'] == 'bad-request'

    # bad reference year for end_date
    booking_params['booking_start_date'] = '2020-01-01'
    booking_params['booking_end_date'] = '2020-12-31'
    with freezegun.freeze_time('2019-09-01'):
        resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params, status=400)
    assert resp.json['err_desc'] == "booking_end_date max value: 2020-07-31"
    assert resp.json['err'] == 'bad-request'

    booking_params['booking_start_date'] = '2021-01-01'
    booking_params['booking_end_date'] = '2021-12-31'
    with freezegun.freeze_time('2019-09-01'):
        resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params, status=400)
    assert resp.json['err_desc'] == "booking_end_date max value: 2021-07-31"
    assert resp.json['err'] == 'bad-request'


def test_clae_booking_endpoint_no_result(app, resource, booking_params):
    resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params)
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_endpoint(app, resource, booking_params, child_activities_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    activities = child_activities_data['ENFANT'][0]
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities', return_value=activities
    ):
        content = "<PORTAIL/>"
        with mock_getdata(content, 'ReservationAnnuelle'):
            with mock.patch('django.core.cache.cache.delete') as mock_cache_delete:
                resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params)
        assert mock_cache_delete.call_args_list == [
            mock.call('toulouse-axel-%s-children-activities-XXX-2019' % resource.pk),
            mock.call('toulouse-axel-%s-booking-data-XXX-3535-2020-04-13' % resource.pk),
        ]
        assert resp.json['err'] == 0
        assert resp.json['updated'] is True
        assert 'data' in resp.json
        assert 'xml_request' in resp.json['data']
        assert 'xml_response' in resp.json['data']

        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params)
        payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
        assert payload == {
            'DATEDEMANDE': '2019-09-01',
            'ENFANT': [
                {
                    'IDPERSONNE': '3535',
                    'REGIME': 'AV',
                    'ACTIVITE': [
                        {
                            'IDACTIVITE': 'A19P1M1',
                            'ANNEEREFERENCE': '2019',
                            'PERIODE': [
                                {
                                    'DATEDEBUT': '2020-04-13',
                                    'DATEDFIN': '2020-04-17',
                                    'SEMAINETYPE': '11011',
                                },
                            ],
                        },
                        {
                            'IDACTIVITE': 'A19P1M2',
                            'ANNEEREFERENCE': '2019',
                            'PERIODE': [
                                {
                                    'DATEDEBUT': '2020-04-13',
                                    'DATEDFIN': '2020-04-17',
                                    'SEMAINETYPE': '11011',
                                },
                            ],
                        },
                        {
                            'IDACTIVITE': 'A19P1M3',
                            'ANNEEREFERENCE': '2019',
                            'PERIODE': [
                                {
                                    'DATEDEBUT': '2020-04-13',
                                    'DATEDFIN': '2020-04-17',
                                    'SEMAINETYPE': '10000',
                                },
                            ],
                        },
                        {
                            'IDACTIVITE': 'A19P1M4',
                            'ANNEEREFERENCE': '2019',
                            'PERIODE': [
                                {
                                    'DATEDEBUT': '2020-04-13',
                                    'DATEDFIN': '2020-04-17',
                                    'SEMAINETYPE': '00100',
                                },
                            ],
                        },
                    ],
                }
            ],
            'IDDUI': 'XXX',
        }

        # empty list
        new_booking_params = copy.deepcopy(booking_params)
        new_booking_params['booking_list_MAT'] = ['42:MAT:A19P1M1:2020-04-15']  # wrong child, ignored
        new_booking_params['booking_list_MIDI'] = []
        new_booking_params['booking_list_SOIR'] = []
        new_booking_params['booking_list_GARD'] = []
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=new_booking_params)
        payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
        assert len(payload['ENFANT']) == 1
        assert payload['ENFANT'][0]['IDPERSONNE'] == '3535'
        assert len(payload['ENFANT'][0]['ACTIVITE']) == 4
        activity1 = payload['ENFANT'][0]['ACTIVITE'][0]
        assert activity1['IDACTIVITE'] == 'A19P1M1'
        assert len(activity1['PERIODE']) == 1
        assert activity1['PERIODE'][0]['DATEDEBUT'] == '2020-04-13'
        assert activity1['PERIODE'][0]['DATEDFIN'] == '2020-04-17'
        assert activity1['PERIODE'][0]['SEMAINETYPE'] == '00000'
        activity2 = payload['ENFANT'][0]['ACTIVITE'][1]
        assert activity2['IDACTIVITE'] == 'A19P1M2'
        assert len(activity2['PERIODE']) == 1
        assert activity2['PERIODE'][0]['DATEDEBUT'] == '2020-04-13'
        assert activity2['PERIODE'][0]['DATEDFIN'] == '2020-04-17'
        assert activity2['PERIODE'][0]['SEMAINETYPE'] == '00000'
        activity3 = payload['ENFANT'][0]['ACTIVITE'][2]
        assert activity3['IDACTIVITE'] == 'A19P1M3'
        assert len(activity3['PERIODE']) == 1
        assert activity3['PERIODE'][0]['DATEDEBUT'] == '2020-04-13'
        assert activity3['PERIODE'][0]['DATEDFIN'] == '2020-04-17'
        assert activity3['PERIODE'][0]['SEMAINETYPE'] == '00000'
        activity4 = payload['ENFANT'][0]['ACTIVITE'][3]
        assert activity4['IDACTIVITE'] == 'A19P1M4'
        assert len(activity4['PERIODE']) == 1
        assert activity4['PERIODE'][0]['DATEDEBUT'] == '2020-04-13'
        assert activity4['PERIODE'][0]['DATEDFIN'] == '2020-04-17'
        assert activity4['PERIODE'][0]['SEMAINETYPE'] == '00000'

        # null list
        new_booking_params = copy.deepcopy(booking_params)
        new_booking_params['booking_list_MAT'] = None
        new_booking_params['booking_list_MIDI'] = None
        new_booking_params['booking_list_SOIR'] = None
        new_booking_params['booking_list_GARD'] = None
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=new_booking_params)
        payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
        assert 'ACTIVITE' not in payload['ENFANT'][0]

        # many weeks => many periods
        new_booking_params = copy.deepcopy(booking_params)
        new_booking_params['booking_start_date'] = '2020-04-01'
        new_booking_params['booking_end_date'] = '2020-04-30'
        new_booking_params['booking_list_MAT'].append('3535:MAT:A19P1M1:2020-04-01')
        new_booking_params['booking_list_MAT'].append(
            '3535:MAT:A19P1M1:2020-03-31'
        )  # before the period, ignored
        new_booking_params['booking_list_MAT'].append('3535:MAT:A19P1M1:2020-04-30')
        new_booking_params['booking_list_MAT'].append(
            '3535:MAT:A19P1M1:2020-05-01'
        )  # after the period, ignored
        new_booking_params['booking_list_MAT'].append('4242:MAT:A19P1M1:2020-04-15')  # wrong child, ignored
        new_booking_params['booking_list_MAT'].append('3535:MAT:FOO:2020-04-15')  # wrong activity id, ignored
        new_booking_params['booking_list_MIDI'] = None
        new_booking_params['booking_list_SOIR'] = None
        new_booking_params['booking_list_GARD'] = None
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            with mock.patch('django.core.cache.cache.delete') as mock_cache_delete:
                resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=new_booking_params)
        assert mock_cache_delete.call_args_list == [
            mock.call('toulouse-axel-%s-children-activities-XXX-2019' % resource.pk),
            mock.call('toulouse-axel-%s-booking-data-XXX-3535-2020-03-30' % resource.pk),
            mock.call('toulouse-axel-%s-booking-data-XXX-3535-2020-04-06' % resource.pk),
            mock.call('toulouse-axel-%s-booking-data-XXX-3535-2020-04-13' % resource.pk),
            mock.call('toulouse-axel-%s-booking-data-XXX-3535-2020-04-20' % resource.pk),
            mock.call('toulouse-axel-%s-booking-data-XXX-3535-2020-04-27' % resource.pk),
        ]
        payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
        assert len(payload['ENFANT']) == 1
        assert payload['ENFANT'][0]['IDPERSONNE'] == '3535'
        assert len(payload['ENFANT'][0]['ACTIVITE']) == 1
        activity = payload['ENFANT'][0]['ACTIVITE'][0]
        assert activity['IDACTIVITE'] == 'A19P1M1'
        assert len(activity['PERIODE']) == 5
        assert activity['PERIODE'][0]['DATEDEBUT'] == '2020-04-01'
        assert activity['PERIODE'][0]['DATEDFIN'] == '2020-04-03'
        assert activity['PERIODE'][0]['SEMAINETYPE'] == '00100'
        assert activity['PERIODE'][1]['DATEDEBUT'] == '2020-04-06'
        assert activity['PERIODE'][1]['DATEDFIN'] == '2020-04-10'
        assert activity['PERIODE'][1]['SEMAINETYPE'] == '00000'
        assert activity['PERIODE'][2]['DATEDEBUT'] == '2020-04-13'
        assert activity['PERIODE'][2]['DATEDFIN'] == '2020-04-17'
        assert activity['PERIODE'][2]['SEMAINETYPE'] == '11011'
        assert activity['PERIODE'][3]['DATEDEBUT'] == '2020-04-20'
        assert activity['PERIODE'][3]['DATEDFIN'] == '2020-04-24'
        assert activity['PERIODE'][3]['SEMAINETYPE'] == '00000'
        assert activity['PERIODE'][4]['DATEDEBUT'] == '2020-04-27'
        assert activity['PERIODE'][4]['DATEDFIN'] == '2020-04-30'
        assert activity['PERIODE'][4]['SEMAINETYPE'] == '00010'


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_regime(app, resource, booking_params, child_activities_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    activities = child_activities_data['ENFANT'][0]
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities', return_value=activities
    ):
        booking_params['regime'] = None
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params)
        payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
        assert payload['ENFANT'][0]['REGIME'] == 'SV'
        booking_params['regime'] = ''
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params)
        payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
        assert payload['ENFANT'][0]['REGIME'] == 'SV'
        del booking_params['regime']
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params)
        payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
        assert payload['ENFANT'][0]['REGIME'] == 'SV'


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_endpoint_next_year(app, resource, child_activities_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    activities = child_activities_data['ENFANT'][0]
    for activity in activities['ACTIVITE']:
        activity['DATEDEBUT'] = '2020-08-01'
        activity['DATEENTREE'] = '2020-08-01'
        activity['DATEFIN'] = '2021-07-31'
        activity['DATESORTIE'] = '2021-07-31'
    booking_params = {
        'booking_start_date': '2020-09-01',
        'booking_end_date': '2020-09-04',
        'booking_list_MAT': [],
        'booking_list_MIDI': None,
        'booking_list_SOIR': None,
        'booking_list_GARD': None,
        'child_id': '3535',
        'regime': 'AV',
    }
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities'
    ) as mock_activities:
        mock_activities.return_value = activities
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params)
    assert mock_activities.call_args_list == [
        mock.call(child_id='3535', dui='XXX', reference_year=2020),
    ]
    payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
    assert len(payload['ENFANT']) == 1
    assert payload['ENFANT'][0]['IDPERSONNE'] == '3535'
    assert len(payload['ENFANT'][0]['ACTIVITE']) == 1
    activity = payload['ENFANT'][0]['ACTIVITE'][0]
    assert len(activity['PERIODE']) == 1
    assert activity['PERIODE'][0]['DATEDEBUT'] == '2020-09-01'
    assert activity['PERIODE'][0]['DATEDFIN'] == '2020-09-04'


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_annual_endpoint_axel_error(app, resource, annual_booking_params):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    with mock.patch('passerelle.contrib.toulouse_axel.schemas.enfants_activites') as operation:
        operation.side_effect = AxelError('FooBar')
        resp = app.post_json(
            '/toulouse-axel/test/clae_booking_annual?NameID=yyy', params=annual_booking_params
        )
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'

    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
    with open(filepath) as xml:
        content = xml.read()
    with mock_getdata(content, 'EnfantsActivites'):
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.side_effect = AxelError('FooBar')
            resp = app.post_json(
                '/toulouse-axel/test/clae_booking_annual?NameID=yyy', params=annual_booking_params
            )
    assert resp.json['err_desc'] == "Axel error: FooBar"
    assert resp.json['err'] == 'error'


def test_clae_booking_annual_endpoint_no_result(app, resource, annual_booking_params):
    resp = app.post_json('/toulouse-axel/test/clae_booking_annual?NameID=yyy', params=annual_booking_params)
    assert resp.json['err_desc'] == "Person not found"
    assert resp.json['err'] == 'not-found'


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_annual_endpoint(app, resource, annual_booking_params, child_activities_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    activities = child_activities_data['ENFANT'][0]
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities', return_value=activities
    ):
        content = "<PORTAIL/>"
        with mock_getdata(content, 'ReservationAnnuelle'):
            with mock.patch('django.core.cache.cache.delete') as mock_cache_delete:
                resp = app.post_json(
                    '/toulouse-axel/test/clae_booking_annual?NameID=yyy', params=annual_booking_params
                )
        assert len(mock_cache_delete.call_args_list) == 48
        assert mock_cache_delete.call_args_list[0] == mock.call(
            'toulouse-axel-%s-children-activities-XXX-2019' % resource.pk
        )
        assert mock_cache_delete.call_args_list[1] == mock.call(
            'toulouse-axel-%s-booking-data-XXX-3535-2019-09-09' % resource.pk
        )
        assert mock_cache_delete.call_args_list[-1] == mock.call(
            'toulouse-axel-%s-booking-data-XXX-3535-2020-07-27' % resource.pk
        )
        assert resp.json['err'] == 0
        assert resp.json['updated'] is True
        assert 'data' in resp.json
        assert 'xml_request' in resp.json['data']
        assert 'xml_response' in resp.json['data']

        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            resp = app.post_json(
                '/toulouse-axel/test/clae_booking_annual?NameID=yyy', params=annual_booking_params
            )
        payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
        assert payload == {
            'DATEDEMANDE': '2019-09-01',
            'ENFANT': [
                {
                    'IDPERSONNE': '3535',
                    'REGIME': 'AV',
                    'ACTIVITE': [
                        {
                            'ANNEEREFERENCE': '2019',
                            'IDACTIVITE': 'A19P1M1',
                            'PERIODE': [
                                {'DATEDEBUT': '2019-09-09', 'DATEDFIN': '2020-07-31', 'SEMAINETYPE': '11011'}
                            ],
                        },
                        {
                            'ANNEEREFERENCE': '2019',
                            'IDACTIVITE': 'A19P1M2',
                            'PERIODE': [
                                {'DATEDEBUT': '2019-09-09', 'DATEDFIN': '2020-07-31', 'SEMAINETYPE': '11011'}
                            ],
                        },
                        {
                            'ANNEEREFERENCE': '2019',
                            'IDACTIVITE': 'A19P1M3',
                            'PERIODE': [
                                {'DATEDEBUT': '2019-09-09', 'DATEDFIN': '2020-07-31', 'SEMAINETYPE': '10000'}
                            ],
                        },
                        {
                            'ANNEEREFERENCE': '2019',
                            'IDACTIVITE': 'A19P1M4',
                            'PERIODE': [
                                {'DATEDEBUT': '2019-09-09', 'DATEDFIN': '2020-07-31', 'SEMAINETYPE': '00100'}
                            ],
                        },
                    ],
                }
            ],
            'IDDUI': 'XXX',
        }

        # empty list
        new_booking_params = copy.deepcopy(annual_booking_params)
        new_booking_params['booking_list_MAT'] = ['42:MAT:A19P1M1:monday']  # wrong child, ignored
        new_booking_params['booking_list_MIDI'] = []
        new_booking_params['booking_list_SOIR'] = []
        new_booking_params['booking_list_GARD'] = []
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            resp = app.post_json(
                '/toulouse-axel/test/clae_booking_annual?NameID=yyy', params=new_booking_params
            )
        payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
        assert len(payload['ENFANT']) == 1
        assert payload['ENFANT'][0]['IDPERSONNE'] == '3535'
        assert len(payload['ENFANT'][0]['ACTIVITE']) == 4
        activity1 = payload['ENFANT'][0]['ACTIVITE'][0]
        assert activity1['IDACTIVITE'] == 'A19P1M1'
        assert len(activity1['PERIODE']) == 1
        assert activity1['PERIODE'][0]['DATEDEBUT'] == '2019-09-09'
        assert activity1['PERIODE'][0]['DATEDFIN'] == '2020-07-31'
        assert activity1['PERIODE'][0]['SEMAINETYPE'] == '00000'
        activity2 = payload['ENFANT'][0]['ACTIVITE'][1]
        assert activity2['IDACTIVITE'] == 'A19P1M2'
        assert len(activity2['PERIODE']) == 1
        assert activity2['PERIODE'][0]['DATEDEBUT'] == '2019-09-09'
        assert activity2['PERIODE'][0]['DATEDFIN'] == '2020-07-31'
        assert activity2['PERIODE'][0]['SEMAINETYPE'] == '00000'
        activity3 = payload['ENFANT'][0]['ACTIVITE'][2]
        assert activity3['IDACTIVITE'] == 'A19P1M3'
        assert len(activity3['PERIODE']) == 1
        assert activity3['PERIODE'][0]['DATEDEBUT'] == '2019-09-09'
        assert activity3['PERIODE'][0]['DATEDFIN'] == '2020-07-31'
        assert activity3['PERIODE'][0]['SEMAINETYPE'] == '00000'
        activity4 = payload['ENFANT'][0]['ACTIVITE'][3]
        assert activity4['IDACTIVITE'] == 'A19P1M4'
        assert len(activity4['PERIODE']) == 1
        assert activity4['PERIODE'][0]['DATEDEBUT'] == '2019-09-09'
        assert activity4['PERIODE'][0]['DATEDFIN'] == '2020-07-31'
        assert activity4['PERIODE'][0]['SEMAINETYPE'] == '00000'

        # null list
        new_booking_params = copy.deepcopy(annual_booking_params)
        new_booking_params['booking_list_MAT'] = None
        new_booking_params['booking_list_MIDI'] = None
        new_booking_params['booking_list_SOIR'] = None
        new_booking_params['booking_list_GARD'] = None
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            resp = app.post_json(
                '/toulouse-axel/test/clae_booking_annual?NameID=yyy', params=new_booking_params
            )
        payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
        assert 'ACTIVITE' not in payload['ENFANT'][0]


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_annual_next_year(app, resource, child_activities_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    activities = child_activities_data['ENFANT'][0]
    for activity in activities['ACTIVITE']:
        activity['DATEDEBUT'] = '2020-08-01'
        activity['DATEENTREE'] = '2020-08-01'
        activity['DATEFIN'] = '2021-07-31'
        activity['DATESORTIE'] = '2021-07-31'
    annual_booking_params = {
        'booking_list_MAT': [],
        'booking_list_MIDI': None,
        'booking_list_SOIR': None,
        'booking_list_GARD': None,
        'child_id': '3535',
        'regime': 'AV',
        'booking_date': '2020-08-01',
    }
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities', return_value=activities
    ):
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            app.post_json('/toulouse-axel/test/clae_booking_annual?NameID=yyy', params=annual_booking_params)
    payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
    assert payload['ENFANT'][0]['ACTIVITE'][0]['PERIODE'][0]['DATEDEBUT'] == '2020-08-01'
    assert payload['ENFANT'][0]['ACTIVITE'][0]['PERIODE'][0]['DATEDFIN'] == '2021-07-31'


@freezegun.freeze_time('2019-09-01')
def test_clae_booking_annual_regime(app, resource, annual_booking_params, child_activities_data):
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
    activities = child_activities_data['ENFANT'][0]
    with mock.patch(
        'passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities', return_value=activities
    ):
        annual_booking_params['regime'] = None
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            app.post_json('/toulouse-axel/test/clae_booking_annual?NameID=yyy', params=annual_booking_params)
        payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
        assert payload['ENFANT'][0]['REGIME'] == 'SV'
        annual_booking_params['regime'] = ''
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            app.post_json('/toulouse-axel/test/clae_booking_annual?NameID=yyy', params=annual_booking_params)
        payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
        assert payload['ENFANT'][0]['REGIME'] == 'SV'
        del annual_booking_params['regime']
        with mock.patch('passerelle.contrib.toulouse_axel.schemas.reservation_annuelle') as operation:
            operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='')
            app.post_json('/toulouse-axel/test/clae_booking_annual?NameID=yyy', params=annual_booking_params)
        payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI']
        assert payload['ENFANT'][0]['REGIME'] == 'SV'
