# passerelle - uniform access to multiple data sources and services
# Copyright (C) 2021 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import base64
import cgi
import functools
import io
import json
import os
import uuid
import zipfile
from copy import deepcopy
from unittest import mock

import httmock
import lxml.etree as ET
import pytest
from django.db import connection
from django.db.migrations.executor import MigrationExecutor
from django.utils.encoding import force_str
from requests.exceptions import ReadTimeout

import tests.utils
from passerelle.base.models import Job
from passerelle.contrib.toulouse_smart.models import (
    SmartRequest,
    ToulouseSmartResource,
    WcsRequest,
    WcsRequestFile,
)
from passerelle.contrib.toulouse_smart.utils import localtz_to_utc, utc_to_localtz
from tests.test_manager import login

TEST_BASE_DIR = os.path.join(os.path.dirname(__file__), 'data', 'toulouse_smart')


@pytest.fixture
def smart(db, settings):
    settings.TIME_ZONE = 'Europe/Paris'
    settings.USE_TZ = True
    return tests.utils.make_resource(
        ToulouseSmartResource,
        title='Test',
        slug='test',
        description='Test',
        webservice_base_url='https://smart.example.com/',
        basic_auth_username='username',
        basic_auth_password='password',
    )


@pytest.fixture
def wcs_service(settings):
    wcs_service = {
        'default': {
            'title': 'test',
            'url': 'https://wcs.example.com',
            'secret': 'xxx',
            'orig': 'passerelle',
        },
    }
    settings.KNOWN_SERVICES = {'wcs': wcs_service}
    return wcs_service


def mock_response(*path_contents):
    def decorator(func):
        @httmock.urlmatch()
        def error(url, request):
            assert False, 'request to %s' % url.geturl()

        def register(path, payload, content, status_code=200, exception=None):
            @httmock.urlmatch(path=path)
            def handler(url, request):
                if payload:
                    ctype, pdict = cgi.parse_header(request.headers["content-type"])
                    if ctype == 'multipart/form-data':
                        # here payload is an expected multipart contents list
                        pdict["boundary"] = bytes(pdict["boundary"], "utf-8")
                        pdict['CONTENT-LENGTH'] = request.headers['Content-Length']
                        postvars = cgi.parse_multipart(io.BytesIO(request.body), pdict)
                        for i, media_content in enumerate(postvars['media']):
                            assert media_content == payload[i], (
                                'wrong multipart content sent to %s' % url.geturl()
                            )
                    else:
                        assert json.loads(request.body) == payload, (
                            'wrong payload sent to request to %s' % url.geturl()
                        )
                if exception:
                    raise exception
                return httmock.response(status_code, content)

            return handler

        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            handlers = []
            for row in path_contents:
                handlers.append(register(*row))
            handlers.append(error)

            with httmock.HTTMock(*handlers):
                return func(*args, **kwargs)

        return wrapper

    return decorator


def get_json_file(filename):
    with open(os.path.join(TEST_BASE_DIR, "%s.json" % filename)) as desc:
        return desc.read()


def get_media_file(filename):
    with open(os.path.join(TEST_BASE_DIR, "%s" % filename), 'rb') as desc:
        return desc.read()


def test_save_daylight_time_change(settings):
    settings.TIME_ZONE = 'Europe/Paris'
    settings.USE_TZ = True
    assert localtz_to_utc('2022-10-30T02:19:48') == '2022-10-30T01:19:48+00:00'
    assert utc_to_localtz('2022-10-30T01:19:48+00:00') == '2022-10-30T02:19:48'


@mock_response(['/v1/type-intervention', None, b'<List></List>'])
def test_empty_intervention_types(smart):
    assert smart.get_intervention_types() == []


INTERVENTION_TYPES = '''<List>
   <item>
       <id>1234</id>
       <name>coin</name>
       <properties>
           <properties>
              <name>TYPE-OBJET</name>
              <displayName>Champ 1</displayName>
              <type>string</type>
              <required>false</required>
              <defaultValue>Ne sait pas</defaultValue>
              <restrictedValues>
                <restrictedValues>Candélabre</restrictedValues>
                <restrictedValues>Mât</restrictedValues>
                <restrictedValues>Ne sait pas</restrictedValues>
              </restrictedValues>
           </properties>
           <properties>
              <name>FIELD2</name>
              <displayName>Champ 2</displayName>
              <type>int</type>
              <required>true</required>
           </properties>
           <properties>
              <name>IGNORED-FIELD-HAVING-NO-TYPE</name>
              <displayName>Champ 3</displayName>
           </properties>
          <properties>
              <name>IGNORED-FIELD-HAVING-UNKNOWN-TYPE</name>
              <displayName>Champ 3</displayName>
              <type>plop</type>
           </properties>
       </properties>
   </item>
   <item>
       <id>0002</id>
       <name>empty</name>
   </item>
</List>'''.encode()


