class ChangePasswordTests(APITestCase):

    def setUp(self):
        self.client = APIClient()

        self.user = UserFactory()
        self.user.set_password('Test123!')
        self.user.is_active = False
        self.user.save()

        self.token = ActionToken.objects.create(
            user=self.user,
            type='password_change',
        )

    def test_change_password(self):
        """
        Ensure we can change a password with a valid token and a good password
        """

        new_password = '******'

        data = {
            'token': self.token.key,
            'new_password': new_password
        }

        response = self.client.post(
            reverse('change_password'),
            data,
            format='json',
        )

        tokens = ActionToken.objects.filter(
            user=self.user,
            type='password_change',
            expired=False,
        )

        return_user_id = json.loads(response.content)['id']

        self.assertEqual(return_user_id, self.user.id)

        self.assertEqual(response.status_code, status.HTTP_200_OK)

        # We sync user after this change
        user = User.objects.get(id=self.user.id)
        self.assertTrue(user.check_password(new_password))

        self.assertTrue(len(tokens) == 0)

    def test_change_password_with_bad_token(self):
        """
        Ensure we can't change a password with an invalid token
        """
        data = {
            'token': 'test',
            'new_password': '******'
        }

        response = self.client.post(
            reverse('change_password'),
            data,
            format='json',
        )

        tokens = ActionToken.objects.filter(
            user=self.user,
            type='password_change',
        )

        content = {
            'token': "test is not a valid token.",
        }
        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        self.assertTrue(len(tokens) == 1)

        self.assertFalse(self.user.check_password('dWqq!Kld3#9dw'))

    def test_change_password_without_token(self):
        """
        Ensure we can't change a password without token
        """
        data = {
            'new_password': '******'
        }

        response = self.client.post(
            reverse('change_password'),
            data,
            format='json',
        )

        tokens = ActionToken.objects.filter(
            user=self.user,
            type='password_change',
        )

        content = {
            'token': ["This field is required."],
        }
        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        self.assertTrue(len(tokens) == 1)

        self.assertFalse(self.user.check_password('dWqq!Kld3#9dw'))

    def test_change_password_with_an_empty_token(self):
        """
        Ensure we can't change a password with an empty token
        """
        data = {
            'token': '',
            'new_password': '******'
        }

        response = self.client.post(
            reverse('change_password'),
            data,
            format='json',
        )

        tokens = ActionToken.objects.filter(
            user=self.user,
            type='password_change',
        )

        content = {
            'token': ["This field may not be blank."],
        }
        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        self.assertTrue(len(tokens) == 1)

        self.assertFalse(self.user.check_password('dWqq!Kld3#9dw'))

    def test_change_password_without_new_password(self):
        """
        Ensure we can't change a password without a new password
        """
        data = {
            'token': self.token.key,
        }

        response = self.client.post(
            reverse('change_password'),
            data,
            format='json',
        )

        tokens = ActionToken.objects.filter(
            user=self.user,
            type='password_change',
        )

        content = {
            'new_password': ["This field is required."],
        }
        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        self.assertTrue(len(tokens) == 1)

        self.assertFalse(self.user.check_password('dWqq!Kld3#9dw'))

    def test_change_password_with_an_empty_new_password(self):
        """
        Ensure we can't change a password without a valid new password
        """
        data = {
            'token': self.token.key,
            'new_password': '',
        }

        response = self.client.post(
            reverse('change_password'),
            data,
            format='json',
        )

        tokens = ActionToken.objects.filter(
            user=self.user,
            type='password_change',
        )

        content = {
            'new_password': ["This field may not be blank."],
        }
        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        self.assertTrue(len(tokens) == 1)

        self.assertFalse(self.user.check_password('dWqq!Kld3#9dw'))

    def test_change_password_with_a_weak_new_password(self):
        """
        Ensure we can't change a password with a weak new password
        """
        data = {
            'token': self.token.key,
            'new_password': '******'
        }

        response = self.client.post(
            reverse('change_password'),
            data,
            format='json',
        )

        tokens = ActionToken.objects.filter(
            user=self.user,
            type='password_change',
        )

        content = {
            'new_password': [
                'This password is too short. '
                'It must contain at least 8 characters.',
            ],
        }
        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

        self.assertTrue(len(tokens) == 1)

        self.assertFalse(self.user.check_password('akrent'))
