from itertools import permutations
from unittest import TestCase
from unittest.mock import MagicMock, patch

from flask.ext.login import AnonymousUserMixin

from sipa.model.wu.user import User, UserDB


class UserTestCase(TestCase):
    userdb_mock = MagicMock()

    def setUp(self):
        self.userdb_mock.reset_mock()

    def assert_userdata_passed(self, user, user_dict):
        self.assertEqual(user.name, user_dict['name'])
        self.assertEqual(user.group, user_dict.get('group', 'passive'))
        self.assertEqual(user.mail.value, user_dict['mail'])

    @staticmethod
    def patch_user_group(user_dict):
        return patch(
            'sipa.model.wu.user.User.define_group',
            MagicMock(return_value=user_dict.get('group', 'passive'))
        )

    @patch('sipa.model.wu.user.UserDB', userdb_mock)
    def test_explicit_init(self):
        sample_user = {
            'uid': 'testnutzer',
            'name': "Test Nutzer",
            'mail': "test@nutzer.de",
            'group': 'passive',
        }

        with self.patch_user_group(sample_user):
            user = User(
                uid=sample_user['uid'],
                name=sample_user['name'],
                mail=sample_user['mail'],
            )

        self.assert_userdata_passed(user, sample_user)
        assert self.userdb_mock.called

    @patch('sipa.model.wu.user.UserDB', userdb_mock)
    def test_define_group(self):
        sample_users = {
            # <uid>: <resulting_group>
            'uid1': 'passive',
            'uid2': 'active',
            'uid3': 'exactive',
        }

        def fake_search_in_group(uid, group_string):
            if group_string == "Aktiv":
                return sample_users[uid] == 'active'
            elif group_string == "Exaktiv":
                return sample_users[uid] == 'exactive'
            else:
                raise NotImplementedError

        for uid, group in sample_users.items():
            with patch('sipa.model.wu.user.search_in_group',
                       fake_search_in_group):
                user = User(uid=uid, name="", mail="")
                self.assertEqual(user.define_group(), group)
        return

    @patch('sipa.model.wu.user.UserDB', userdb_mock)
    def test_get_constructor(self):
        test_users = [
            {'uid': "uid1", 'name': "Name Eins", 'mail': "test@foo.bar"},
            {'uid': "uid2", 'name': "Mareike Musterfrau", 'mail': "test@foo.baz"},
            {'uid': "uid3", 'name': "Deine Mutter", 'mail': "shizzle@damn.onion"},
        ]

        for test_user in test_users:
            with self.patch_user_group(test_user), \
                 patch('sipa.model.wu.user.LdapConnector') as LdapConnectorMock:
                    LdapConnectorMock.fetch_user.return_value = test_user

                    user = User.get(test_user['uid'])
                    assert LdapConnectorMock.fetch_user.called

            self.assertIsInstance(user, User)
            self.assert_userdata_passed(user, test_user)

    def test_get_constructor_returns_anonymous(self):
        with patch('sipa.model.wu.user.LdapConnector') as LdapConnectorMock:
            LdapConnectorMock.fetch_user.return_value = None
            user = User.authenticate("foo", "bar")
        self.assertIsInstance(user, AnonymousUserMixin)


class UserDBTestCase(TestCase):
    def test_ipmask_validity_checker(self):
        valid_elements = ['1', '125', '255', '%']
        valid = permutations(valid_elements, 4)

        # probably not the most elegant choices, but that should do the trick
        invalid_elements = ['%%', '%%%', '1%1', '1%%1']
        invalid = []
        for p in valid:
            p = list(p)
            for inv in invalid_elements:
                invalid += [p[:i] + [inv] + p[i+1:] for i in range(4)]

        for ip_tuple in invalid:
            with self.assertRaises(ValueError):
                UserDB.test_ipmask_validity(".".join(ip_tuple))

        for ip_tuple in valid:
            with self.assertNotRaises(ValueError):
                UserDB.test_ipmask_validity(".".join(ip_tuple))

@@ -194,16 +194,83 @@
  UserDB%0A
+from sipa.model.wu.ldap_utils import UserNotFound, PasswordInvalid%0A
 %0A%0Aclass 
@@ -3384,16 +3384,1352 @@
 Mixin)%0A%0A
+    def test_authentication_passing(self):%0A        %22%22%22Test correct instanciation behaviour of %60User.authenticate%60.%0A%0A        It is checked whether the ldap is called correctly and%0A        instanciation is done using %60User.get%60.%0A        %22%22%22%0A        sample_users = %5B%0A            (%22uid%22, %22pass%22),%0A            (%22foo%22, %22bar%22),%0A            (%22baz%22, None),%0A        %5D%0A        for uid, password in sample_users:%0A            with patch('sipa.model.wu.user.LdapConnector') as ldap_mock, %5C%0A                 patch('sipa.model.wu.user.User.get') as get_mock:%0A                User.authenticate(uid, password)%0A%0A                self.assertEqual(ldap_mock.call_args%5B0%5D, (uid, password))%0A                self.assertEqual(get_mock.call_args%5B0%5D, (uid,))%0A%0A    def test_authentication_reraise_unknown_user(self):%0A        %22%22%22Test that certain exceptions are re-raised by %60User.authenticate%60.%0A%0A        Objects of interest are %60UserNotFound%60, %60PasswordInvalid%60.%0A        %22%22%22%0A        for exception_class in %5BUserNotFound, PasswordInvalid%5D:%0A            def raise_exception(): raise exception_class()%0A            with patch('sipa.model.wu.user.LdapConnector') as ldap_mock:%0A                ldap_mock().__enter__.side_effect = raise_exception%0A                with self.assertRaises(exception_class):%0A                    User.authenticate(username=None, password=None)%0A%0A
 %0Aclass U

Test `User.authenticate` works correctly