@mock_response(['/v1/type-intervention', None, INTERVENTION_TYPES])
def test_model_intervention_types(smart):
    assert smart.get_intervention_types() == [
        {
            'id': '1234',
            'name': 'coin',
            'order': 1,
            'properties': [
                {
                    'name': 'TYPE-OBJET',
                    'displayName': 'Champ 1',
                    'required': False,
                    'type': 'item',
                    'defaultValue': 'Ne sait pas',
                    'restrictedValues': ['Candélabre', 'Mât', 'Ne sait pas'],
                },
                {'name': 'FIELD2', 'displayName': 'Champ 2', 'required': True, 'type': 'int'},
            ],
        },
        {
            'id': '0002',
            'name': 'empty',
            'order': 2,
        },
    ]


URL = '/toulouse-smart/test/'


@mock_response(['/v1/type-intervention', None, INTERVENTION_TYPES])
def test_endpoint_intervention_types(app, smart):
    resp = app.get(URL + 'type-intervention')
    assert resp.json == {
        'data': [
            {'id': 'coin', 'text': 'coin', 'uuid': '1234'},
            {'id': 'empty', 'text': 'empty', 'uuid': '0002'},
        ],
        'err': 0,
    }


@mock_response()
def test_endpoint_intervention_types_unavailable(app, smart):
    resp = app.get(URL + 'type-intervention')
    assert resp.json == {'data': [{'id': '', 'text': 'Service is unavailable', 'disabled': True}], 'err': 0}


@mock_response(['/v1/type-intervention', None, INTERVENTION_TYPES])
def test_manage_intervention_types(app, smart, admin_user):
    login(app)
    resp = app.get('/manage' + URL + 'type-intervention/')
    assert [[td.text for td in tr.cssselect('td,th')] for tr in resp.pyquery('tr')] == [
        ["Nom du type d'intervention", 'Nom', 'Type', 'Requis', 'Valeur par défaut'],
        ['1 - coin'],
        [None, 'TYPE-OBJET', 'item («Candélabre», «Mât», «Ne sait pas»)', '✘', 'Ne sait pas'],
        [None, 'FIELD2', 'int', '✔', None],
        ['2 - empty'],
    ]
    resp = resp.click('Export to blocks')
    with zipfile.ZipFile(io.BytesIO(resp.body)) as zip_file:
        assert zip_file.namelist() == ['block-coin.wcs']
        with zip_file.open('block-coin.wcs') as fd:
            content = ET.tostring(ET.fromstring(fd.read()), pretty_print=True).decode()
            assert (
                content
                == '''<block id="1234">
  <name>coin</name>
  <slug>coin</slug>
  <fields>
    <field>
      <id>522697a9-de01-b198-9e37-58c35718203a</id>
      <label>Champ 1</label>
      <type>item</type>
      <required>False</required>
      <varname>type_objet</varname>
      <display_locations>
        <display_location>validation</display_location>
        <display_location>summary</display_location>
      </display_locations>
      <items>
        <item>Cand&#233;labre</item>
        <item>M&#226;t</item>
        <item>Ne sait pas</item>
      </items>
    </field>
    <field>
      <id>e72f251a-5eef-5b78-c35a-94b549510029</id>
      <label>Champ 2</label>
      <type>string</type>
      <required>True</required>
      <varname>field2</varname>
      <display_locations>
        <display_location>validation</display_location>
        <display_location>summary</display_location>
      </display_locations>
      <validation>
        <type>digits</type>
      </validation>
    </field>
  </fields>
</block>
'''
            )


INTERVENTION_ID = json.loads(get_json_file('create_intervention'))['id']


@mock_response(
    ['/v1/intervention', None, get_json_file('create_intervention')],
)
def test_get_intervention(app, smart):
    resp = app.get(URL + 'get-intervention?id=%s' % INTERVENTION_ID)
    assert not resp.json['err']
    assert resp.json['data']['id'] == INTERVENTION_ID
    assert resp.json['data']['state'] == {
        'id': 'e844e67f-5382-4c0f-94d8-56f618263485',
        'table': None,
        'stateLabel': 'Nouveau',
        'closes': False,
    }
    assert resp.json['data']['interventionCreated'] == '2021-07-07T14:19:31.302000'
    assert resp.json['data']['interventionDesired'] == '2021-06-30T18:08:05'


@mock_response(
    ['/v1/intervention', None, None, 500],
)
def test_get_intervention_error_status(app, smart):
    resp = app.get(URL + 'get-intervention?id=%s' % INTERVENTION_ID)
    assert resp.json['err']
    assert 'failed to get' in resp.json['err_desc']


@mock_response(
    ['/v1/intervention', None, None, 404],
)
def test_get_intervention_wrond_id(app, smart):
    resp = app.get(URL + 'get-intervention?id=%s' % INTERVENTION_ID)
    assert resp.json['err']
    assert 'failed to get' in resp.json['err_desc']
    assert '404' in resp.json['err_desc']


CREATE_INTERVENTION_PAYLOAD_EXTRA = {
    'slug': 'coin',
    'description': 'coin coin',
    'lat': 48.833708,
    'lon': 2.323349,
    'cityId': '12345',
    'interventionCreated': '2021-06-30T18:08:05',
    'interventionDesired': '2021-06-30T18:08:05',
    'submitterFirstName': 'John',
    'submitterLastName': 'Doe',
    'submitterMail': 'john.doe@example.com',
    'submitterPhone': '0123456789',
    'submitterAddress': '3 rue des champs de blés',
    'submitterType': 'usager',
    'externalReferences': 'AlloToulouse',
    'external_number': '42-2',
    'external_status': 'statut-1-wcs',
    'address': 'https://wcs.example.com/backoffice/management/foo/2/',
    'form_api_url': 'https://wcs.example.com/api/forms/foo/2/',
    'checkDuplicated': 'False',
    'onPrivateLand': 'True',
    'safeguardRequired': True,
}


