# Copyright 2015 Internap.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json

from flexmock import flexmock, flexmock_teardown
from hamcrest import assert_that, equal_to, is_
from netaddr import IPNetwork
from netaddr.ip import IPAddress

from netman.core.objects.interface_states import OFF, ON
from netman.core.objects.switch_descriptor import SwitchDescriptor
from netman.core.objects.vrrp_group import VrrpGroup
from tests import ExactIpNetwork
from tests.api import matches_fixture, open_fixture
from tests.api.base_api_test import BaseApiTest
from netman.api.api_utils import RegexConverter
from netman.api.switch_api import SwitchApi
from netman.api.switch_session_api import SwitchSessionApi
from netman.core.objects.access_groups import IN, OUT
from netman.core.objects.exceptions import IPNotAvailable, UnknownIP, UnknownVlan, UnknownAccessGroup, UnknownInterface, \
    UnknownSwitch, OperationNotCompleted, UnknownSession, SessionAlreadyExists, InvalidAccessGroupName
from netman.core.objects.interface import Interface
from netman.core.objects.port_modes import ACCESS, TRUNK, DYNAMIC, BOND_MEMBER
from netman.core.objects.vlan import Vlan
from netman.core.objects.bond import Bond


class SwitchApiTest(BaseApiTest):
    def setUp(self):
        super(SwitchApiTest, self).setUp()
        self.app.url_map.converters['regex'] = RegexConverter

        self.switch_factory = flexmock()
        self.switch_mock = flexmock()
        self.session_manager = flexmock()

        self.session_manager.should_receive("get_switch_for_session").and_raise(UnknownSession("patate"))

        SwitchApi(self.switch_factory, self.session_manager).hook_to(self.app)
        SwitchSessionApi(self.switch_factory, self.session_manager).hook_to(self.app)

    def tearDown(self):
        flexmock_teardown()

    def test_vlans_serialization(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('get_vlans').and_return([
            Vlan(2, "", [IPNetwork('3.3.3.3/24'), IPNetwork('2.2.2.2/24')],
                 vrrp_groups=[
                     VrrpGroup(id=1, ips=[IPAddress('2.2.2.2')], priority=100),
                     VrrpGroup(id=2, ips=[IPAddress('3.3.3.1')], priority=100)
                 ],
                 dhcp_relay_servers=[IPAddress("10.10.10.1")],
                 icmp_redirects=True),
            Vlan(1, "One", [IPNetwork('1.1.1.1/24')], vrf_forwarding="MY_VRF", access_group_in="Blah_blah",
                 vrrp_groups=[
                     VrrpGroup(id=1, ips=[IPAddress('1.1.1.2')], priority=90, hello_interval=5, dead_interval=15,
                               track_id='101', track_decrement=50)
                 ],
                 icmp_redirects=False),
            ]).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.get("/switches/my.switch/vlans")

        assert_that(code, equal_to(200))
        assert_that(result, matches_fixture("get_switch_hostname_vlans.json"))

    def test_single_vlan_serialization(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('get_vlan').with_args(1).and_return(
            Vlan(1, "One", [IPNetwork('1.1.1.1/24')], vrf_forwarding="MY_VRF", access_group_in="Blah_blah",
                 vrrp_groups=[
                     VrrpGroup(id=1, ips=[IPAddress('1.1.1.2')], priority=90, hello_interval=5, dead_interval=15,
                               track_id='101', track_decrement=50)
                 ],
                 icmp_redirects=False),
        ).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.get("/switches/my.switch/vlans/1")

        assert_that(code, equal_to(200))
        assert_that(result, matches_fixture("get_switch_hostname_vlans_vlan.json"))

    def test_get_vlans_can_be_empty(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('get_vlans').and_return([]).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.get("/switches/my.switch/vlans")

        assert_that(code, equal_to(200))
        assert_that(result, equal_to([]))

    def test_get_vlan_interfaces(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('get_vlan_interfaces').with_args(1).and_return(
            ["ethernet 1/4", "FastEthernet0/3", "GigabitEthernet0/8"]
        ).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.get("/switches/my.switch/vlans/1/interfaces")

        assert_that(code, equal_to(200))
        assert_that(result, matches_fixture("get_switch_hostname_vlans_vlan_interfaces.json"))

    def test_get_vlan_interfaces_nonexistent_vlan_raises(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('get_vlan_interfaces').with_args(4000).and_raise(UnknownVlan('4000')).ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.get("/switches/my.switch/vlans/4000/interfaces")

        assert_that(code, equal_to(404))
        assert_that(result, equal_to({'error': 'Vlan 4000 not found'}))

    def test_single_interfaces_serialization(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('get_interface').with_args('ethernet 1/4').and_return(
            Interface(name="ethernet 1/4", shutdown=False, port_mode=TRUNK, trunk_native_vlan=2999,
                      trunk_vlans=[3001, 3000, 3002], mtu=1500),
        ).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.get("/switches/my.switch/interfaces/ethernet 1/4")

        assert_that(code, equal_to(200))
        assert_that(result, matches_fixture("get_switch_hostname_interface.json"))

    def test_single_interfaces_is_inexistent(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('get_interface').with_args('ethernet 1/INEXISTENT').and_raise(
                UnknownInterface("ethernet 1/INEXISTENT")
        ).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.get("/switches/my.switch/interfaces/ethernet 1/INEXISTENT")

        assert_that(code, equal_to(404))
        assert_that(result, equal_to({'error': 'Unknown interface ethernet 1/INEXISTENT'}))

    def test_interfaces_serialization(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('get_interfaces').and_return([
            Interface(name="FastEthernet0/3", shutdown=True, port_mode=ACCESS, access_vlan=1999),
            Interface(name="GigabitEthernet0/6", shutdown=False, port_mode=DYNAMIC, access_vlan=1999, trunk_native_vlan=2999, trunk_vlans=[3001, 3000, 3002], auto_negotiation=True),
            Interface(name="ethernet 1/4", shutdown=False, port_mode=TRUNK, trunk_native_vlan=2999, trunk_vlans=[3001, 3000, 3002], mtu=1500),
            Interface(name="GigabitEthernet0/8", shutdown=False, bond_master=12, port_mode=BOND_MEMBER, trunk_native_vlan=None, trunk_vlans=[], auto_negotiation=False),
            ]).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.get("/switches/my.switch/interfaces")

        assert_that(code, equal_to(200))
        assert_that(result, matches_fixture("get_switch_hostname_interfaces.json"))

    def test_add_vlan(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('add_vlan').with_args(2000, "two_thousands").once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.post("/switches/my.switch/vlans", fixture="post_switch_hostname_vlans.json")

        assert_that(code, equal_to(201))

    def test_add_vlan_name_is_optionnal(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('add_vlan').with_args(2000, None).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.post("/switches/my.switch/vlans", data={"number": 2000})

        assert_that(code, equal_to(201))

    def test_add_vlan_name_is_optionnal_and_can_be_specified_empty(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('add_vlan').with_args(2000, None).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.post("/switches/my.switch/vlans", data={"number": "2000", "name": ""})

        assert_that(code, equal_to(201))

    def test_add_vlan_validates_the_name_if_present(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').never()

        result, code = self.post("/switches/my.switch/vlans", data={"number": 2000, "name": "deux milles"})
        assert_that(code, equal_to(400))
        assert_that(result, equal_to({'error': 'Vlan name is invalid'}))

        result, code = self.post("/switches/my.switch/vlans", data={"number": 4097})
        assert_that(code, equal_to(400))
        assert_that(result, equal_to({'error': 'Vlan number is invalid'}))

        result, code = self.post("/switches/my.switch/vlans", data={"number": 0})
        assert_that(code, equal_to(400))
        assert_that(result, equal_to({'error': 'Vlan number is invalid'}))

        result, code = self.post("/switches/my.switch/vlans", data={"number": "patate"})
        assert_that(code, equal_to(400))
        assert_that(result, equal_to({'error': 'Vlan number is invalid'}))

        result, code = self.post("/switches/my.switch/vlans", data={})
        assert_that(code, equal_to(400))
        assert_that(result, equal_to({'error': 'Vlan number is invalid'}))

        result, code = self.post("/switches/my.switch/vlans", raw_data="not even json")
        assert_that(code, equal_to(400))
        assert_that(result, equal_to({'error': 'Malformed content, should be a JSON object'}))

    def test_add_vlan_nameless(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('add_vlan').with_args(2000, None).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.post("/switches/my.switch/vlans", data={"number": 2000})

        assert_that(code, equal_to(201))

    def test_remove_vlan(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('remove_vlan').with_args(2000).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.delete("/switches/my.switch/vlans/2000")

        assert_that(code, equal_to(204))

    def test_configure_switch_port_access(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('set_access_mode').with_args("FastEthernet0/4").once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.put("/switches/my.switch/interfaces/FastEthernet0/4/port-mode", raw_data="access")

        assert_that(code, equal_to(204))

    def test_configure_switch_port_trunk(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('set_trunk_mode').with_args("FastEthernet0/4").once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.put("/switches/my.switch/interfaces/FastEthernet0/4/port-mode", raw_data="trunk")

        assert_that(code, equal_to(204))

    def test_configure_switch_port_unknown(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('set_switchport_mode').never()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.put("/switches/my.switch/interfaces/FastEthernet0/4/port-mode", raw_data="sirpatate")

        assert_that(code, equal_to(400))
        assert_that(result, equal_to({'error': 'Unknown port mode detected sirpatate'}))

    def test_configure_switch_bond_port_access(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('set_bond_access_mode').with_args(123).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.put("/switches/my.switch/bonds/123/port-mode", raw_data="access")

        assert_that(code, equal_to(204))

    def test_configure_switch_bond_port_trunk(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('set_bond_trunk_mode').with_args(123).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.put("/switches/my.switch/bonds/123/port-mode", raw_data="trunk")

        assert_that(code, equal_to(204))

    def test_edit_bond_spanning_tree(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('edit_bond_spanning_tree').with_args(5, edge=True).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.put("/switches/my.switch/bonds/5/spanning-tree",
                                fixture="put_switch_hostname_interfaces_intname_spanningtree.json")

        assert_that(code, equal_to(204))

    def test_edit_bond_spanning_tree_optional_params(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('edit_bond_spanning_tree').with_args(5).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.put("/switches/my.switch/bonds/5/spanning-tree", raw_data="{}")

        assert_that(code, equal_to(204))

    def test_edit_bond_spanning_tree_with_wrong_params(self):
        result, code = self.put("/switches/my.switch/bonds/5/spanning-tree",
                                raw_data="whizzle")

        assert_that(code, equal_to(400))
        assert_that(result['error'], is_('Malformed JSON request'))

        result, code = self.put("/switches/my.switch/bonds/5/spanning-tree",
                                raw_data='{"unknown_key": "value"}')

        assert_that(code, equal_to(400))
        assert_that(result['error'], is_('Unknown key: unknown_key'))

    def test_anonymous_switch(self):
        self.switch_factory.should_receive('get_switch_by_descriptor').with_args(SwitchDescriptor(
            hostname='my.switch',
            model='cisco',
            username='root',
            password='password',
            port=None,
            netman_server=None)).once().ordered().and_return(self.switch_mock)

        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('get_vlans').once().ordered().and_return([Vlan(1, "One"), Vlan(2, "Two")])
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.get("/switches/my.switch/vlans", headers={'Netman-Model':'cisco', 'Netman-Username':'root',
                                                                      'Netman-Password':'password'})
        assert_that(code, equal_to(200))

    def test_anonymous_switch_all_headers_set(self):
        result, code = self.get("/switches/my.switch/vlans", headers={'Netman-Model':'cisco'})
        assert_that(code, equal_to(400))
        assert_that(result, equal_to({'error': 'For anonymous switch usage, please specify headers: Netman-Model, Netman-Username and Netman-Password.'}))

        result, code = self.get("/switches/my.switch/vlans", headers={'Netman-Username':'root'})
        assert_that(code, equal_to(400))
        assert_that(result, equal_to({'error': 'For anonymous switch usage, please specify headers: Netman-Model, Netman-Username and Netman-Password.'}))

        result, code = self.get("/switches/my.switch/vlans", headers={'Netman-Password':'password'})
        assert_that(code, equal_to(400))
        assert_that(result, equal_to({'error': 'For anonymous switch usage, please specify headers: Netman-Model, Netman-Username and Netman-Password.'}))

    def test_anonymous_switch_can_have_a_port_specified(self):
        self.switch_factory.should_receive('get_switch_by_descriptor').with_args(SwitchDescriptor(
            hostname='my.switch',
            model='cisco',
            username='root',
            password='password',
            port=830,
            netman_server=None)).once().ordered().and_return(self.switch_mock)
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('get_vlans').and_return([Vlan(1, "One"), Vlan(2, "Two")]).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.get("/switches/my.switch/vlans", headers={'Netman-Model':'cisco', 'Netman-Username':'root',
                                                                      'Netman-Password':'password', 'Netman-Port':'830'})
        assert_that(code, equal_to(200))

    def test_anonymous_switch_port_has_to_be_integer(self):
        result, code = self.get("/switches/my.switch/vlans", headers={'Netman-Model':'cisco', 'Netman-Username':'root',
                                                                      'Netman-Password':'password', 'Netman-Port':'bleh'})
        assert_that(code, equal_to(400))

    def test_anonymous_switch_can_be_netman_proxied(self):
        self.switch_factory.should_receive('get_switch_by_descriptor').with_args(SwitchDescriptor(
            hostname='my.switch',
            model='cisco',
            username='root',
            password='password',
            port=None,
            netman_server='1.2.3.4')).once().ordered().and_return(self.switch_mock)
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('get_vlans').and_return([Vlan(1, "One"), Vlan(2, "Two")]).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.get("/switches/my.switch/vlans", headers={
            'Netman-Model':'cisco',
            'Netman-Username':'root',
            'Netman-Password':'password',
            'Netman-Proxy-Server':'1.2.3.4'
        })
        assert_that(code, equal_to(200))

    def test_anonymous_switch_can_be_multi_netman_proxied(self):
        self.switch_factory.should_receive('get_switch_by_descriptor').with_args(SwitchDescriptor(
            hostname='my.switch',
            model='cisco',
            username='root',
            password='password',
            port=None,
            netman_server=['1.2.3.4', '5.6.7.8'])).once().ordered().and_return(self.switch_mock)
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('get_vlans').and_return([Vlan(1, "One"), Vlan(2, "Two")]).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.get("/switches/my.switch/vlans", headers={
            'Netman-Model':'cisco',
            'Netman-Username':'root',
            'Netman-Password':'password',
            'Netman-Proxy-Server':' 1.2.3.4 , 5.6.7.8 '
        })
        assert_that(code, equal_to(200))

    def test_reset_interface(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('reset_interface').with_args('FastEthernet0/4').once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.put("/switches/my.switch/interfaces/FastEthernet0/4")

        assert_that(code, equal_to(204))

    def test_put_interface_with_data_is_not_implemented(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.put("/switches/my.switch/interfaces/FastEthernet0/4",
                                raw_data='providing data is not supported')

        assert_that(code, equal_to(501))

    def test_set_interface_state_off(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('set_interface_state').with_args('FastEthernet0/4', OFF).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.put("/switches/my.switch/interfaces/FastEthernet0/4/shutdown", raw_data='true')

        assert_that(code, equal_to(204))

    def test_set_interface_state_on(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('set_interface_state').with_args('FastEthernet0/4', ON).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.put("/switches/my.switch/interfaces/FastEthernet0/4/shutdown", raw_data='false')

        assert_that(code, equal_to(204))

    def test_set_interface_state_off_invalid_argument(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).never()
        self.switch_mock.should_receive('connect').never()
        self.switch_mock.should_receive('set_interface_state').with_args('FastEthernet0/4', OFF).never()
        self.switch_mock.should_receive('disconnect').never()

        result, code = self.put("/switches/my.switch/interfaces/FastEthernet0/4/shutdown", raw_data='Patate')

        assert_that(code, equal_to(400))
        assert_that(result, equal_to({'error': 'Unreadable content "patate". Should be either "true" or "false"'}))

    def test_set_interface_state_off_no_argument(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).never()
        self.switch_mock.should_receive('connect').never()
        self.switch_mock.should_receive('set_interface_state').with_args('FastEthernet0/4', OFF).never()
        self.switch_mock.should_receive('disconnect').never()

        result, code = self.put("/switches/my.switch/interfaces/FastEthernet0/4/shutdown")
        assert_that(code, equal_to(400))
        assert_that(result, equal_to({'error': 'Unreadable content "". Should be either "true" or "false"'}))

    def test_unset_interface_state(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('unset_interface_state').with_args('FastEthernet0/4').once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.delete("/switches/my.switch/interfaces/FastEthernet0/4/shutdown")

        assert_that(code, equal_to(204))

    def test_set_interface_auto_negotiation_state_off(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('set_interface_auto_negotiation_state').with_args('FastEthernet0/4', OFF).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.put("/switches/my.switch/interfaces/FastEthernet0/4/auto-negotiation", raw_data='false')

        assert_that(code, equal_to(204))

    def test_set_interface_auto_negotiation_state_on(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).once().ordered()
        self.switch_mock.should_receive('connect').once().ordered()
        self.switch_mock.should_receive('set_interface_auto_negotiation_state').with_args('FastEthernet0/4', ON).once().ordered()
        self.switch_mock.should_receive('disconnect').once().ordered()

        result, code = self.put("/switches/my.switch/interfaces/FastEthernet0/4/auto-negotiation", raw_data='true')

        assert_that(code, equal_to(204))

    def test_set_interface_auto_negotiation_state_off_invalid_argument(self):
        self.switch_factory.should_receive('get_switch').with_args('my.switch').and_return(self.switch_mock).never()
        self.switch_mock.should_receive('connect').never()
        self.switch_mock.should_receive('set_interface_auto_negotiation_state').with_args('FastEthernet0/4', OFF).never()
        self.switch_mock.should_receive('disconnect').never()

        result, code = self.put("/switches/my.switch/interfaces/FastEthernet0/4/auto-negotiation", raw_data='Patate')

        assert_that(code, equal_to(400))
        assert_that(result, equal_to({'error': 'Unreadable content "patate". Should be either "true" or "false"'}))

    def test_set_interface_auto_negotiation_state_off_no_argument(self):