# -*- coding: UTF-8 -*-

# Copyright (C) 2015-2016 Avencall
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


from hamcrest import (assert_that,
                      contains_exactly,
                      contains_inanyorder,
                      contains_string,
                      empty,
                      equal_to,
                      has_entries,
                      has_item,
                      has_items,
                      has_length,
                      instance_of,
                      is_not,
                      not_none)
from test_api import associations as a
from test_api import confd
from test_api import config
from test_api import fixtures
from test_api import helpers as h
from test_api import provd
from test_api.bus import BusClient
import unittest


client = h.user_import.csv_client()

class TestUserImport(unittest.TestCase):

    def get_import_field(self, response, fieldname, row_number=1):
        rows = response.item['created']
        return self.get_field(rows, fieldname, row_number)


    def get_update_field(self, response, fieldname, row_number=1):
        rows = response.item['updated']
        return self.get_field(rows, fieldname, row_number)


    def get_field(self, rows, fieldname, row_number=1):
        assert_that(rows,
                    has_length(row_number),
                    "CSV import did not create enough rows")

        row = rows[row_number - 1]
        assert_that(row,
                    has_entries({'row_number': row_number,
                                fieldname: not_none()}))

        return row[fieldname]


    def assert_error(self, response, expect):
        response.assert_status(400)
        assert_that(response.json, has_entries(errors=expect))


    def has_error_field(self, fieldname, row_number=1):
        return contains_exactly(has_entries(timestamp=instance_of(int),
                                    details=has_entries(row_number=row_number),
                                    message=contains_string(fieldname)))


    def test_given_required_fields_missing_then_error_returned(self):
        csv = [{"lastname": "missingfirstname"}]

        response = client.post("/users/import", csv)
        self.assert_error(response, self.has_error_field("firstname"))


    def test_given_entity_id_does_not_exist_then_error_returned(self):
        csv = [{"firstname": "entityfirstname",
                "entity_id": "999999999"}]

        response = client.post("/users/import", csv)
        self.assert_error(response, self.has_error_field("entity"))


    def test_given_csv_has_minimal_fields_for_a_user_then_user_imported(self):
        csv = [{"firstname": "Rîchard"}]

        response = client.post("/users/import", csv)
        user_id = self.get_import_field(response, 'user_id')
        user_uuid = self.get_import_field(response, 'user_uuid')

        user = confd.users(user_id).get().item
        assert_that(user, has_entries(firstname="Rîchard", uuid=user_uuid))


    def test_given_csv_has_all_fields_for_a_user_then_user_imported(self):
        csv = [{"firstname": "Rîchard",
                "lastname": "Lâpointe",
                "email": "richard@lapointe.org",
                "entity_id": "1",
                "language": "fr_FR",
                "username": "richardlapointe",
                "password": "secret",
                "outgoing_caller_id": '"Rîchy Cool" <4185551234>',
                "mobile_phone_number": "4181234567",
                "supervision_enabled": "1",
                "call_transfer_enabled": "0",
                "call_record_enabled": "1",
                "online_call_record_enabled": "0",
                "simultaneous_calls": "5",
                "ring_seconds": "10",
                "userfield": "userfield",
                "provisioning_code": "123456",
                "call_permission_password": "1234",
                "enabled": "1"
                }]

        response = client.post("/users/import", csv)
        user_id = self.get_import_field(response, 'user_id')
        user_uuid = self.get_import_field(response, 'user_uuid')

        user = confd.users(user_id).get().item
        assert_that(user, has_entries(firstname="Rîchard",
                                    lastname="Lâpointe",
                                    email="richard@lapointe.org",
                                    language="fr_FR",
                                    username="richardlapointe",
                                    password="secret",
                                    outgoing_caller_id='"Rîchy Cool" <4185551234>',
                                    mobile_phone_number="4181234567",
                                    supervision_enabled=True,
                                    call_transfer_enabled=False,
                                    call_record_enabled=True,
                                    online_call_record_enabled=False,
                                    simultaneous_calls=5,
                                    ring_seconds=10,
                                    userfield="userfield",
                                    call_permission_password='1234',
                                    enabled=True,
                                    uuid=user_uuid))


    def test_given_csv_has_cti_username_and_password(self):
        csv = [{"firstname": "Jean",
                "lastname": "Lefebvre",
                "username": "ctiusername",
                "password": "ctisecret",
                "cti_profile_enabled": 1,
                "cti_profile_name": "Agent"
                }]

        response = client.post("/users/import", csv)

        user_id = self.get_import_field(response, 'user_id')
        user = confd.users(user_id).get().item
        assert_that(user, has_entries(firstname="Jean",
                                    lastname="Lefebvre",
                                    username="ctiusername",
                                    password="ctisecret"))


    def test_given_csv_column_has_wrong_type_then_error_returned(self):
        csv = [{'firstname': 'invalidbool',
                'supervision_enabled': 'yeah'}]

        response = client.post("/users/import", csv)
        self.assert_error(response, self.has_error_field('supervision_enabled'))


    def test_given_user_contains_error_then_error_returned(self):
        csv = [{"firstname": "richard",
                "mobile_phone_number": "blah"}]

        response = client.post("/users/import", csv)
        self.assert_error(response, self.has_error_field('mobile_phone_number'))


    def test_given_import_users_with_duplicate_username_then_error_returned(self):
        csv = [{"firstname": "Thômas",
                "username": "thomas1",
                "password": "secret",
                "cti_profile_enabled": "1",
                "cti_profile_name": "Client"}]

        duplicate_user = {"firstname": "Thômas",
                        "username": "thomas1",
                        "password": "secret",
                        "cti_profile_enabled": "1",
                        "cti_profile_name": "Client"}

        csv.append(duplicate_user)
        response = client.post("/users/import", csv)
        self.assert_error(response, self.has_error_field("User already exists", 2))


    def test_given_csv_has_minimal_voicemail_fields_then_voicemail_imported(self):
        number, context = h.voicemail.generate_number_and_context()
        csv = [{"firstname": "Jôey",
                "voicemail_name": "Jôey VM",
                "voicemail_number": number,
                "voicemail_context": context}]

        response = client.post("/users/import", csv)
        voicemail_id = self.get_import_field(response, 'voicemail_id')

        voicemail = confd.voicemails(voicemail_id).get().item
        assert_that(voicemail, has_entries(name="Jôey VM",
                                        number=number,
                                        context=context))


    def test_given_csv_has_all_voicemail_fields_then_voicemail_imported(self):
        number, context = h.voicemail.generate_number_and_context()
        csv = [{"firstname": "Jôey",
                "voicemail_name": "Jôey VM",
                "voicemail_number": number,
                "voicemail_context": context,
                "voicemail_password": "1234",
                "voicemail_email": "email@example.com",
                "voicemail_attach_audio": "0",
                "voicemail_delete_messages": "1",
                "voicemail_ask_password": "0",
                }]

        response = client.post("/users/import", csv)
        voicemail_id = self.get_import_field(response, 'voicemail_id')

        voicemail = confd.voicemails(voicemail_id).get().item
        assert_that(voicemail, has_entries(name="Jôey VM",
                                        number=number,
                                        context=context,
                                        password="1234",
                                        email='email@example.com',
                                        attach_audio=False,
                                        delete_messages=True,
                                        ask_password=False))


    def test_given_voicemail_contains_error_then_error_returned(self):
        csv = [{"firstname": "Jôey",
                "voicemail_name": "Jôey VM",
                "voicemail_number": "%%%$",
                "voicemail_context": config.CONTEXT}]

        response = client.post("/users/import", csv)
        self.assert_error(response, self.has_error_field('number'))


    @fixtures.registrar(id='registrar1')
    def test_given_csv_has_minimal_line_fields_then_line_created(self, registrar):
        csv = [{"firstname": "Chârles",
                "line_protocol": "sip",
                "line_site": "registrar1",
                "context": config.CONTEXT}]

        response = client.post("/users/import", csv)
        line_id = self.get_import_field(response, 'line_id')

        line = confd.lines(line_id).get().item
        assert_that(line, has_entries(context=config.CONTEXT,
                                    protocol='sip',
                                    registrar=registrar['id']))


    @fixtures.registrar(id='registrar1')
    def test_given_csv_has_registrar_in_uppercase_then_line_created(self, registrar):
        csv = [{"firstname": "Chârles",
                "line_protocol": "sip",
                "line_site": "REGISTRAR1",
                "context": config.CONTEXT}]

        response = client.post("/users/import", csv)
        line_id = self.get_import_field(response, 'line_id')

        line = confd.lines(line_id).get().item
        assert_that(line, has_entries(registrar=registrar['id']))


    def test_given_csv_has_unknown_registrar_then_error_returned(self):
        csv = [{"firstname": "Chârles",
                "line_protocol": "sip",
                "line_site": "registrarX",
                "context": config.CONTEXT}]

        response = client.post("/users/import", csv)

        self.assert_error(response, self.has_error_field('registrar'))


    def test_given_line_has_error_then_error_returned(self):
        csv = [{"firstname": "Chârles",
                "line_protocol": "sip",
                "context": 'invalidcontext'}]

        response = client.post("/users/import", csv)
        self.assert_error(response, self.has_error_field('context'))


    def test_given_csv_has_minimal_sip_fields_then_sip_endpoint_created(self):
        csv = [{"firstname": "Chârles",
                "line_protocol": "sip",
                "context": config.CONTEXT}]

        response = client.post("/users/import", csv)
        sip_id = self.get_import_field(response, 'sip_id')

        sip = confd.endpoints.sip(sip_id).get().item
        assert_that(sip, has_entries(id=sip_id))


    def test_given_csv_has_all_sip_fields_then_sip_endpoint_created(self):
        csv = [{"firstname": "Chârles",
                "line_protocol": "sip",
                "context": config.CONTEXT,
                "sip_username": "sipusername",
                "sip_secret": "sipsecret"}]

        response = client.post("/users/import", csv)
        sip_id = self.get_import_field(response, 'sip_id')

        sip = confd.endpoints.sip(sip_id).get().item
        assert_that(sip, has_entries(username="sipusername",
                                    secret="sipsecret"))


    def test_given_csv_has_uppercase_sip_username(self):
        csv = [{"firstname": "Chârles",
                "line_protocol": "sip",
                "context": config.CONTEXT,
                "sip_username": "SIPidentity",
                "sip_secret": "sipsecret"}]

        response = client.post("/users/import", csv)
        sip_id = self.get_import_field(response, 'sip_id')

        sip = confd.endpoints.sip(sip_id).get().item
        assert_that(sip, has_entries(username="sipidentity",
                                    secret="sipsecret"))


    def test_given_csv_has_user_with_webrtc_activated_is_created(self):
        csv = [{"firstname": "Chârles WebRTC",
                "line_protocol": "webrtc",
                "context": config.CONTEXT}]

        response = client.post("/users/import", csv)
        sip_id = self.get_import_field(response, 'sip_id')

        expected_options = [
            ["webrtc", "yes"],
        ]

        sip_options = confd.endpoints.sip(sip_id).get().item['options']
        assert_that(sip_options, has_items(*expected_options))


    def test_given_csv_has_user_with_webrtc_deactivated_is_created_without_webrtc_option(self):
        csv = [{"firstname": "Chârles No WebRTC",
                "line_protocol": "sip",
                "context": config.CONTEXT}]

        response = client.post("/users/import", csv)
        sip_id = self.get_import_field(response, 'sip_id')

        unexpected_option_name = "webrtc"

        sip_options = confd.endpoints.sip(sip_id).get().item['options']
        option_webrtc = [opt for opt in sip_options if unexpected_option_name in opt]
        assert_that(option_webrtc, empty())


    def test_given_csv_has_user_with_ua_activated_is_created(self):
        csv = [{"firstname": "Chârles UA",
                "line_protocol": "ua",
                "context": config.CONTEXT}]

        response = client.post("/users/import", csv)
        sip_id = self.get_import_field(response, 'sip_id')

        expected_options = [
            ["webrtc", "ua"],
        ]

        sip_options = confd.endpoints.sip(sip_id).get().item['options']
        assert_that(sip_options, has_items(*expected_options))


    def test_given_csv_has_minimal_sccp_fields_then_sccp_endpoint_created(self):
        csv = [{"firstname": "Chârles",
                "line_protocol": "sccp",
                "context": config.CONTEXT}]

        response = client.post("/users/import", csv)
        sccp_id = self.get_import_field(response, 'sccp_id')

        sccp = confd.endpoints.sccp(sccp_id).get().item
        assert_that(sccp, has_entries(id=sccp_id))


    def test_given_csv_has_extension_fields_then_extension_created(self):
        exten = h.extension.find_available_exten(config.CONTEXT)
        csv = [{"firstname": "Géorge",
                "line_protocol": "sip",
                "exten": exten,
                "context": config.CONTEXT}]

        response = client.post("/users/import", csv)
        extension_id = self.get_import_field(response, 'extension_id')

        extension = confd.extensions(extension_id).get().item
        assert_that(extension, has_entries(exten=exten,
                                        context=config.CONTEXT))


    def test_given_csv_extension_has_errors_then_errors_returned(self):
        csv = [{"firstname": "Géorge",
                "line_protocol": "sip",
                "exten": "9999",
                "context": "invalid"}]

        response = client.post("/users/import", csv)
        self.assert_error(response, self.has_error_field('context'))


    def test_given_csv_has_minimal_incall_fields_then_incall_created(self):
        exten = h.extension.find_available_exten(config.INCALL_CONTEXT)
        csv = [{"firstname": "Pâscal",
                "line_protocol": "sccp",
                "context": config.CONTEXT,
                "incall_exten": exten,
                "incall_context": config.INCALL_CONTEXT}]

        response = client.post("/users/import", csv)
        incall_extension_id = self.get_import_field(response, 'incall_extension_id')

        extension = confd.extensions(incall_extension_id).get().item
        assert_that(extension, has_entries(exten=exten,
                                        context=config.INCALL_CONTEXT))


    def test_given_csv_has_all_incall_fields_then_incall_created(self):
        exten = h.extension.find_available_exten(config.INCALL_CONTEXT)
        csv = [{"firstname": "Pâscal",
                "line_protocol": "sccp",
                "context": config.CONTEXT,
                "incall_exten": exten,
                "incall_context": config.INCALL_CONTEXT,
                "incall_ring_seconds": "10"}]

        response = client.post("/users/import", csv)
        self.get_import_field(response, 'incall_extension_id')


    def test_given_csv_incall_has_errors_then_errors_returned(self):
        csv = [{"firstname": "Géorge",
                "line_protocol": "sip",
                "context": config.CONTEXT,
                "incall_exten": "9999",
                "incall_context": "invalid"}]

        response = client.post("/users/import", csv)
        self.assert_error(response, self.has_error_field('context'))


    def test_given_csv_has_cti_fields_then_cti_profile_associated(self):
        csv = [{"firstname": "Thômas",
                "username": "thomas11",
                "password": "secret",
                "cti_profile_enabled": "1",
                "cti_profile_name": "Client"}]

        response = client.post("/users/import", csv)
        cti_profile_id = self.get_import_field(response, 'cti_profile_id')
        user_id = self.get_import_field(response, 'user_id')

        user_cti_profile = confd.users(user_id).cti.get().item
        assert_that(user_cti_profile, has_entries(cti_profile_id=cti_profile_id,
                                                enabled=True))


    def test_given_csv_cti_profile_has_errors_then_errors_returned(self):
        csv = [{"firstname": "Thômas",
                "username": "thomas2",
                "password": "secret",
                "cti_profile_enabled": "1",
                "cti_profile_name": "InvalidProfile"}]

        response = client.post("/users/import", csv)
        self.assert_error(response, self.has_error_field('CtiProfile'))


    def test_given_csv_cti_password_should_be_longer_than_8_chars(self):
        csv = [{"firstname": "Thômas",
                "username": "thomas3",
                "password": "Pass12;",
                "cti_profile_enabled": "1",
                "cti_profile_name": "Client"}]

        response = client.post("/users/import", csv)
        assert_that(response.item['warnings'][0],
                    has_entries(message=contains_string("Password should contain at least 8 characters")))


    def test_given_csv_cti_password_should_contain_uppercase_letter(self):
        csv = [{"firstname": "Thômas",
                "username": "thomas4",
                "password": "secret123?!",
                "cti_profile_enabled": "1",
                "cti_profile_name": "Client"}]

        response = client.post("/users/import", csv)
        assert_that(response.item['warnings'][0],
                    has_entries(message=contains_string("Password should contain lowercase and uppercase letters, numbers and special characters")))

    def test_given_csv_cti_password_should_contain_number(self):
        csv = [{"firstname": "Thômas",
                "username": "thomas5",
                "password": "SecretPw!%",
                "cti_profile_enabled": "1",
                "cti_profile_name": "Client"}]

        response = client.post("/users/import", csv)
        assert_that(response.item['warnings'][0],
                    has_entries(message=contains_string("Password should contain lowercase and uppercase letters, numbers and special characters")))


    def test_given_csv_cti_password_should_contain_special_chars(self):
        csv = [{"firstname": "Thômas",
                "username": "thomas6",
                "password": "Secret123",
                "cti_profile_enabled": "1",
                "cti_profile_name": "Client"}]

        response = client.post("/users/import", csv)
        assert_that(response.item['warnings'][0],
                    has_entries(message=contains_string("Password should contain lowercase and uppercase letters, numbers and special characters")))


    def test_given_csv_cti_password_does_not_comply_rules(self):
        csv = [{"firstname": "Thômas",
                "username": "thomas7",
                "password": "secret",
                "cti_profile_enabled": "1",
                "cti_profile_name": "Client"}]

        response = client.post("/users/import", csv)
        assert_that(response.item['warnings'][0],
                    has_entries(message=contains_string("Password should contain lowercase and uppercase letters, numbers and special characters")))
        assert_that(response.item['warnings'][0],
                    has_entries(message=contains_string("Password should contain at least 8 characters")))


    @fixtures.csv_entry()
    def test_when_updating_user_fields_then_user_resource_updated(self, entry):
        csv = [{"uuid": entry['user_uuid'],
                "username": "joellachance",
                "password": "secret",
                }]

        response = client.put("/users/import", csv)
        assert_that(response.item['warnings'][0],
                    has_entries(message=contains_string("Password should contain lowercase and uppercase letters, numbers and special characters")))
        assert_that(response.item['warnings'][0],
                    has_entries(message=contains_string("Password should contain at least 8 characters")))


    def test_given_csv_cti_password_should_return_warnings_per_row(self):
        csv = [{"firstname": "toto",
                "username": "toto2",
                "password": "Pass1!",
                "cti_profile_enabled": "1",
                "cti_profile_name": "Client"},
            {"firstname": "Tomas",
                "username": "tomas2",
                "password": "Pass1!",
                "cti_profile_enabled": "1",
                "cti_profile_name": "Client"}
            ]

        response = client.post("/users/import", csv)
        assert_that(response.item['warnings'][0],
                    has_entries(message=contains_string("['Password should contain at least 8 characters']")))
        assert_that(response.item['warnings'][1],
                    has_entries(message=contains_string("['Password should contain at least 8 characters']")))


    @fixtures.call_permission()
    def test_given_csv_has_call_permission_then_call_permission_associated(self, call_permission):
        csv = [{"firstname": "Gërtrüde",
                "call_permissions": call_permission['name']}]

        expected = contains_exactly(
            has_entries(
                row_number=1,
                call_permission_ids=contains_exactly(call_permission['id'])))

        response = client.post("/users/import", csv)
        assert_that(response.item['created'], expected)

        user_id = response.item['created'][0]['user_id']
        user_call_permissions = confd.users(user_id).callpermissions.get().items
        assert_that(user_call_permissions, contains_exactly(has_entries(call_permission_id=call_permission['id'],
                                                                user_id=user_id)))


    @fixtures.call_permission()
    @fixtures.call_permission()
    def test_given_csv_has_multiple_call_permissions_then_all_call_permission_associated(self, perm1, perm2):
        permissions = "{perm1[name]};{perm2[name]}".format(perm1=perm1, perm2=perm2)
        csv = [{"firstname": "Rônald",
                "call_permissions": permissions}]

        expected = contains_exactly(
            has_entries(
                row_number=1,
                call_permission_ids=has_items(perm1['id'], perm2['id'])))

        response = client.post("/users/import", csv)
        assert_that(response.item['created'], expected)

        user_id = response.item['created'][0]['user_id']
        user_call_permissions = confd.users(user_id).callpermissions.get().items
        assert_that(user_call_permissions, contains_inanyorder(has_entries(call_permission_id=perm1['id'],
                                                                        user_id=user_id),
                                                            has_entries(call_permission_id=perm2['id'],
                                                                        user_id=user_id)))


    def test_given_call_permission_does_not_exist_then_error_raised(self):
        csv = [{"firstname": "Trévor",
                "call_permissions": "unknownperm"}]

        response = client.post("/users/import", csv)
        self.assert_error(response, self.has_error_field('CallPermission'))


    @fixtures.label()
    def test_given_csv_has_label_then_label_associated(self, label):
        csv = [{"firstname": "Gërtrüde",
                "labels": label['display_name']}]

        response = client.post("/users/import", csv)

        user_id = response.item['created'][0]['user_id']
        user = confd.users(user_id).get().item

        assert_that(user['labels'], contains_exactly(label['id']))


    @fixtures.label()
    @fixtures.label()
    def test_given_csv_has_multiple_labels_then_all_labels_associated(self, label1, label2):
        labels = "{label1};{label2}".format(label1=label1["display_name"],
                                            label2=label2["display_name"])
        csv = [{"firstname": "Rônald",
                "labels": labels}]

        response = client.post("/users/import", csv)

        user_id = response.item['created'][0]['user_id']
        user = confd.users(user_id).get().item
        assert_that(user['labels'], contains_inanyorder(label1['id'], label2['id']))


    def test_given_label_does_not_exist_then_error_raised(self):
        csv = [{"firstname": "Trévor",
                "labels": "unknownlabel"}]

        response = client.post("/users/import", csv)
        self.assert_error(response, self.has_error_field('labels'))


    def test_given_csv_has_all_resources_then_all_relations_created(self):
        exten = h.extension.find_available_exten(config.CONTEXT)
        incall_exten = h.extension.find_available_exten(config.INCALL_CONTEXT)
        vm_number = h.voicemail.find_available_number(config.CONTEXT)

        csv = [{"firstname": "Frânçois",
                "exten": exten,
                "context": config.CONTEXT,
                "line_protocol": "sip",
                "incall_exten": incall_exten,
                "incall_context": config.INCALL_CONTEXT,
                "voicemail_name": "francois",
                "voicemail_number": vm_number,
                "voicemail_context": config.CONTEXT,
                "cti_profile_name": "Client",
                }]

        response = client.post("/users/import", csv)

        user_id = self.get_import_field(response, 'user_id')
        line_id = self.get_import_field(response, 'line_id')
        voicemail_id = self.get_import_field(response, 'voicemail_id')
        extension_id = self.get_import_field(response, 'extension_id')
        incall_extension_id = self.get_import_field(response, 'incall_extension_id')
        sip_id = self.get_import_field(response, 'sip_id')
        cti_profile_id = self.get_import_field(response, 'cti_profile_id')

        response = confd.users(user_id).lines.get()
        assert_that(response.items, contains_exactly(has_entries(line_id=line_id)))

        response = confd.lines(line_id).extensions.get()
        assert_that(response.items, has_items(has_entries(extension_id=extension_id),
                                            has_entries(extension_id=incall_extension_id)))

        response = confd.users(user_id).voicemail.get()
        assert_that(response.item, has_entries(voicemail_id=voicemail_id))

        response = confd.lines(line_id).endpoints.sip.get()
        assert_that(response.item, has_entries(endpoint='sip',
                                            endpoint_id=sip_id))

        response = confd.users(user_id).cti.get()
        assert_that(response.item, has_entries(cti_profile_id=cti_profile_id))


    @fixtures.sip()
    @fixtures.extension()
    @fixtures.extension(context=config.INCALL_CONTEXT)
    @fixtures.voicemail()
    @fixtures.call_permission()
    def test_given_resources_already_exist_when_importing_then_resources_associated(self, sip, extension, incall, voicemail,
                                                                                    call_permission):
        cti_profile = h.cti_profile.find_by_name("Client")

        csv = [{"firstname": "importassociate",
                "exten": extension['exten'],
                "context": extension['context'],
                "line_protocol": "sip",
                "sip_username": sip['username'],
                "incall_exten": incall['exten'],
                "incall_context": incall['context'],
                "voicemail_number": voicemail['number'],
                "voicemail_context": voicemail['context'],
                "cti_profile_name": "Client",
                "call_permissions": call_permission['name'],
                }]

        response = client.post("/users/import", csv)

        user_id = self.get_import_field(response, 'user_id')
        line_id = self.get_import_field(response, 'line_id')

        response = confd.users(user_id).lines.get()
        assert_that(response.items, contains_exactly(has_entries(line_id=line_id)))

        response = confd.lines(line_id).extensions.get()
        assert_that(response.items, has_items(has_entries(extension_id=extension['id']),
                                            has_entries(extension_id=incall['id'])))

        response = confd.users(user_id).voicemail.get()
        assert_that(response.item, has_entries(voicemail_id=voicemail['id']))

        response = confd.lines(line_id).endpoints.sip.get()
        assert_that(response.item, has_entries(endpoint='sip',
                                            endpoint_id=sip['id']))

        response = confd.users(user_id).cti.get()
        assert_that(response.item, has_entries(cti_profile_id=cti_profile['id']))

        response = confd.users(user_id).callpermissions.get()
        assert_that(response.items, contains_exactly(has_entries(call_permission_id=call_permission['id'],
                                                        user_id=user_id)))


    @fixtures.call_permission()
    @fixtures.call_permission()
    @fixtures.label()
    @fixtures.label()
    def test_given_csv_has_more_than_one_entry_then_all_entries_imported(self, perm1, perm2, label1, label2):
        exten1 = h.extension.find_available_exten(config.CONTEXT)
        incall_exten1 = h.extension.find_available_exten('from-extern')
        vm_number1 = h.voicemail.find_available_number(config.CONTEXT)

        exten2 = h.extension.find_available_exten(config.CONTEXT, exclude=[exten1])
        incall_exten2 = h.extension.find_available_exten('from-extern', exclude=[incall_exten1])
        vm_number2 = h.voicemail.find_available_number(config.CONTEXT, exclude=[vm_number1])

        csv = [
            {"entity_id": "1",
            "firstname": "Jèan",
            "lastname": "Bâptiste",
            "email": "jean@baptiste.st",
            "mobile_phone_number": "5551234567",
            "ring_seconds": "15",
            "simultaneous_calls": "10",
            "language": "fr_FR",
            "outgoing_caller_id": '"Jean Le Grand" <5557654321>',
            "userfield": "userfield",
            "supervision_enabled": "1",
            "call_transfer_enabled": "1",
            "call_record_enabled": "0",
            "online_call_record_enabled": "0",
            "call_permission_password": "1234",
            "enabled": "1",
            "exten": exten1,
            "context": config.CONTEXT,
            "line_protocol": "sip",
            "sip_username": "jeansipusername",
            "sip_secret": "jeansippassword",
            "incall_exten": incall_exten1,
            "incall_context": 'from-extern',
            "incall_ring_seconds": "10",
            "voicemail_name": "jean",
            "voicemail_number": vm_number1,
            "voicemail_context": config.CONTEXT,
            "voicemail_password": "1234",
            "voicemail_email": "test@example.com",
            "voicemail_attach_audio": "1",
            "voicemail_delete_messages": "1",
            "voicemail_ask_password": "1",
            "cti_profile_name": "Client",
            "cti_profile_enabled": "1",
            "username": "jean",
            "password": "secret",
            "call_permissions": perm1['name'],
            "labels": label1['display_name']},
            {"entity_id": "1",
            "firstname": "Moùssa",
            "lastname": "Nôbamgo",
            "email": "moussa@nobamgo.ta",
            "mobile_phone_number": "5553456789",
            "ring_seconds": "20",
            "simultaneous_calls": "8",
            "language": "fr_FR",
            "outgoing_caller_id": '"Mousssssssaaaa" <5557654321>',
            "userfield": "userfield",
            "supervision_enabled": "1",
            "call_transfer_enabled": "1",
            "call_record_enabled": "1",
            "online_call_record_enabled": "1",
            "call_permission_password": "5678",
            "enabled": "0",
            "exten": exten2,
            "context": config.CONTEXT,
            "line_protocol": "sccp",
            "incall_exten": incall_exten2,
            "incall_context": 'from-extern',
            "incall_ring_seconds": "12",
            "voicemail_name": "moussa",
            "voicemail_number": vm_number2,
            "voicemail_context": config.CONTEXT,
            "voicemail_password": "2345",
            "voicemail_email": "test2@example.com",
            "voicemail_attach_audio": "1",
            "voicemail_delete_messages": "1",
            "voicemail_ask_password": "1",
            "cti_profile_name": "Client",
            "cti_profile_enabled": "1",
            "username": "moussa2",
            "password": "secret",
            "call_permissions": perm2['name'],
            "labels": label2['display_name']},
        ]

        response = client.post("/users/import", csv)

        assert_that(len(response.item['created']), equal_to(2))


    def test_given_field_group_is_empty_then_resource_is_not_created(self):
        group = {"cti_profile_enabled": "",
                "cti_profile_name": "",
                "username": "",
                "password": ""}
        yield self.import_empty_group, group, 'cti_profile_id'

        group = {"voicemail_name": "",
                "voicemail_number": "",
                "voicemail_context": "",
                "voicemail_password": "",
                "voicemail_email": "",
                "voicemail_attach_audio": "",
                "voicemail_delete_messages": "",
                "voicemail_ask_password": ""}
        yield self.import_empty_group, group, 'voicemail_id'

        group = {"line_protocol": ""}
        yield self.import_empty_group, group, 'line_id'

        group = {"line_protocol": "sip",
                "exten": "",
                "context": ""}
        yield self.import_empty_group, group, 'extension_id'

        exten = h.extension.find_available_exten(config.CONTEXT)
        group = {"line_protocol": "sip",
                "exten": exten,
                "context": config.CONTEXT,
                "incall_exten": "",
                "incall_context": ""}
        yield self.import_empty_group, group, 'incall_extension_id'

        group = {"call_permissions": ""}
        yield self.import_empty_group, group, 'call_permission_ids', []


    def import_empty_group(self, fields, parameter, expected=None):
        fields['firstname'] = "Abigaël"
        response = client.post("/users/import", [fields])
        entry = response.item['created'][0]
        assert_that(entry, has_entries({parameter: expected}))


    def test_given_2_entries_in_csv_imported_then_one_reload_event_published(self):
        BusClient.listen_delayed_events('config.ipbx.reload')
        csv = [
            {"firstname": "user1"},
            {"firstname": "user2"},
        ]
        all_optimized_commands = ['dialplan reload',
                                'module reload chan_sccp.so',
                                'module reload app_queue.so',
                                'sip reload']
        expected_commands = all_optimized_commands

        client.post("/users/import", csv)

        reload_events = BusClient.drain_events()

        assert_that(reload_events, has_length(1))
        assert_that(reload_events[0]['data']['commands'], contains_inanyorder(*expected_commands))


    @fixtures.csv_entry()
    def test_when_updating_user_fields_then_user_resource_updated(self, entry):
        csv = [{"uuid": entry['user_uuid'],
                "firstname": "Joël",
                "lastname": "Làchance",
                "language": "fr_FR",
                "email": "joel@lachance.fr",
                "username": "joellachance",
                "password": "secret",
                "outgoing_caller_id": '"Joël Spîffy" <4185551234>',
                "mobile_phone_number": "4181234567",
                "supervision_enabled": "1",
                "call_transfer_enabled": "0",
                "call_record_enabled": "1",
                "online_call_record_enabled": "0",
                "simultaneous_calls": "5",
                "ring_seconds": "10",
                "userfield": "userfield",
                "call_permission_password": "123",
                "enabled": "0",
                }]

        response = client.put("/users/import", csv)
        user_id = self.get_update_field(response, 'user_id')
        user_uuid = self.get_update_field(response, 'user_uuid')

        user = confd.users(user_id).get().item
        assert_that(user, has_entries(firstname="Joël",
                                    lastname="Làchance",
                                    email="joel@lachance.fr",
                                    language="fr_FR",
                                    username="joellachance",
                                    password="secret",
                                    outgoing_caller_id='"Joël Spîffy" <4185551234>',
                                    mobile_phone_number="4181234567",
                                    supervision_enabled=True,
                                    call_transfer_enabled=False,
                                    call_record_enabled=True,
                                    online_call_record_enabled=False,
                                    simultaneous_calls=5,
                                    ring_seconds=10,
                                    userfield="userfield",
                                    call_permission_password='123',
                                    enabled=False,
                                    uuid=user_uuid))


    @fixtures.csv_entry(voicemail=True)
    def test_when_updating_voicemail_fields_then_voicemail_updated(self, entry):
        number = h.voicemail.find_available_number(config.CONTEXT)

        csv = [{"uuid": entry["user_uuid"],
                "voicemail_name": "Jôey VM",
                "voicemail_number": number,
                "voicemail_context": config.CONTEXT,
                "voicemail_password": "1234",
                "voicemail_email": "email@example.com",
                "voicemail_attach_audio": "0",
                "voicemail_delete_messages": "1",
                "voicemail_ask_password": "0",
                }]

        response = client.put("/users/import", csv)
        voicemail_id = self.get_update_field(response, 'voicemail_id')

        voicemail = confd.voicemails(voicemail_id).get().item
        assert_that(voicemail, has_entries(name="Jôey VM",
                                        number=number,
                                        context=config.CONTEXT,
                                        password="1234",
                                        email='email@example.com',
                                        attach_audio=False,
                                        delete_messages=True,
                                        ask_password=False))


    @fixtures.csv_entry()
    def test_when_adding_voicemail_fields_then_voicemail_created(self, entry):
        number = h.voicemail.find_available_number(config.CONTEXT)

        csv = [{"uuid": entry['user_uuid'],
                "voicemail_name": "Jôey VM",
                "voicemail_number": number,
                "voicemail_context": config.CONTEXT,
                "voicemail_password": "1234",
                "voicemail_email": "email@example.com",
                "voicemail_attach_audio": "0",
                "voicemail_delete_messages": "1",
                "voicemail_ask_password": "0",
                }]

        response = client.put("/users/import", csv)
        voicemail_id = self.get_update_field(response, 'voicemail_id')

        voicemail = confd.voicemails(voicemail_id).get().item
        assert_that(voicemail, has_entries(name="Jôey VM",
                                        number=number,
                                        context=config.CONTEXT,
                                        password="1234",
                                        email='email@example.com',
                                        attach_audio=False,
                                        delete_messages=True,
                                        ask_password=False))


    @fixtures.csv_entry(line_protocol="sip")
    def test_when_updating_sip_line_fields_then_sip_updated(self, entry):
        csv = [{"uuid": entry["user_uuid"],
                "sip_username": "mynewsipusername",
                "sip_secret": "mynewsippassword",
                }]

        response = client.put("/users/import", csv)
        sip_id = self.get_update_field(response, 'sip_id')

        sip = confd.endpoints.sip(sip_id).get().item
        assert_that(sip, has_entries(username="mynewsipusername",
                                    secret="mynewsippassword"))


    @fixtures.csv_entry(line_protocol="sip")
    def test_when_updating_sip_line_webrtc_field_to_yes_then_field_updated(self, entry):
        csv = [{"uuid": entry["user_uuid"],
                "line_protocol": "webrtc",
                }]

        response = client.put("/users/import", csv)
        sip_id = self.get_update_field(response, 'sip_id')

        sip = confd.endpoints.sip(sip_id).get().item
        assert_that(sip['options'], has_item(contains_exactly("webrtc", "yes")))


    @fixtures.csv_entry(line_protocol="sip")
    def test_when_updating_sip_line_without_webrtc_field_then_options_not_updated(self, entry):
        csv = [{"uuid": entry["user_uuid"],
                }]

        response = client.put("/users/import", csv)
        sip_id = self.get_update_field(response, 'sip_id')

        sip = confd.endpoints.sip(sip_id).get().item
        assert_that(sip['options'], is_not(has_item(has_item("webrtc"))))


    @fixtures.user()
    @fixtures.line()
    @fixtures.sip(options=[["foo", "bar"]])
    def test_other_sip_options_are_unchanged_when_adding_webrtc_option(self, user, line, sip):
        csv = [{"uuid": user["uuid"],
                "line_protocol": "webrtc",
                }]

        expected_options = [
            ["foo", "bar"],
            ["webrtc", "yes"],
        ]

        with a.line_endpoint_sip(line, sip), a.user_line(user, line):
            response = client.put("/users/import", csv)
            sip_id = self.get_update_field(response, 'sip_id')

            sip_options = confd.endpoints.sip(sip_id).get().item['options']
            assert_that(sip_options, has_items(*expected_options))


    @fixtures.user()
    @fixtures.line()
    @fixtures.sip(options=[["foo", "bar"], ["webrtc", "no"]])
    def test_other_sip_options_are_unchanged_when_updating_webrtc_option(self, user, line, sip):
        csv = [{"uuid": user["uuid"],
                "line_protocol": "webrtc",
                }]

        expected_options = [
            ["foo", "bar"],
            ["webrtc", "yes"],
        ]

        with a.line_endpoint_sip(line, sip), a.user_line(user, line):
            response = client.put("/users/import", csv)
            sip_id = self.get_update_field(response, 'sip_id')

            sip_options = confd.endpoints.sip(sip_id).get().item['options']
            assert_that(sip_options, has_items(*expected_options))


    @fixtures.user()
    @fixtures.line()
    @fixtures.sip(options=[["foo", "bar"], ["webrtc", "yes"]])
    def test_other_sip_options_are_unchanged_when_removing_webrtc_option(self, user, line, sip):
        csv = [{"uuid": user["uuid"],
                "line_protocol": "sip",
                }]

        expected_options = [
            ["foo", "bar"],
        ]

        with a.line_endpoint_sip(line, sip), a.user_line(user, line):
            response = client.put("/users/import", csv)
            sip_id = self.get_update_field(response, 'sip_id')

            sip_options = confd.endpoints.sip(sip_id).get().item['options']
            assert_that(sip_options, has_items(*expected_options))


    @fixtures.user()
    @fixtures.line()
    @fixtures.sip(options=[["foo", "bar"], ["webrtc", "yes"]])
    def test_webrtc_option_is_removed_when_changing_line_to_sip(self, user, line, sip):
        csv = [{"uuid": user["uuid"],
                "line_protocol": "sip",
                }]

        with a.line_endpoint_sip(line, sip), a.user_line(user, line):
            response = client.put("/users/import", csv)
            sip_id = self.get_update_field(response, 'sip_id')

            sip_options = confd.endpoints.sip(sip_id).get().item['options']
            assert_that(sip_options, is_not(has_item(has_item("webrtc"))))


    @fixtures.csv_entry(line_protocol="sip")
    def test_when_updating_sip_line_webrtc_field_to_ua_then_field_updated(self, entry):
        csv = [{"uuid": entry["user_uuid"],
                "line_protocol": "ua",
                }]

        response = client.put("/users/import", csv)
        sip_id = self.get_update_field(response, 'sip_id')

        sip = confd.endpoints.sip(sip_id).get().item
        assert_that(sip['options'], has_item(contains_exactly("webrtc", "ua")))


    @fixtures.csv_entry()
    def test_when_adding_sip_line_then_sip_created(self, entry):
        csv = [{"uuid": entry["user_uuid"],
                "line_protocol": "sip",
                "sip_username": "createdsipusername",
                "sip_secret": "createdsippassword",
                }]

        response = client.put("/users/import", csv)
        sip_id = self.get_update_field(response, 'sip_id')

        sip = confd.endpoints.sip(sip_id).get().item
        assert_that(sip, has_entries(username="createdsipusername",
                                    secret="createdsippassword"))


    @fixtures.csv_entry()
    def test_when_adding_sccp_line_then_sccp_created(self, entry):
        csv = [{"uuid": entry["user_uuid"],
                "line_protocol": "sccp",
                }]

        response = client.put("/users/import", csv)
        sccp_id = self.get_update_field(response, 'sccp_id')

        sccp = confd.endpoints.sccp(sccp_id).get().item
        assert_that(sccp, has_entries(id=sccp_id))


    @fixtures.csv_entry(line_protocol="sip")
    def test_when_changing_line_protocol_then_error_raised(self, entry):
        csv = [{"uuid": entry["user_uuid"],
                "line_protocol": "sccp",
                }]

        response = client.put("/users/import", csv)

        self.assert_error(response, self.has_error_field('endpoint'))


    @fixtures.csv_entry(extension=True)
    def test_when_updating_extension_then_extension_updated(self, entry):
        exten = h.extension.find_available_exten(config.CONTEXT)

        csv = [{"uuid": entry["user_uuid"],
                "exten": exten,
                "context": config.CONTEXT,
                }]

        response = client.put("/users/import", csv)
        extension_id = self.get_update_field(response, 'extension_id')

        extension = confd.extensions(extension_id).get().item
        assert_that(extension, has_entries(exten=exten,
                                        context=config.CONTEXT))


    @fixtures.csv_entry(line_protocol="sip")
    def test_when_adding_extension_then_extension_created(self, entry):
        exten = h.extension.find_available_exten(config.CONTEXT)

        csv = [{"uuid": entry["user_uuid"],
                "exten": exten,
                "context": config.CONTEXT,
                }]

        response = client.put("/users/import", csv)
        extension_id = self.get_update_field(response, 'extension_id')

        extension = confd.extensions(extension_id).get().item
        assert_that(extension, has_entries(exten=exten,
                                        context=config.CONTEXT))


    @fixtures.csv_entry(incall=True)
    def test_when_updating_incall_fields_then_incall_updated(self, entry):
        exten = h.extension.find_available_exten(config.INCALL_CONTEXT)

        csv = [{"uuid": entry["user_uuid"],
                "incall_exten": exten,
                "incall_context": config.INCALL_CONTEXT,
                "incall_ring_seconds": "10"}]

        response = client.put("/users/import", csv)
        incall_id = self.get_update_field(response, 'incall_extension_id')

        extension = confd.extensions(incall_id).get().item
        assert_that(extension, has_entries(exten=exten,
                                        context=config.INCALL_CONTEXT))


    @fixtures.csv_entry(line_protocol="sip", extension=True)
    def test_when_adding_incall_then_incall_created(self, entry):
        exten = h.extension.find_available_exten(config.INCALL_CONTEXT)

        csv = [{"uuid": entry["user_uuid"],
                "incall_exten": exten,
                "incall_context": config.INCALL_CONTEXT,
                "incall_ring_seconds": "10"}]

        response = client.put("/users/import", csv)
        incall_id = self.get_update_field(response, 'incall_extension_id')

        extension = confd.extensions(incall_id).get().item
        assert_that(extension, has_entries(exten=exten,
                                        context=config.INCALL_CONTEXT))


    @fixtures.csv_entry(cti_profile=True,
                        cti_profile_name="Client")
    def test_when_updating_cti_profile_fields_then_cti_profile_updated(self, entry):
        csv = [{"uuid": entry['user_uuid'],
                "cti_profile_name": "Agent"}]

        response = client.put("/users/import", csv)
        user_id = self.get_update_field(response, 'user_id')
        cti_profile_id = h.cti_profile.find_id_for_profile("Agent")

        user_cti_profile = confd.users(user_id).cti.get().item
        assert_that(user_cti_profile, has_entries(cti_profile_id=cti_profile_id,
                                                enabled=True))


    @fixtures.csv_entry()
    def test_when_adding_cti_profile_fields_then_cti_profile_added(self, entry):
        csv = [{"uuid": entry['user_uuid'],
                "cti_profile_name": "Agent",
                "cti_profile_enabled": "1"}]

        response = client.put("/users/import", csv)
        user_id = self.get_update_field(response, 'user_id')

        cti_profile_id = h.cti_profile.find_id_for_profile("Agent")
        user_cti_profile = confd.users(user_id).cti.get().item
        assert_that(user_cti_profile, has_entries(cti_profile_id=cti_profile_id,
                                                enabled=True))


    @fixtures.csv_entry(call_permissions=2)
    @fixtures.call_permission()
    @fixtures.call_permission()
    def test_when_updating_call_permission_field_then_call_permissions_updated(self, entry, perm1, perm2):
        permissions = "{perm1[name]};{perm2[name]}".format(perm1=perm1, perm2=perm2)
        csv = [{"uuid": entry['user_uuid'],
                "call_permissions": permissions}]

        response = client.put("/users/import", csv)

        expected = contains_exactly(
            has_entries(
                row_number=1,
                call_permission_ids=has_items(perm1['id'], perm2['id'])))

        assert_that(response.item['updated'], expected)

        old_perm_id1, old_perm_id2 = entry['call_permission_ids']
        user_id = response.item['updated'][0]['user_id']

        response = confd.users(user_id).callpermissions.get()
        assert_that(response.items, contains_inanyorder(has_entries(call_permission_id=perm1['id'],
                                                                    user_id=user_id),
                                                        has_entries(call_permission_id=perm2['id'],
                                                                    user_id=user_id)))


    @fixtures.csv_entry(call_permissions=1)
    def test_when_call_permission_column_is_empty_then_call_permission_is_removed(self, entry):
        csv = [{"uuid": entry['user_uuid'],
                "call_permissions": ""}]

        response = client.put("/users/import", csv)

        expected = contains_exactly(
            has_entries(
                row_number=1,
                call_permission_ids=empty()))

        assert_that(response.item['updated'], expected)

        user_id = response.item['updated'][0]['user_id']

        response = confd.users(user_id).callpermissions.get()
        assert_that(response.items, empty())


    @fixtures.csv_entry(call_permissions=1)
    def test_when_call_permission_column_is_not_in_csv_then_call_permission_remains_unchanged(self, entry):
        csv = [{"uuid": entry['user_uuid']}]

        perm_id = entry['call_permission_ids'][0]

        response = client.put("/users/import", csv)

        expected = contains_exactly(
            has_entries(
                row_number=1,
                call_permission_ids=contains_exactly(perm_id)))

        assert_that(response.item['updated'], expected)


    @fixtures.csv_entry(labels=2)
    @fixtures.label()
    @fixtures.label()
    def test_when_updating_label_field_then_labels_updated(self, entry, label1, label2):
        labels = "{label1};{label2}".format(label1=label1["display_name"],
                                            label2=label2["display_name"])
        csv = [{"uuid": entry['user_uuid'],
                "labels": labels}]

        response = client.put("/users/import", csv)

        user_id = response.item['updated'][0]['user_id']
        user_labels = confd.users(user_id).get().item['labels']

        assert_that(user_labels, contains_inanyorder(label1['id'],
                                                    label2['id']))


    @fixtures.csv_entry(labels=1)
    def test_when_label_column_is_empty_then_label_is_removed(self, entry):
        csv = [{"uuid": entry['user_uuid'],
                "labels": ""}]

        response = client.put("/users/import", csv)

        user_id = response.item['updated'][0]['user_id']
        user_labels = confd.users(user_id).get().item['labels']

        assert_that(user_labels, empty())


    @fixtures.csv_entry(labels=1)
    def test_when_label_column_is_not_in_csv_then_label_is_not_removed(self, entry):
        csv = [{"uuid": entry['user_uuid'],
                "firstname": "Chârles"}]

        response = client.put("/users/import", csv)

        user_id = response.item['updated'][0]['user_id']
        user_labels = confd.users(user_id).get().item['labels']

        assert_that(user_labels, has_length(1))


    @fixtures.user(agent_number="456")
    def test_when_updating_user_then_agent_not_disassociated(self, user):
        csv = [{"uuid": user['uuid'],
                "firstname": "Chârles"}]

        client.put("/users/import", csv)

        updated_user = confd.users(user['id']).get().item

        assert_that(updated_user, has_entries(agent_number="456"))


    @fixtures.registrar(id='registrar1')
    @fixtures.registrar(id='registrar2')
    def test_when_updating_registrar_fields_then_registrar_updated(self, registrar1, registrar2):
        initial_csv = [{"firstname": "Chârles",
                        "line_protocol": "sip",
                        "line_site": "registrar1",
                        "context": config.CONTEXT}]

        response = client.post("/users/import", initial_csv)
        user_uuid = self.get_import_field(response, 'user_uuid')
        line_id = self.get_import_field(response, 'line_id')

        line = confd.lines(line_id).get().item

        assert_that(line, has_entries(registrar='registrar1'))

        updated_csv = [{"uuid": user_uuid,
                        "line_protocol": "sip",
                        "line_site": "registrar2"}]

        client.put("/users/import", updated_csv)

        updated_line = confd.lines(line_id).get().item

        assert_that(updated_line, has_entries(registrar='registrar2'))


    @fixtures.user()
    @fixtures.line()
    @fixtures.sip()
    @fixtures.extension()
    @fixtures.device()
    @fixtures.registrar(id='registrar2', registrar_ip='12.0.2.2')
    def test_when_updating_registrar_fields_then_provd_updated(self, user, line, sip, extension, device, registrar):
        csv = [{"uuid": user['uuid'],
                "line_site": "registrar2"}]

        with a.line_endpoint_sip(line, sip), a.user_line(user, line), \
                a.line_extension(line, extension), a.line_device(line, device):
            client.put("/users/import", csv)

            provd_config = provd.configs.get(device['id'])
            sip_line = provd_config['raw_config']['sip_lines']['1']

            assert_that(sip_line['registrar_ip'], equal_to(registrar['registrar_main']))


    @fixtures.user()
    @fixtures.line()
    @fixtures.sip()
    @fixtures.extension()
    @fixtures.device()
    def test_when_updating_exten_fields_then_provd_updated(self, user, line, sip, extension, device):
        csv = [{"uuid": user['uuid'],
                "exten": "1600"}]

        with a.line_endpoint_sip(line, sip), a.user_line(user, line), \
                a.line_extension(line, extension), a.line_device(line, device):
            client.put("/users/import", csv)

            provd_config = provd.configs.get(device['id'])
            sip_line = provd_config['raw_config']['sip_lines']['1']

            assert_that(sip_line['number'], equal_to('1600'))


    @fixtures.user()
    @fixtures.line()
    @fixtures.sip()
    @fixtures.extension()
    @fixtures.device()
    def test_when_updating_protocol_fields_then_errors_returned(self, user, line, sip, extension, device):
        csv = [{"uuid": user['uuid'],
                "line_protocol": "sccp"}]

        with a.line_endpoint_sip(line, sip), a.user_line(user, line), \
                a.line_extension(line, extension), a.line_device(line, device):
            response = client.put("/users/import", csv)

            response.assert_status(400)


    def check_error_on_update(self, entry, fields, error):
        entry = dict(entry)
        entry.update(fields)
        entry['uuid'] = entry['user_uuid']
        response = client.put("/users/import", [entry])
        self.assert_error(response, self.has_error_field(error))


    def test_given_csv_has_errors_then_errors_returned(self):
        csv = [{"firstname": "Thômas",
                "username": "thomas1234",
                "password": "secret",
                "bosssecretary": "no",
                "cti_profile_enabled": "1",
                "cti_profile_name": "Client"}]

        response = client.post("/users/import", csv)
        response.assert_status(400)
        assert_that(response.json[0], contains_string("Input Error"))


    @fixtures.csv_entry(extension=True, voicemail=True, incall=True, cti_profile=True, line_protocol="sip")
    @fixtures.csv_entry(extension=True, voicemail=True, incall=True, cti_profile=True, line_protocol="sccp")
    def test_given_2_entries_in_csv_then_2_entries_updated(self, entry1, entry2):
        exten1 = h.extension.find_available_exten(config.CONTEXT)
        incall_exten1 = h.extension.find_available_exten(config.INCALL_CONTEXT)
        vm_number1 = h.voicemail.find_available_number(config.CONTEXT)

        exten2 = h.extension.find_available_exten(config.CONTEXT, exclude=[exten1])
        incall_exten2 = h.extension.find_available_exten(config.INCALL_CONTEXT, exclude=[incall_exten1])
        vm_number2 = h.voicemail.find_available_number(config.CONTEXT, exclude=[vm_number1])

        csv = [
            {"uuid": entry1["user_uuid"],
            "entity_id": "1",
            "exten": exten1,
            "context": config.CONTEXT,
            "firstname": "Géorge",
            "lastname": "Bâptiste",
            "email": "george@baptiste.st",
            "mobile_phone_number": "5551234567",
            "ring_seconds": "15",
            "simultaneous_calls": "10",
            "language": "fr_FR",
            "outgoing_caller_id": '"Géorge Le Grand" <5557654321>',
            "userfield": "userfield",
            "supervision_enabled": "1",
            "call_transfer_enabled": "1",
            "call_record_enabled": "0",
            "online_call_record_enabled": "0",
            "call_permission_password": "321",
            "enabled": "1",
            "sip_username": "georgesipusername",
            "sip_secret": "georgesippassword",
            "incall_exten": incall_exten1,
            "incall_context": config.INCALL_CONTEXT,
            "incall_ring_seconds": "10",
            "voicemail_name": "george",
            "voicemail_number": vm_number1,
            "voicemail_context": config.CONTEXT,
            "voicemail_password": "1234",
            "voicemail_email": "test@example.com",
            "voicemail_attach_audio": "1",
            "voicemail_delete_messages": "1",
            "voicemail_ask_password": "1",
            "cti_profile_name": "Agent",
            "cti_profile_enabled": "1",
            "username": "george",
            "password": "secret"},
            {"uuid": entry2['user_uuid'],
            "entity_id": "1",
            "firstname": "Moùssa",
            "lastname": "Nôbamgo",
            "email": "moussa@nobamgo.sd",
            "mobile_phone_number": "5553456789",
            "ring_seconds": "20",
            "simultaneous_calls": "8",
            "language": "fr_FR",
            "outgoing_caller_id": '"Mousssssssaaaa" <5557654321>',
            "userfield": "userfield",
            "supervision_enabled": "1",
            "call_transfer_enabled": "1",
            "call_record_enabled": "1",
            "online_call_record_enabled": "1",
            "call_permission_password": "654",
            "enabled": "0",
            "exten": exten2,
            "context": config.CONTEXT,
            "incall_exten": incall_exten2,
            "incall_context": 'from-extern',
            "incall_ring_seconds": "12",
            "voicemail_name": "moussa",
            "voicemail_number": vm_number2,
            "voicemail_context": config.CONTEXT,
            "voicemail_password": "2345",
            "voicemail_email": "test2@example.com",
            "voicemail_attach_audio": "1",
            "voicemail_delete_messages": "1",
            "voicemail_ask_password": "1",
            "cti_profile_name": "Agent",
            "cti_profile_enabled": "1",
            "username": "moussa1",
            "password": "secret"},
        ]

        response = client.put("/users/import", csv)
        entry = response.item['updated']

        assert_that(entry, has_items(has_entries(user_id=entry1['user_id'],
                                                line_id=entry1['line_id'],
                                                extension_id=entry1['extension_id'],
                                                voicemail_id=entry1['voicemail_id'],
                                                cti_profile_id=entry1['cti_profile_id'],
                                                sip_id=entry1['sip_id'],
                                                incall_extension_id=entry1['incall_extension_id']),
                                    has_entries(user_id=entry2['user_id'],
                                                line_id=entry2['line_id'],
                                                extension_id=entry2['extension_id'],
                                                voicemail_id=entry2['voicemail_id'],
                                                cti_profile_id=entry2['cti_profile_id'],
                                                sccp_id=entry2['sccp_id'],
                                                incall_extension_id=entry2['incall_extension_id'])))


    @fixtures.csv_entry(extension=True, line_protocol="sip")
    @fixtures.csv_entry(extension=True, line_protocol="sip")
    def test_given_2_entries_in_csv_updated_then_one_reload_event_published(self, entry1, entry2):
        BusClient.listen_delayed_events('config.ipbx.reload')
        csv = [
            {"uuid": entry1["user_uuid"],
            "firstname": "Géorge"},
            {"uuid": entry2['user_uuid'],
            "firstname": "Moùssa"},
        ]
        all_optimized_commands = ['dialplan reload',
                                'module reload chan_sccp.so',
                                'module reload app_queue.so',
                                'sip reload']
        expected_commands = all_optimized_commands

        client.put("/users/import", csv)

        reload_events = BusClient.drain_events()

        assert_that(reload_events, has_length(1))
        assert_that(reload_events[0]['data']['commands'], contains_inanyorder(*expected_commands))


    @fixtures.user()
    @fixtures.sip()
    @fixtures.extension()
    @fixtures.extension(context=config.INCALL_CONTEXT)
    @fixtures.voicemail()
    @fixtures.call_permission()
    def test_given_resources_not_associated_when_updating_then_resources_associated(self, user, sip, extension, incall, voicemail,
                                                                                    call_permission):
        csv = [{"uuid": user['uuid'],
                "exten": extension['exten'],
                "context": extension['context'],
                "line_protocol": "sip",
                "sip_username": sip['username'],
                "incall_exten": incall['exten'],
                "incall_context": incall['context'],
                "voicemail_number": voicemail['number'],
                "voicemail_context": voicemail['context'],
                "cti_profile_name": "Client",
                "call_permissions": call_permission['name'],
                }]

        response = client.put("/users/import", csv)

        entry = response.item['updated'][0]

        response = confd.users(entry['user_id']).lines.get()
        assert_that(response.items, contains_exactly(has_entries(line_id=entry['line_id'])))

        response = confd.lines(entry['line_id']).extensions.get()
        assert_that(response.items, has_items(has_entries(extension_id=entry['extension_id']),
                                            has_entries(extension_id=entry['incall_extension_id'])))

        response = confd.users(entry['user_id']).voicemail.get()
        assert_that(response.item, has_entries(voicemail_id=entry['voicemail_id']))

        response = confd.lines(entry['line_id']).endpoints.sip.get()
        assert_that(response.item, has_entries(endpoint='sip',
                                            endpoint_id=entry['sip_id']))

        response = confd.users(entry['user_id']).cti.get()
        assert_that(response.item, has_entries(cti_profile_id=entry['cti_profile_id']))

        response = confd.users(entry['user_id']).callpermissions.get()
        assert_that(response.items, contains_inanyorder(has_entries(call_permission_id=call_permission['id'],
                                                                    user_id=entry['user_id'])))


    @fixtures.csv_entry(extension=True, voicemail=True, incall=True, cti_profile=True, line_protocol="sip",
                        call_permissions=1)
    @fixtures.call_permission()
    @fixtures.label()
    def test_given_each_field_updated_individually_then_entry_updated(self, entry, call_permission, label):
        exten = h.extension.find_available_exten(config.CONTEXT)
        incall_exten = h.extension.find_available_exten(config.INCALL_CONTEXT)
        vm_number = h.voicemail.find_available_number(config.CONTEXT)

        fields = {"entity_id": "1",
                "exten": exten,
                "context": config.CONTEXT,
                "firstname": "Fàbien",
                "lastname": "Bâptiste",
                "email": "fabien@baptiste.st",
                "mobile_phone_number": "5551234567",
                "ring_seconds": "15",
                "simultaneous_calls": "10",
                "language": "fr_FR",
                "outgoing_caller_id": '"Fàbien Le Grand" <5557654321>',
                "userfield": "userfield",
                "supervision_enabled": "1",
                "call_transfer_enabled": "1",
                "call_record_enabled": "1",
                "online_call_record_enabled": "1",
                "call_permission_password": "542",
                "enabled": "0",
                "sip_username": "fabiensipusername",
                "sip_secret": "fabiensippassword",
                "incall_exten": incall_exten,
                "incall_context": config.INCALL_CONTEXT,
                "incall_ring_seconds": "10",
                "voicemail_name": "fabien",
                "voicemail_number": vm_number,
                "voicemail_context": config.CONTEXT,
                "voicemail_password": "1234",
                "voicemail_email": "test@example.com",
                "voicemail_attach_audio": "1",
                "voicemail_delete_messages": "1",
                "voicemail_ask_password": "1",
                "cti_profile_name": "Agent",
                "cti_profile_enabled": "1",
                "username": "fabien",
                "call_permissions": call_permission['name'],
                "labels": label['display_name'],
                "password": "secret"}

        for name, value in fields.items():
            yield self.update_csv_field, entry['user_uuid'], name, value


    def update_csv_field(self, uuid, field, value):
        csv = [{'uuid': uuid, field: value}]
        response = client.put("/users/import", csv)
        response.assert_ok()