FIELDS_PAYLOAD = {
    'coin_raw': [
        {
            'type_objet': 'Candélabre',
            'type_objet_raw': 'Candélabre',
            'field2': '42',
        },
    ],
}


CREATE_INTERVENTION_PAYLOAD = {
    'fields': FIELDS_PAYLOAD,
    'extra': CREATE_INTERVENTION_PAYLOAD_EXTRA,
}

UUID = uuid.UUID('12345678123456781234567812345678')

CREATE_INTERVENTION_QUERY = {
    'add_media_url': 'http://testserver/toulouse-smart/test/add-media?uuid=%s' % str(UUID),
    'description': 'coin coin',
    'cityId': '12345',
    'interventionCreated': '2021-06-30T16:08:05+00:00',
    'interventionDesired': '2021-06-30T16:08:05+00:00',
    'submitterFirstName': 'John',
    'submitterLastName': 'Doe',
    'submitterMail': 'john.doe@example.com',
    'submitterPhone': '0123456789',
    'submitterAddress': '3 rue des champs de bl\u00e9s',
    'submitterType': 'usager',
    'externalReferences': 'AlloToulouse',
    'external_number': '42-2',
    'external_status': 'statut-1-wcs',
    'address': 'https://wcs.example.com/backoffice/management/foo/2/',
    'interventionData': {'TYPE-OBJET': 'Candélabre', 'FIELD2': 42},
    'geom': {'type': 'Point', 'coordinates': [2.323349, 48.833708], 'crs': 'EPSG:4326'},
    'interventionTypeId': '1234',
    'notificationUrl': 'http://testserver/toulouse-smart/test/update-intervention?uuid=%s' % str(UUID),
    'notification_url': 'http://testserver/toulouse-smart/test/update-intervention?uuid=%s' % str(UUID),
    'onPrivateLand': 'true',
    'safeguardRequired': 'true',
}

CREATE_INTERVENTION_QUERY_WITHOUT_PROPERTIES = deepcopy(CREATE_INTERVENTION_QUERY)
CREATE_INTERVENTION_QUERY_WITHOUT_PROPERTIES['interventionTypeId'] = '0002'
CREATE_INTERVENTION_QUERY_WITHOUT_PROPERTIES['interventionData'] = {}


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_create_intervention(mocked_uuid4, app, smart):
    with pytest.raises(WcsRequest.DoesNotExist):
        smart.wcs_requests.get(uuid=UUID)
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert str(UUID) in CREATE_INTERVENTION_QUERY['notification_url']
    assert not resp.json['err']
    assert resp.json['data']['uuid'] == str(UUID)
    assert resp.json['data']['wcs_form_api_url'] == 'https://wcs.example.com/api/forms/foo/2/'
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert wcs_request.wcs_form_api_url == 'https://wcs.example.com/api/forms/foo/2/'
    assert wcs_request.wcs_form_number == '42-2'
    assert wcs_request.payload == CREATE_INTERVENTION_QUERY
    assert wcs_request.result['id'] == INTERVENTION_ID
    assert wcs_request.result['state'] == {
        'id': 'e844e67f-5382-4c0f-94d8-56f618263485',
        'table': None,
        'stateLabel': 'Nouveau',
        'closes': False,
    }
    assert wcs_request.result['interventionCreated'] == '2021-07-07T14:19:31.302000'
    assert wcs_request.result['interventionDesired'] == '2021-06-30T18:08:05'
    assert wcs_request.status == 'sent'


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY_WITHOUT_PROPERTIES, get_json_file('create_intervention')],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_create_intervention_without_properties(mocked_uuid4, app, smart):
    payload = deepcopy(CREATE_INTERVENTION_PAYLOAD)
    payload['extra']['slug'] = 'empty'
    payload['fields']['coin_raw'] = None
    resp = app.post_json(URL + 'create-intervention/', params=payload)
    assert not resp.json['err']


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY_WITHOUT_PROPERTIES, get_json_file('create_intervention')],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_create_intervention_providing_empty_block(mocked_uuid4, app, smart):
    payload = deepcopy(CREATE_INTERVENTION_PAYLOAD)
    payload['extra']['slug'] = 'empty'
    payload['fields']['coin_raw'] = None
    payload['fields']['empty_raw'] = None
    resp = app.post_json(URL + 'create-intervention/', params=payload)
    assert not resp.json['err']


def test_create_intervention_wrong_payload(app, smart):
    payload = deepcopy(CREATE_INTERVENTION_PAYLOAD)
    del payload['extra']['slug']
    resp = app.post_json(URL + 'create-intervention/', params=payload, status=400)
    assert resp.json['err']
    assert "'slug' is a required property" in resp.json['err_desc']


