import os
import unittest

import mock

from routersploit.exceptions import OptionValidationError
from routersploit.exploits import Exploit, GLOBAL_OPTS, Option
from tests.test_case import RoutersploitTestCase


def suffix(x):
    return "{}_suffix".format(x)


def SUFFIX(x):
    return "{}_SUFFIX".format(x)


class TestExploitFoo(Exploit):
    doo = Option(default=1, description="description_one")
    paa = Option(default=2, description="description_two")


class TestExploitBar(Exploit):
    doo = Option(default=3, description="description_three")
    paa = Option(default=4, description="description_four")


class TestExploitWithValidators(Exploit):
    doo = Option(default="default_value", description="description_three",
                 validators=suffix)
    paa = Option(default="default_value", description="description_three",
                 validators=(suffix, SUFFIX))


class OptionTest(RoutersploitTestCase):
    def setUp(self):
        self.exploit_foo = TestExploitFoo()
        self.exploit_bar = TestExploitBar()
        self.exploit_with_validators = TestExploitWithValidators()
        GLOBAL_OPTS.clear()

    def test_default_value(self):
        """ Test if default value is properly set. """
        self.assertEqual(self.exploit_foo.doo, 1)
        self.assertEqual(self.exploit_foo.paa, 2)
        self.assertEqual(self.exploit_bar.doo, 3)
        self.assertEqual(self.exploit_bar.paa, 4)

    def test_set_value(self):
        """ Test if descriptors are properly set. """
        self.exploit_foo.doo = "doopaa"
        self.exploit_foo.paa = "kajak"

        self.assertEqual(self.exploit_foo.doo, "doopaa")
        self.assertEqual(self.exploit_foo.paa, "kajak")
        self.assertEqual(self.exploit_bar.doo, 3)
        self.assertEqual(self.exploit_bar.paa, 4)

    def test_if_validator_is_NOT_applied_on_default_value(self):
        self.assertEqual(self.exploit_with_validators.doo, "default_value")

    def test_if_validator_is_applied_after_setting_value(self):
        self.exploit_with_validators.doo = "new_value"
        self.assertEqual(self.exploit_with_validators.doo, "new_value_suffix")

    def test_if_validator_is_applied_in_specific_order(self):
        self.exploit_with_validators.paa = "new_value"
        self.assertEqual(self.exploit_with_validators.paa,
                         "new_value_suffix_SUFFIX")

    def test_if_exploit_option_is_picked_up_before_global(self):
        GLOBAL_OPTS['doo'] = 'global_doo'
        self.exploit_bar.doo = 'value'
        self.exploit_foo.doo = 'value'
        self.assertEqual(self.exploit_bar.doo, 'value')
        self.assertEqual(self.exploit_foo.doo, 'value')

    def test_if_global_options_is_picked_up_before_default(self):
        GLOBAL_OPTS['doo'] = 'global_doo'
        self.assertEqual(self.exploit_bar.doo, 'global_doo')
        self.assertEqual(self.exploit_foo.doo, 'global_doo')

    def test_if_validators_are_applied_on_global_options(self):
        GLOBAL_OPTS['doo'] = 'global_doo'
        self.assertEqual(self.exploit_with_validators.doo, 'global_doo_suffix')

    def test_str_representation(self):
        with mock.patch.object(TestExploitFoo, "__module__",
                               new_callable=mock.PropertyMock) as mock_module:
            mock_module.return_value = "routersploit.modules.exploits.foo.bar"
            self.assertEqual(str(TestExploitFoo()),
                             os.path.join('exploits', 'foo', 'bar'))

    def test_exploit_options_property(self):
        self.assertEqual(self.exploit_bar.options, ['paa', 'target', 'doo'])
        self.assertEqual(self.exploit_foo.options, ['paa', 'target', 'doo'])

    def test_validate_setup(self):
        with self.assertRaises(OptionValidationError):
            self.exploit_bar.target = ""
            self.exploit_bar.validate_setup()


if __name__ == '__main__':
    unittest.main()
