"""
This module provides tests for :py:mod:`osusers.forms`.

"""
from unittest.mock import MagicMock, Mock, patch

from django import forms
from django.test import TestCase

from django.contrib.auth import get_user_model
from django.urls import reverse

from passlib.hash import sha512_crypt

from osusers.forms import (
    AddSshPublicKeyForm,
    ChangeOsUserPasswordForm,
    DUPLICATE_SSH_PUBLIC_KEY_FOR_USER,
    EditSshPublicKeyCommentForm,
    INVALID_SSH_PUBLIC_KEY,
)

from osusers.models import SshPublicKey, User

Customer = get_user_model()


class AddSshPublicKeyFormTest(TestCase):
    """
    Test for :py:class:`osusers.forms.AddSshPublicKeyForm`.

    """

    def _setup_hostingpackage(self):
        customer = Customer.objects.create_user("test")
        user = User.objects.create_user(customer=customer)
        self.hostingpackage = Mock(id=42, osuser=user)

    def test_constructor_needs_hostingpackage(self):
        instance = MagicMock()
        with self.assertRaises(KeyError) as ke:
            AddSshPublicKeyForm(instance)
        self.assertEqual(ke.exception.args[0], "hostingpackage")

    def test_constructor(self):
        self._setup_hostingpackage()
        instance = MagicMock()
        form = AddSshPublicKeyForm(instance, hostingpackage=self.hostingpackage)
        self.assertTrue(hasattr(form, "osuser"))
        self.assertEqual(form.osuser, self.hostingpackage.osuser)
        self.assertTrue(hasattr(form, "helper"))
        self.assertEqual(
            form.helper.form_action,
            reverse("add_ssh_key", kwargs={"package": self.hostingpackage.id}),
        )
        self.assertIn("publickeytext", form.fields)
        self.assertEqual(form.helper.inputs[0].name, "submit")

    @patch("osusers.forms.SshPublicKey.objects.parse_key_text")
    def test_clean_publickeytext_invalid(self, parse_key_text):
        self._setup_hostingpackage()
        instance = MagicMock()
        form = AddSshPublicKeyForm(instance, hostingpackage=self.hostingpackage)
        form.cleaned_data = {"publickeytext": "a bad key"}
        parse_key_text.side_effect = ValueError
        with self.assertRaises(forms.ValidationError) as ve:
            form.clean_publickeytext()
        self.assertEqual(ve.exception.message, INVALID_SSH_PUBLIC_KEY)

    @patch("osusers.forms.SshPublicKey.objects.parse_key_text")
    def test_clean_publickeytext_valid(self, _):
        self._setup_hostingpackage()
        instance = MagicMock()
        form = AddSshPublicKeyForm(instance, hostingpackage=self.hostingpackage)
        form.cleaned_data = {"publickeytext": "good key comment"}
        retval = form.clean_publickeytext()
        self.assertEqual(retval, "good key comment")

    def test_clean_none(self):
        self._setup_hostingpackage()
        instance = MagicMock()
        form = AddSshPublicKeyForm(instance, hostingpackage=self.hostingpackage)
        form.cleaned_data = {"publickeytext": None}
        form.clean()
        self.assertIsNone(form.cleaned_data["publickeytext"])

    @patch("osusers.forms.SshPublicKey.objects.parse_key_text")
    def test_clean_fresh(self, parse_key_text):
        self._setup_hostingpackage()
        instance = MagicMock()
        form = AddSshPublicKeyForm(instance, hostingpackage=self.hostingpackage)
        sshpubkey = "good key comment"
        form.cleaned_data = {"publickeytext": sshpubkey}
        parse_key_text.return_value = sshpubkey.split(" ")
        form.clean()
        self.assertEqual(form.cleaned_data["publickeytext"], "good key comment")

    @patch("osusers.forms.SshPublicKey.objects.parse_key_text")
    def test_clean_duplicate(self, parse_key_text):
        self._setup_hostingpackage()
        instance = MagicMock()
        form = AddSshPublicKeyForm(instance, hostingpackage=self.hostingpackage)
        SshPublicKey.objects.create(
            user=self.hostingpackage.osuser,
            algorithm="good",
            data="key",
            comment="comment",
        )
        sshpubkey = "good key comment"
        form.cleaned_data = {"publickeytext": sshpubkey}
        parse_key_text.return_value = sshpubkey.split(" ")
        form.clean()
        self.assertIn("publickeytext", form.errors)
        self.assertIn(DUPLICATE_SSH_PUBLIC_KEY_FOR_USER, form.errors["publickeytext"])

    @patch("osusers.admin.SshPublicKey.objects.parse_key_text")
    def test_save(self, parse_key_text):
        self._setup_hostingpackage()
        instance = MagicMock()
        form = AddSshPublicKeyForm(instance, hostingpackage=self.hostingpackage)
        sshpubkey = "good key comment"
        form.cleaned_data = {"publickeytext": sshpubkey}
        parse_key_text.return_value = sshpubkey.split(" ")
        retval = form.save()
        self.assertTrue(isinstance(retval, SshPublicKey))
        self.assertEqual(retval.algorithm, "good")
        self.assertEqual(retval.data, "key")
        self.assertEqual(retval.comment, "comment")


class ChangeOsUserPasswordFormTest(TestCase):
    """
    Test for :py:class:`osusers.forms.ChangeOsUserPasswordForm`.

    """

    def _setup_user(self):
        customer = Customer.objects.create_user("test")
        self.user = User.objects.create_user(customer=customer)

    def test_constructor(self):
        self._setup_user()
        form = ChangeOsUserPasswordForm(instance=self.user)
        self.assertTrue(hasattr(form, "instance"))
        self.assertEqual(form.instance, self.user)
        self.assertTrue(hasattr(form, "helper"))
        self.assertEqual(
            form.helper.form_action,
            reverse("set_osuser_password", kwargs={"slug": self.user.username}),
        )
        self.assertEqual(form.helper.inputs[0].name, "submit")

    def test_save(self):
        self._setup_user()
        form = ChangeOsUserPasswordForm(instance=self.user)
        form.cleaned_data = {"password1": "test"}
        user = form.save()
        self.assertTrue(sha512_crypt.verify("test", user.shadow.passwd))


class EditSshPublicKeyCommentFormTest(TestCase):
    """
    Test for :py:class:`osusers.forms.EditSshPublicKeyCommentForm`.

    """

    def _setup_hostingpackage(self):
        customer = Customer.objects.create_user("test")
        user = User.objects.create_user(customer=customer)
        self.hostingpackage = Mock(id=42, osuser=user)

    def test_constructor_needs_hostingpackage(self):
        instance = MagicMock()
        with self.assertRaises(KeyError) as ke:
            EditSshPublicKeyCommentForm(instance)
        self.assertEqual(ke.exception.args[0], "hostingpackage")

    def test_constructor(self):
        self._setup_hostingpackage()
        instance = MagicMock(id=1)
        form = EditSshPublicKeyCommentForm(
            instance=instance, hostingpackage=self.hostingpackage
        )
        self.assertTrue(hasattr(form, "osuser"))
        self.assertEqual(form.osuser, self.hostingpackage.osuser)
        self.assertIn("comment", form.fields)
        self.assertTrue(hasattr(form, "helper"))
        self.assertEqual(
            form.helper.form_action,
            reverse(
                "edit_ssh_key_comment",
                kwargs={"package": self.hostingpackage.id, "pk": instance.id},
            ),
        )
        self.assertEqual(form.helper.inputs[0].name, "submit")