@mock_response()
def test_create_intervention_types_unavailable(app, smart):
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert resp.json['err']
    assert 'Service is unavailable' in resp.json['err_desc']


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
)
def test_create_intervention_wrong_block_slug(app, smart):
    payload = deepcopy(CREATE_INTERVENTION_PAYLOAD)
    payload['extra']['slug'] = 'coin-coin'
    resp = app.post_json(URL + 'create-intervention/', params=payload, status=400)
    assert resp.json['err']
    assert "unknown 'coin-coin' block slug" in resp.json['err_desc']


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
)
def test_create_intervention_no_block(app, smart):
    payload = deepcopy(CREATE_INTERVENTION_PAYLOAD)
    del payload['fields']['coin_raw']
    resp = app.post_json(URL + 'create-intervention/', params=payload, status=400)
    assert resp.json['err']
    assert resp.json['err_desc'] == "'field2' field is required on 'coin' block"


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
)
def test_create_intervention_cast_error(app, smart):
    payload = deepcopy(CREATE_INTERVENTION_PAYLOAD)
    payload['fields']['coin_raw'][0]['field2'] = 'not-an-integer'
    resp = app.post_json(URL + 'create-intervention/', params=payload, status=400)
    assert resp.json['err']
    assert "cannot cast 'field2' field to <class 'int'>" in resp.json['err_desc']


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
)
def test_create_intervention_missing_value(app, smart):
    field_payload = {
        'coin_raw': [
            {
                'type_objet': 'Candélabre',
                'type_objet_raw': 'Candélabre',
                'field2': None,
            },
        ],
    }
    payload = deepcopy(CREATE_INTERVENTION_PAYLOAD)
    payload['fields'] = field_payload
    resp = app.post_json(URL + 'create-intervention/', params=payload, status=400)
    assert resp.json['err']
    assert "field is required on 'coin' block" in resp.json['err_desc']


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
)
def test_create_intervention_missing_field(app, smart):
    field_payload = {
        'coin_raw': [
            {
                'type_objet': 'Candélabre',
                'type_objet_raw': 'Candélabre',
            },
        ],
    }
    payload = deepcopy(CREATE_INTERVENTION_PAYLOAD)
    payload['fields'] = field_payload
    resp = app.post_json(URL + 'create-intervention/', params=payload, status=400)
    assert resp.json['err']
    assert "field is required on 'coin' block" in resp.json['err_desc']


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, None, 500],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_create_intervention_twice(mocked_uuid4, app, smart):
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    assert resp.json['data']['status'] == 'registered'
    assert smart.wcs_requests.count() == 1

    # re-create intervention after it success: no error is returned, but no new request is sent
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    assert resp.json['data']['status'] == 'registered'
    assert smart.wcs_requests.count() == 1


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, None, 500],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_create_intervention_transport_error(mocked_uuid, app, freezer, smart):
    freezer.move_to('2021-07-08 00:00:00')
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    job = Job.objects.get(method_name='create_intervention_job')
    assert job.status == 'registered'
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert wcs_request.status == 'registered'
    assert 'failed to post' in wcs_request.result

    freezer.move_to('2021-07-08 00:00:03')
    smart.jobs()
    job = Job.objects.get(method_name='create_intervention_job')
    assert job.status == 'registered'
    assert job.update_timestamp > job.creation_timestamp
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert wcs_request.status == 'registered'
    assert 'failed to post' in wcs_request.result


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, None, None, ReadTimeout('timeout')],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_create_intervention_timeout_error(mocked_uuid, app, smart):
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    job = Job.objects.get(method_name='create_intervention_job')
    assert job.status == 'registered'
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert wcs_request.status == 'registered'
    assert 'failed to post' in wcs_request.result
    assert 'timeout' in wcs_request.result


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, None, 500],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_create_intervention_inconsistency_id_error(mocked_uuid4, app, freezer, smart):
    freezer.move_to('2021-07-08 00:00:00')
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert wcs_request.status == 'registered'
    job = Job.objects.get(method_name='create_intervention_job')
    assert job.status == 'registered'

    freezer.move_to('2021-07-08 00:00:03')
    wcs_request.delete()
    smart.jobs()
    job = Job.objects.get(method_name='create_intervention_job')
    assert job.status == 'failed'


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, 'not json content'],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_create_intervention_content_error(mocked_uuid, app, freezer, smart):
    freezer.move_to('2021-07-08 00:00:00')
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert wcs_request.status == 'registered'
    assert 'invalid json' in wcs_request.result


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, '400 Client Error', 400],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_create_intervention_client_error(mocked_uuid, app, freezer, smart):
    freezer.move_to('2021-07-08 00:00:00')
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert '400 Client Error' in wcs_request.result
    assert wcs_request.tries == 1
    assert wcs_request.status == 'registered'
    assert wcs_request.smart_requests.count() == 0
    job = Job.objects.get(method_name='create_intervention_job')
    assert job.status == 'registered'

    smart.jobs()
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert wcs_request.tries == 2
    assert wcs_request.status == 'registered'
    assert wcs_request.smart_requests.count() == 1
    smart_request = wcs_request.smart_requests.latest('id')
    assert smart_request.payload['creation_response']['status'] == 'registered'

    smart.jobs()
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert wcs_request.tries == 3
    assert wcs_request.status == 'registered'
    assert wcs_request.smart_requests.count() == 2
    smart_request = wcs_request.smart_requests.latest('id')
    assert smart_request.payload['creation_response']['status'] == 'registered'

    freezer.move_to('2021-07-08 01:00:01')
    smart.jobs()
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert wcs_request.tries == 4
    assert wcs_request.status == 'registered'
    assert wcs_request.smart_requests.count() == 3
    smart_request = wcs_request.smart_requests.latest('id')
    assert smart_request.payload['creation_response']['status'] == 'registered'

    freezer.move_to('2021-07-09 01:00:02')
    smart.jobs()
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert '400 Client Error' in wcs_request.result
    assert wcs_request.tries == 5
    assert wcs_request.status == 'failed'
    job = Job.objects.get(method_name='create_intervention_job')
    assert job.status == 'failed'
    assert '400 Client Error' in job.status_details['error_summary']
    assert wcs_request.smart_requests.count() == 4
    smart_request = wcs_request.smart_requests.latest('id')
    assert smart_request.payload['creation_response']['status'] == 'failed'

    # re-create intervention after it fails: a new request is sent
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    assert resp.json['data']['status'] == 'registered'
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert '400 Client Error' in wcs_request.result
    assert wcs_request.tries == 1
    assert wcs_request.status == 'registered'