class UsersIdTests(SerializerTestCase):
    @classmethod
    def setUpClass(cls):
        super(UsersIdTests, cls).setUpClass()
        cls.user_attrs = [
            'id',
            'url',
            'email',
            'first_name',
            'last_name',
            'is_active',
            'phone',
            'other_phone',
            'is_superuser',
            'is_staff',
            'last_login',
            'date_joined',
            'groups',
            'user_permissions',
        ]

    def setUp(self):
        self.client = APIClient()

        self.user = UserFactory()
        self.user.set_password('Test123!')
        self.user.save()

        self.admin = AdminFactory()
        self.admin.set_password('Test123!')
        self.admin.save()

    def test_retrieve_user_id_not_exist(self):
        """
        Ensure we can't retrieve a user that doesn't exist.
        """
        self.client.force_authenticate(user=self.admin)

        response = self.client.get(reverse(
            'user-detail',
            kwargs={'pk': 999},
        ))

        content = {"detail": "Not found."}
        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

    def test_retrieve_user_id_not_exist_without_permission(self):
        """
        Ensure we can't know a user doesn't exist without permission
        """
        self.client.force_authenticate(user=self.user)

        response = self.client.get(reverse(
            'user-detail',
            kwargs={'pk': 999},
        ))

        content = {
            'detail': 'You do not have permission to perform this action.'
        }
        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_retrieve_user(self):
        """
        Ensure we can retrieve a user.
        """
        self.client.force_authenticate(user=self.user)

        response = self.client.get(
            reverse(
                'user-detail',
                kwargs={'pk': self.user.id},
            ))

        content = json.loads(response.content)

        # Check id of the user
        self.assertEqual(content['id'], 1)

        self.validate_attrs(content)

        # Check the status code
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_retrieve_user_profile(self):
        """
        Ensure we can retrieve our details through /profile.
        """
        self.client.force_authenticate(user=self.user)

        response = self.client.get(reverse('profile', ))

        content = json.loads(response.content)

        # Check id of the user
        self.assertEqual(content['id'], 1)

        self.validate_attrs(content)

        # Check the status code
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_partial_update_user_with_permission(self):
        """
        Ensure we can update a specific user if caller has permission.
        """

        data = {
            "phone": "1234567890",
        }

        self.client.force_authenticate(user=self.admin)

        response = self.client.patch(
            reverse(
                'user-detail',
                kwargs={'pk': self.user.id},
            ),
            data,
            format='json',
        )

        content = json.loads(response.content)

        # Check if update was successful
        self.assertEqual(content['phone'], data['phone'])

        # Check id of the user
        self.assertEqual(content['id'], 1)

        self.validate_attrs(content)

        # Check the status code
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_partial_update_user_with_permission_change_password(self):
        """
        Ensure we can change password if current password is provided and the
        new password is validated.
        """

        data = {"password": "******", "new_password": "******"}

        self.client.force_authenticate(user=self.user)

        response = self.client.patch(
            reverse(
                'user-detail',
                kwargs={'pk': self.user.id},
            ),
            data,
            format='json',
        )

        content = json.loads(response.content)

        # Check id of the user
        self.assertEqual(content['id'], 1)

        self.validate_attrs(content)

        self.user.refresh_from_db()

        # Ensure that the password has been changed successfully
        self.assertTrue(self.user.check_password("!321tseT"))

        # Check the status code
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_update_user_with_permission(self):
        """
        Ensure we can update a specific user if caller has permission.
        Put requires a full update, including password.
        """
        data = {
            'password': '******',
            'new_password': '******',
            'phone': '1234567890',
            'first_name': 'Chuck',
            'last_name': 'Norris',
        }

        self.client.force_authenticate(user=self.user)

        response = self.client.put(
            reverse(
                'user-detail',
                kwargs={'pk': self.user.id},
            ),
            data,
            format='json',
        )

        content = json.loads(response.content)

        self.user.refresh_from_db()

        # Check if update was successful
        self.assertEqual(content['phone'], data['phone'])
        self.assertTrue(self.user.check_password("!321tset"))

        # Check id of the user
        self.assertEqual(content['id'], 1)

        self.validate_attrs(content)

        # Check the status code
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_update_user_without_permission(self):
        """
        Ensure we can't update a specific user doesn't have permission.
        """

        data = {
            "phone": "1234567890",
        }

        self.client.force_authenticate(user=self.user)

        response = self.client.patch(
            reverse(
                'user-detail',
                kwargs={'pk': self.admin.id},
            ),
            data,
            format='json',
        )

        content = {
            'detail': 'You do not have permission to perform this action.'
        }
        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_update_non_existent_user(self):
        """
        Ensure we get permission denied when trying to update an invalid user.
        """

        data = {
            "phone": "1234567890",
        }

        self.client.force_authenticate(user=self.user)

        response = self.client.patch(
            reverse(
                'user-detail',
                kwargs={'pk': 9999},
            ),
            data,
            format='json',
        )

        content = {
            'detail': 'You do not have permission to perform this action.'
        }
        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)

    def test_update_non_existent_user_as_admin(self):
        """
        Ensure we get not found when trying to update an invalid user as
        an admin.
        """

        data = {
            "phone": "1234567890",
        }

        self.client.force_authenticate(user=self.admin)

        response = self.client.patch(
            reverse(
                'user-detail',
                kwargs={'pk': 9999},
            ),
            data,
            format='json',
        )

        content = {'detail': 'Not found.'}

        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

    def test_update_user_weak_new_password(self):
        """
        Ensure we can't update our password if it is not validated.
        """

        data = {
            "phone": "1234567890",
            "password": "******",
            "new_password": "******",
        }

        self.client.force_authenticate(user=self.user)

        response = self.client.patch(
            reverse(
                'user-detail',
                kwargs={'pk': self.user.id},
            ),
            data,
            format='json',
        )

        content = {
            'new_password': [
                'This password is too common.',
                'This password is entirely numeric.'
            ]
        }
        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_update_user_missing_old_password(self):
        """
        Ensure we can't update our password if the current one is not provided.
        """

        data = {
            "phone": "1234567890",
            "new_password": "******",
        }

        self.client.force_authenticate(user=self.user)

        response = self.client.patch(
            reverse(
                'user-detail',
                kwargs={'pk': self.user.id},
            ),
            data,
            format='json',
        )

        content = {'password': '******'}
        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_update_user_wrong_old_password(self):
        """
        Ensure we can't update our password if the current one is wrong.
        """

        data = {
            "phone": "1234567890",
            "password": "******",
            "new_password": "******",
        }

        self.client.force_authenticate(user=self.user)

        response = self.client.patch(
            reverse(
                'user-detail',
                kwargs={'pk': self.user.id},
            ),
            data,
            format='json',
        )

        content = {'password': '******'}
        self.assertEqual(json.loads(response.content), content)

        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

    def test_delete_user_as_admin(self):
        """
        Ensure we can deactivate a user as an admin.
        """
        self.client.force_authenticate(user=self.admin)

        response = self.client.delete(
            reverse(
                'user-detail',
                kwargs={'pk': self.user.id},
            ), )
        self.user.refresh_from_db()

        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        self.assertFalse(self.user.is_active)

        self.user.is_active = True
        self.user.refresh_from_db()

    def test_delete_user(self):
        """
        Ensure that a user can't deactivate its own account.
        """
        self.client.force_authenticate(user=self.user)

        response = self.client.delete(
            reverse(
                'user-detail',
                kwargs={'pk': self.user.id},
            ), )
        self.user.refresh_from_db()

        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        self.assertFalse(self.user.is_active)

        self.user.is_active = True
        self.user.refresh_from_db()

    def test_delete_inexistent_user(self):
        """
        Ensure that deleting a non-existent user does nothing.
        """
        self.client.force_authenticate(user=self.user)

        response = self.client.delete(
            reverse(
                'user-detail',
                kwargs={'pk': 999},
            ), )

        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)