@mock.patch('passerelle.utils.RequestSession.request')
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_create_intervention_timeout(mocked_uuid, mocked_get, app, freezer, smart):
    from tests.utils import FakedResponse

    mocked_get.side_effect = [
        FakedResponse(
            headers={'Content-Type': 'application/xml; charset=charset=utf-8'},
            status_code=200,
            content=INTERVENTION_TYPES,
        ),
        ReadTimeout('timeout'),
        FakedResponse(
            headers={'Content-Type': 'application/json'},
            status_code=200,
            content=get_json_file('create_intervention'),
        ),
    ]

    # synchronous requests
    freezer.move_to('2021-07-08 00:00:00')
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    assert mocked_get.call_count == 2
    assert mocked_get.call_args_list[0][1]['timeout'] == 10
    assert mocked_get.call_args_list[1][1]['timeout'] == 10

    # asynchronous request
    freezer.move_to('2021-07-09 01:00:02')
    smart.jobs()
    assert mocked_get.call_count == 3
    assert mocked_get.call_args_list[2][1]['timeout'] == 25


UPDATE_INTERVENTION_PAYLOAD = {
    'data': {
        'status': 'close manque info',
        'type_retour_cloture': 'Smart non Fait',
        'libelle_cloture': "rien à l'adresse indiquée",
        'commentaire_cloture': 'le commentaire',
    }
}
UPDATE_INTERVENTION_QUERY = UPDATE_INTERVENTION_PAYLOAD
WCS_RESPONSE_SUCCESS = '{"err": 0, "url": null}'
WCS_RESPONSE_ERROR = '{"err": 1, "err_class": "Access denied", "err_desc": null}'


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')],
    ['/api/forms/foo/2/hooks/update_intervention/', UPDATE_INTERVENTION_QUERY, WCS_RESPONSE_SUCCESS],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_update_intervention(mocked_uuid, app, smart, wcs_service):
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    assert CREATE_INTERVENTION_QUERY[
        'notification_url'
    ] == 'http://testserver/toulouse-smart/test/update-intervention?uuid=%s' % str(UUID)
    smart.wcs_requests.get(uuid=UUID)

    mocked_push = mock.patch(
        "passerelle.contrib.toulouse_smart.models.SmartRequest.push",
        return_value=False,
    )
    mocked_push.start()

    assert Job.objects.count() == 0
    url = URL + 'update-intervention?uuid=%s' % str(UUID)
    resp = app.post_json(url, params=UPDATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    assert resp.json['data']['uuid'] == str(UUID)
    assert resp.json['data']['payload']['data']['type_retour_cloture'] == 'Smart non Fait'
    smart_request = smart.wcs_requests.get(uuid=UUID).smart_requests.get()

    mocked_push.stop()
    assert Job.objects.count() == 1
    job = Job.objects.get(method_name='update_intervention_job')
    assert job.status == 'registered'
    smart.jobs()
    job = Job.objects.get(method_name='update_intervention_job')
    assert job.status == 'completed'
    smart_request = smart.wcs_requests.get(uuid=UUID).smart_requests.get()
    assert smart_request.result == {'err': 0, 'url': None}


def test_update_intervention_wrong_uuid(app, smart):
    with pytest.raises(WcsRequest.DoesNotExist):
        smart.wcs_requests.get(uuid=UUID)

    url = URL + 'update-intervention?uuid=0123456789'
    resp = app.post_json(url, params=UPDATE_INTERVENTION_PAYLOAD, status=400)
    assert resp.json['err']
    assert 'is not a valid UUID.' in resp.json['err_desc']
    assert SmartRequest.objects.count() == 0

    url = URL + 'update-intervention?uuid=%s' % str(UUID)
    resp = app.post_json(url, params=UPDATE_INTERVENTION_PAYLOAD, status=400)
    assert resp.json['err']
    assert 'Cannot find intervention' in resp.json['err_desc']
    assert SmartRequest.objects.count() == 0


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_update_intervention_job_wrong_service(mocked_uuid, app, smart, wcs_service):
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']

    wcs_service['default']['url'] = 'http://wrong.example.com'
    url = URL + 'update-intervention?uuid=%s' % str(UUID)
    resp = app.post_json(url, params=UPDATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    smart.jobs()
    job = Job.objects.get(method_name='update_intervention_job')
    assert job.status == 'completed'
    smart_request = smart.wcs_requests.get(uuid=UUID).smart_requests.get()
    assert 'Cannot find wcs service' in smart_request.result


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')],
    ['/api/forms/foo/2/hooks/update_intervention/', UPDATE_INTERVENTION_QUERY, WCS_RESPONSE_ERROR, 403],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_update_intervention_job_wcs_error(mocked_uuid, app, smart, wcs_service):
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']

    url = URL + 'update-intervention?uuid=%s' % str(UUID)
    resp = app.post_json(url, params=UPDATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    smart.jobs()
    job = Job.objects.get(method_name='update_intervention_job')
    assert job.status == 'completed'
    smart_request = smart.wcs_requests.get(uuid=UUID).smart_requests.get()
    assert smart_request.result == {'err': 1, 'err_class': 'Access denied', 'err_desc': None}


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')],
    ['/api/forms/foo/2/hooks/update_intervention/', UPDATE_INTERVENTION_QUERY, 'bla', 500],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_update_intervention_job_transport_error(mocked_uuid, app, freezer, smart, wcs_service):
    freezer.move_to('2021-07-08 00:00:00')
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']

    url = URL + 'update-intervention?uuid=%s' % str(UUID)
    resp = app.post_json(url, params=UPDATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    job = Job.objects.get(method_name='update_intervention_job')
    assert job.status == 'registered'
    smart_request = smart.wcs_requests.get(uuid=UUID).smart_requests.get()
    assert smart_request.result is None

    freezer.move_to('2021-07-08 00:00:03')
    smart.jobs()
    job = Job.objects.get(method_name='update_intervention_job')
    assert job.status == 'registered'
    assert job.update_timestamp > job.creation_timestamp
    smart_request = smart.wcs_requests.get(uuid=UUID).smart_requests.get()
    assert smart_request.result is None


ADD_MEDIA_PAYLOAD = {
    'files/0': {
        'filename': '201x201.jpg',
        'content_type': 'image/jpeg',
        'content': force_str(base64.b64encode(get_media_file('201x201.jpg'))),
    },
    'files/1': None,
}

ADD_MEDIA_QUERY = [get_media_file('201x201.jpg')]


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention/%s/media' % INTERVENTION_ID, ADD_MEDIA_QUERY, 200],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_add_media(mocked_uuid, app, smart):
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']

    url = resp.json['data']['payload']['add_media_url']
    url = URL + 'add-media?uuid=%s' % str(UUID)
    resp = app.post_json(url, params=ADD_MEDIA_PAYLOAD)
    assert not resp.json['err']
    assert resp.json['data']['uuid'] == str(UUID)
    assert resp.json['data']['nb_registered'] == 1
    assert Job.objects.count() == 1
    job = Job.objects.get(method_name='add_media_job')
    assert job.status == 'registered'
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    wcs_request_file = wcs_request.files.get(**job.parameters)
    path = wcs_request_file.content.path
    assert os.path.isfile(path)
    with wcs_request_file.content.open('rb') as desc:
        assert desc.read() == get_media_file('201x201.jpg')

    # smart not responding
    mocked_push = mock.patch(
        "passerelle.contrib.toulouse_smart.models.WcsRequestFile.push",
        return_value=False,
    )
    mocked_push.start()
    job = Job.objects.get(method_name='add_media_job')
    assert job.status == 'registered'

    # smart responding
    mocked_push.stop()
    smart.jobs()
    job = Job.objects.get(method_name='add_media_job')
    assert job.status == 'completed'
    wcs_request_file = wcs_request.files.get(**job.parameters)
    with pytest.raises(ValueError, match='no file associated'):
        assert not wcs_request_file.content.path
    assert not os.path.isfile(path)


def test_add_media_wrong_uuid(app, smart):
    with pytest.raises(WcsRequest.DoesNotExist):
        smart.wcs_requests.get(uuid=UUID)

    url = URL + 'add-media?uuid=%s' % str(UUID)
    resp = app.post_json(url, params=ADD_MEDIA_PAYLOAD, status=400)
    assert resp.json['err']
    assert 'Cannot find intervention' in resp.json['err_desc']
    assert WcsRequestFile.objects.count() == 0


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention/%s/media' % json.loads(get_json_file('create_intervention'))['id'], None, None, 500],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_add_media_error(mocked_uuid, app, freezer, smart):
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']

    freezer.move_to('2021-10-30 00:00:00')
    url = resp.json['data']['payload']['add_media_url']
    resp = app.post_json(url, params=ADD_MEDIA_PAYLOAD)
    job = Job.objects.get(method_name='add_media_job')
    assert job.status == 'registered'

    freezer.move_to('2021-10-30 00:00:03')
    smart.jobs()
    job = Job.objects.get(method_name='add_media_job')
    assert job.status == 'registered'
    assert job.update_timestamp > job.creation_timestamp


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention/%s/media' % INTERVENTION_ID, None, None, None, ReadTimeout('timeout')],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_add_media_timeout_error(mocked_uuid, app, freezer, smart):
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']

    freezer.move_to('2021-10-30 00:00:00')
    url = resp.json['data']['payload']['add_media_url']
    resp = app.post_json(url, params=ADD_MEDIA_PAYLOAD)
    job = Job.objects.get(method_name='add_media_job')
    assert job.status == 'registered'

    freezer.move_to('2021-10-30 00:00:03')
    smart.jobs()
    job = Job.objects.get(method_name='add_media_job')
    assert job.status == 'registered'
    assert job.update_timestamp > job.creation_timestamp


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention/%s/media' % INTERVENTION_ID, ADD_MEDIA_QUERY, 200],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, '400 Client Error', 400],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_add_media_with_create_intervention_failure(mocked_uuid, app, smart):
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']

    url = resp.json['data']['payload']['add_media_url']
    url = URL + 'add-media?uuid=%s' % str(UUID)
    resp = app.post_json(url, params=ADD_MEDIA_PAYLOAD)
    assert not resp.json['err']
    assert resp.json['data']['uuid'] == str(UUID)
    assert resp.json['data']['nb_registered'] == 1
    job = Job.objects.get(method_name='add_media_job')
    assert job.status == 'registered'

    # simulate failure on intervention creation
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    wcs_request.status = 'failed'
    wcs_request.save()
    wcs_request_file = wcs_request.files.get(**job.parameters)
    path = wcs_request_file.content.path
    assert os.path.isfile(path)

    smart.jobs()
    job = Job.objects.get(method_name='add_media_job')
    assert job.status == 'failed'
    assert 'related wcs request failed' in job.status_details['error_summary']


UPDATE_INTERVENTION_QUERY_ON_ASYNC_CREATION = {
    'creation_response': {
        'wcs_form_api_url': CREATE_INTERVENTION_PAYLOAD_EXTRA['form_api_url'],
        'wcs_form_number': CREATE_INTERVENTION_PAYLOAD_EXTRA['external_number'],
        'uuid': str(UUID),
        'payload': CREATE_INTERVENTION_QUERY,
        'result': json.loads(get_json_file('create_intervention')),
        'status': 'sent',
        'tries': 1,
    }
}
UPDATE_INTERVENTION_QUERY_ON_ASYNC_CREATION['creation_response']['result'][
    'interventionCreated'
] = '2021-07-07T14:19:31.302000'
UPDATE_INTERVENTION_QUERY_ON_ASYNC_CREATION['creation_response']['result'][
    'interventionDesired'
] = '2021-06-30T18:08:05'


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')],
    [
        '/api/forms/foo/2/hooks/update_intervention/',
        UPDATE_INTERVENTION_QUERY_ON_ASYNC_CREATION,
        WCS_RESPONSE_SUCCESS,
    ],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_create_intervention_async(mocked_uuid4, app, smart, wcs_service):
    mocked_wcs_request_push = mock.patch(
        "passerelle.contrib.toulouse_smart.models.WcsRequest.push",
        return_value=False,
    )
    mocked_smart_request_push = mock.patch(
        "passerelle.contrib.toulouse_smart.models.SmartRequest.push",
        return_value=False,
    )

    # smart and wcs are down
    mocked_wcs_request_push.start()
    mocked_smart_request_push.start()

    assert Job.objects.count() == 0
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert str(UUID) in CREATE_INTERVENTION_QUERY['notification_url']
    assert not resp.json['err']
    assert resp.json['data']['uuid'] == str(UUID)
    assert resp.json['data']['wcs_form_api_url'] == 'https://wcs.example.com/api/forms/foo/2/'
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert wcs_request.wcs_form_api_url == 'https://wcs.example.com/api/forms/foo/2/'
    assert wcs_request.wcs_form_number == '42-2'
    assert wcs_request.payload == CREATE_INTERVENTION_QUERY
    assert wcs_request.status == 'registered'

    assert Job.objects.count() == 1
    job = Job.objects.get(method_name='create_intervention_job')
    assert job.status == 'registered'

    # smart is up
    mocked_wcs_request_push.stop()
    smart.jobs()
    assert Job.objects.count() == 2

    job = Job.objects.get(method_name='create_intervention_job')
    assert job.status == 'completed'
    wcs_request = smart.wcs_requests.get(uuid=UUID)
    assert wcs_request.result['id'] == INTERVENTION_ID
    assert wcs_request.status == 'sent'

    job = Job.objects.get(method_name='update_intervention_job')
    assert job.status == 'registered'

    # wcs is up
    mocked_smart_request_push.stop()
    smart.jobs()

    job = Job.objects.get(method_name='update_intervention_job')
    assert job.status == 'completed'
    smart_request = wcs_request.smart_requests.get()
    assert smart_request.payload['creation_response']['uuid'] == str(UUID)
    assert smart_request.payload['creation_response']['result']['id'] == INTERVENTION_ID
    assert smart_request.payload['creation_response']['status'] == 'sent'


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention/%s/media' % INTERVENTION_ID, ADD_MEDIA_QUERY, 200],
    ['/v1/intervention', CREATE_INTERVENTION_QUERY, get_json_file('create_intervention')],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_add_media_async(mocked_uuid4, app, smart, freezer):
    mocked_wcs_request_push = mock.patch(
        "passerelle.contrib.toulouse_smart.models.WcsRequest.push",
        return_value=False,
    )

    # smart is down
    freezer.move_to('2021-10-30 00:00:00')
    mocked_wcs_request_push.start()
    resp = app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert not resp.json['err']
    url = resp.json['data']['payload']['add_media_url']
    resp = app.post_json(url, params=ADD_MEDIA_PAYLOAD)
    smart.jobs()
    job = Job.objects.get(method_name='create_intervention_job')
    assert job.status == 'registered'
    job = Job.objects.get(method_name='add_media_job')
    assert job.status == 'registered'
    assert str(job.after_timestamp) == '2021-10-30 00:10:00+00:00'

    # smart is up
    freezer.move_to('2021-10-30 00:00:03')
    mocked_wcs_request_push.stop()
    smart.jobs()
    job = Job.objects.get(method_name='create_intervention_job')
    assert job.status == 'completed'
    job = Job.objects.get(method_name='add_media_job')
    assert job.status == 'registered'

    # 10 minutes later
    freezer.move_to('2021-10-30 00:10:03')
    smart.jobs()
    job = Job.objects.get(method_name='add_media_job')
    assert job.status == 'completed'


@mock_response(
    ['/v1/type-intervention', None, INTERVENTION_TYPES],
    ['/v1/intervention', None, get_json_file('create_intervention')],
)
@mock.patch("django.db.models.fields.UUIDField.get_default", return_value=UUID)
def test_create_intervention_multiple_step(mocked_uuid4, app, smart):
    app.post_json(URL + 'create-intervention/', params=CREATE_INTERVENTION_PAYLOAD)
    assert smart.wcs_requests.get(uuid=UUID, wcs_form_step='initial')

    NEW_UUID = uuid.UUID('a' * 32)
    mocked_uuid4.return_value = NEW_UUID
    app.post_json(
        URL + 'create-intervention/', params=dict(CREATE_INTERVENTION_PAYLOAD, form_step='reclamation-1')
    )
    assert smart.wcs_requests.get(uuid=NEW_UUID, wcs_form_step='reclamation-1')


def test_pk_change_migration(transactional_db):
    migrate_from = [('toulouse_smart', '0005_auto_20220105_1514'), ('base', '0029_auto_20210202_1627')]
    migrate_to = [('toulouse_smart', '0012_migrate_jobs')]

    executor = MigrationExecutor(connection)
    old_apps = executor.loader.project_state(migrate_from).apps

    # state of the db is not important
    executor.migrate(migrate_from)

    Job = old_apps.get_model('base', 'Job')
    ContentType = old_apps.get_model('contenttypes', 'ContentType')
    ToulouseSmartResource = old_apps.get_model('toulouse_smart', 'ToulouseSmartResource')
    WcsRequest = old_apps.get_model('toulouse_smart', 'WcsRequest')
    WcsRequestFile = old_apps.get_model('toulouse_smart', 'WcsRequestFile')
    SmartRequest = old_apps.get_model('toulouse_smart', 'SmartRequest')

    tsr = ToulouseSmartResource.objects.create(slug='a', description='a')
    ct = ContentType.objects.get(app_label='toulouse_smart', model='toulousesmartresource')
    wcs_request = WcsRequest.objects.create(
        resource=tsr, wcs_form_api_url='https://example.com/1/', wcs_form_number='1'
    )
    WcsRequestFile.objects.create(resource=wcs_request, filename='a', content_type='a', content='a/b')
    SmartRequest.objects.create(resource=wcs_request, payload={}, result={})
    job = Job.objects.create(
        resource_type=ct,
        resource_pk=tsr.pk,
        method_name='create_intervention_job',
        parameters={
            'pk': wcs_request.pk,
        },
    )

    executor = MigrationExecutor(connection)
    executor.migrate(migrate_to)
    executor.loader.build_graph()
    apps = executor.loader.project_state(migrate_to).apps

    Job = apps.get_model('base', 'Job')
    ContentType = apps.get_model('contenttypes', 'ContentType')
    ToulouseSmartResource = apps.get_model('toulouse_smart', 'ToulouseSmartResource')
    WcsRequest = apps.get_model('toulouse_smart', 'WcsRequest')
    WcsRequestFile = apps.get_model('toulouse_smart', 'WcsRequestFile')

    wcs_request = WcsRequest.objects.get()
    job = Job.objects.get()
    assert job.parameters['pk'] == str(wcs_request.uuid)
    assert WcsRequest.objects.get(pk=job.parameters['pk'])
    assert wcs_request.files.get().resource_id == wcs_request.uuid
    assert wcs_request.smart_requests.get().resource_id == wcs_request.uuid
