mirror of
https://git.linux-kernel.at/oliver/ivatar.git
synced 2025-11-17 21:48:02 +00:00
2417 lines
81 KiB
Python
2417 lines
81 KiB
Python
"""
|
|
Test our views in ivatar.ivataraccount.views and ivatar.views
|
|
"""
|
|
|
|
import contextlib
|
|
|
|
# pylint: disable=too-many-lines
|
|
from urllib.parse import urlsplit
|
|
from io import BytesIO
|
|
from contextlib import suppress
|
|
import io
|
|
import os
|
|
import gzip
|
|
import xml.etree.ElementTree
|
|
import base64
|
|
import django
|
|
from django.test import TestCase
|
|
from django.test import Client
|
|
from django.test import override_settings
|
|
from django.urls import reverse
|
|
from django.core import mail
|
|
from django.core.cache import caches
|
|
from django.contrib.auth.models import User
|
|
from django.contrib.auth import authenticate
|
|
import hashlib
|
|
|
|
from libravatar import libravatar_url
|
|
|
|
from PIL import Image
|
|
|
|
os.environ["DJANGO_SETTINGS_MODULE"] = "ivatar.settings"
|
|
django.setup()
|
|
|
|
# pylint: disable=wrong-import-position
|
|
from ivatar import settings
|
|
from ivatar.ivataraccount.forms import MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT
|
|
from ivatar.ivataraccount.models import Photo, ConfirmedOpenId, ConfirmedEmail
|
|
from ivatar.utils import random_string
|
|
|
|
# pylint: enable=wrong-import-position
|
|
|
|
TEST_IMAGE_FILE = os.path.join(settings.STATIC_ROOT, "img", "deadbeef.png")
|
|
|
|
|
|
@override_settings()
|
|
class Tester(TestCase): # pylint: disable=too-many-public-methods
|
|
"""
|
|
Main test class
|
|
"""
|
|
|
|
client = Client()
|
|
user = None
|
|
username = random_string()
|
|
password = random_string()
|
|
email = "{}@{}.org".format(username, random_string())
|
|
# Dunno why random tld doesn't work, but I'm too lazy now to investigate
|
|
openid = "http://{}.{}.{}/".format(username, random_string(), "org")
|
|
first_name = random_string()
|
|
last_name = random_string()
|
|
|
|
def login(self):
|
|
"""
|
|
Login as user
|
|
"""
|
|
self.client.login(username=self.username, password=self.password)
|
|
|
|
def setUp(self):
|
|
"""
|
|
Prepare for tests.
|
|
- Create user
|
|
"""
|
|
self.user = User.objects.create_user(
|
|
username=self.username,
|
|
password=self.password,
|
|
first_name=self.first_name,
|
|
last_name=self.last_name,
|
|
)
|
|
# Disable caching
|
|
settings.CACHES["default"] = {
|
|
"BACKEND": "django.core.cache.backends.dummy.DummyCache",
|
|
}
|
|
caches._settings = None
|
|
with suppress(AttributeError):
|
|
# clear the existing cache connection
|
|
delattr(caches._connections, "default")
|
|
|
|
def test_new_user(self):
|
|
"""
|
|
Create a new user
|
|
"""
|
|
response = self.client.get(reverse("new_account"))
|
|
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
|
# Empty database / eliminate existing users
|
|
User.objects.all().delete()
|
|
url = reverse("new_account")
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"username": self.username,
|
|
"password1": self.password,
|
|
"password2": self.password,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "unable to create user?")
|
|
self.assertEqual(response.context[0]["user"].username, self.username)
|
|
|
|
def test_new_user_twice(self):
|
|
"""
|
|
Try to create a user that already exists
|
|
"""
|
|
response = self.client.get(reverse("new_account"))
|
|
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
|
# Due to setUp(), we already have this user!
|
|
url = reverse("new_account")
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"username": self.username,
|
|
"password1": self.password,
|
|
"password2": self.password,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "unable to create user?")
|
|
self.assertEqual(response.context[0]["user"].username, "")
|
|
self.assertContains(
|
|
response,
|
|
"A user with that username already exists.",
|
|
1,
|
|
200,
|
|
"can we create a user a second time???",
|
|
)
|
|
|
|
def test_set_password(self):
|
|
"""
|
|
Change the user password
|
|
"""
|
|
self.login()
|
|
response = self.client.get(reverse("password_set"))
|
|
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
|
self.password = random_string()
|
|
response = self.client.post(
|
|
reverse("password_set"),
|
|
{
|
|
"new_password1": self.password,
|
|
"new_password2": self.password,
|
|
},
|
|
follow=True,
|
|
)
|
|
|
|
self.assertEqual(response.status_code, 200, "cannot change password?")
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[0]),
|
|
"password changed successfully - please login again",
|
|
"password change not successful?",
|
|
)
|
|
|
|
self.assertIsNotNone(
|
|
authenticate(
|
|
username=self.username,
|
|
password=self.password,
|
|
),
|
|
"cannot authenticate with new password!?",
|
|
)
|
|
|
|
self.login()
|
|
response = self.client.get(reverse("profile"))
|
|
self.assertEqual(response.context[0]["user"].is_anonymous, False)
|
|
|
|
def test_add_email(self):
|
|
"""
|
|
Add e-mail address
|
|
"""
|
|
self.login()
|
|
response = self.client.get(reverse("add_email"))
|
|
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
|
# Avoid sending out mails
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
|
|
response = self.client.post(
|
|
reverse("add_email"),
|
|
{
|
|
"email": self.email,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "cannot add email?")
|
|
self.assertEqual(
|
|
len(response.context[0]["messages"]),
|
|
1,
|
|
"there must not be more or less than ONE (1) message",
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[0]),
|
|
"Address added successfully",
|
|
"unable to add mail address?",
|
|
)
|
|
|
|
def test_confirm_email(self):
|
|
"""
|
|
Confirm unconfirmed email
|
|
"""
|
|
self.login()
|
|
# Avoid sending out mails
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
|
|
response = self.client.post(
|
|
reverse("add_email"),
|
|
{
|
|
"email": self.email,
|
|
},
|
|
follow=True,
|
|
)
|
|
unconfirmed = self.user.unconfirmedemail_set.first()
|
|
verification_key = unconfirmed.verification_key
|
|
url = reverse("confirm_email", args=[verification_key])
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200, "unable to confirm mail address?")
|
|
|
|
self.assertEqual(
|
|
self.user.unconfirmedemail_set.count(),
|
|
0,
|
|
"there must not be any unconfirmed address, after confirming it",
|
|
)
|
|
self.assertEqual(
|
|
self.user.confirmedemail_set.count(),
|
|
1,
|
|
"there must not be more or less than ONE (1) confirmed address!",
|
|
)
|
|
|
|
def test_confirm_email_w_invalid_auth_key(self): # pylint: disable=invalid-name
|
|
"""
|
|
Test confirmation with invalid auth key
|
|
"""
|
|
self.login()
|
|
# Avoid sending out mails
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
|
|
response = self.client.post(
|
|
reverse("add_email"),
|
|
{
|
|
"email": self.email,
|
|
},
|
|
follow=True,
|
|
)
|
|
url = reverse("confirm_email", args=["x"])
|
|
response = self.client.get(url, follow=True)
|
|
self.assertEqual(
|
|
response.status_code,
|
|
200,
|
|
"Not able to request confirmation - without verification key?",
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Verification key incorrect",
|
|
"Confirm w/o verification key does not produce error message?",
|
|
)
|
|
|
|
def test_confirm_email_w_non_existing_auth_key(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test confirmation with non existing auth key
|
|
"""
|
|
self.login()
|
|
# Avoid sending out mails
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
|
|
response = self.client.post(
|
|
reverse("add_email"),
|
|
{
|
|
"email": self.email,
|
|
},
|
|
follow=True,
|
|
)
|
|
url = reverse("confirm_email", args=["x" * 64])
|
|
response = self.client.get(url, follow=True)
|
|
self.assertEqual(
|
|
response.status_code,
|
|
200,
|
|
"Not able to request confirmation - without verification key?",
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Verification key does not exist",
|
|
"Confirm w/o non existing key does not produce error message?",
|
|
)
|
|
|
|
def test_remove_confirmed_email(self):
|
|
"""
|
|
Remove confirmed email
|
|
"""
|
|
self.login()
|
|
# Avoid sending out mails
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
|
|
response = self.client.post(
|
|
reverse("add_email"),
|
|
{
|
|
"email": self.email,
|
|
},
|
|
) # Create test address
|
|
unconfirmed = self.user.unconfirmedemail_set.first()
|
|
verification_key = unconfirmed.verification_key
|
|
url = reverse("confirm_email", args=[verification_key])
|
|
self.client.get(url) # Confirm
|
|
url = reverse(
|
|
"remove_confirmed_email", args=[self.user.confirmedemail_set.first().id]
|
|
)
|
|
response = self.client.post(url, follow=True)
|
|
self.assertEqual(
|
|
response.status_code, 200, "unable to remove confirmed address?"
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Address removed",
|
|
"Removing confirmed mail does not work?",
|
|
)
|
|
|
|
def test_remove_not_existing_confirmed_email(self): # pylint: disable=invalid-name
|
|
"""
|
|
Try removing confirmed mail that doesn't exist
|
|
"""
|
|
self.login()
|
|
url = reverse("remove_confirmed_email", args=[1234])
|
|
response = self.client.post(url, follow=True)
|
|
self.assertEqual(
|
|
response.status_code, 200, "removing email does not redirect to profile?"
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[0]),
|
|
"Address does not exist",
|
|
"Removing not existing (confirmed) address, should produce an\
|
|
error message!",
|
|
)
|
|
|
|
def test_remove_unconfirmed_email(self):
|
|
"""
|
|
Remove unconfirmed email
|
|
"""
|
|
self.login()
|
|
# Avoid sending out mails
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
|
|
response = self.client.post(
|
|
reverse("add_email"),
|
|
{
|
|
"email": self.email,
|
|
},
|
|
) # Create test address
|
|
url = reverse(
|
|
"remove_unconfirmed_email", args=[self.user.unconfirmedemail_set.first().id]
|
|
)
|
|
response = self.client.post(url, follow=True)
|
|
self.assertEqual(
|
|
response.status_code, 200, "unable to remove unconfirmed address?"
|
|
)
|
|
# Take care, since we do not fetch any page now, the message we need
|
|
# to check is the _second_ (aka [1], since first is [0])
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[1]),
|
|
"Address removed",
|
|
"Removing unconfirmed mail does not work?",
|
|
)
|
|
|
|
def test_gravatar_photo_import(self):
|
|
"""
|
|
import photo from Gravatar (with known mail address)
|
|
"""
|
|
self.login()
|
|
# Avoid sending out mails
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
|
|
response = self.client.post(
|
|
reverse("add_email"),
|
|
{
|
|
"email": "oliver@linux-kernel.at", # Wow, static :-[
|
|
},
|
|
) # Create test address
|
|
unconfirmed = self.user.unconfirmedemail_set.first()
|
|
verification_key = unconfirmed.verification_key
|
|
url = reverse("confirm_email", args=[verification_key])
|
|
self.client.get(url) # Confirm
|
|
|
|
url = reverse("import_photo", args=[self.user.confirmedemail_set.first().id])
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"photo_Gravatar": 1,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(
|
|
response.status_code, 200, "unable to import photo from Gravatar?"
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Gravatar image successfully imported",
|
|
"Importing gravatar photo did not work?",
|
|
)
|
|
self.assertIsInstance(
|
|
self.user.photo_set.first(), Photo, "why is there no Photo (instance)?"
|
|
)
|
|
|
|
def test_raw_image(self):
|
|
"""
|
|
test raw image view (as seen in profile <img src=
|
|
"""
|
|
|
|
# Ensure we have a photo
|
|
self.test_gravatar_photo_import()
|
|
response = self.client.get(
|
|
reverse("raw_image", args=[self.user.photo_set.first().id])
|
|
)
|
|
self.assertEqual(response.status_code, 200, "cannot fetch photo?")
|
|
# Probably not the best way to access the content type
|
|
self.assertEqual(response["Content-Type"], "image/jpg", "Content type wrong!?")
|
|
|
|
self.assertEqual(
|
|
bytes(response.content),
|
|
bytes(self.user.photo_set.first().data),
|
|
"raw_image should return the same content as if we\
|
|
read it directly from the DB",
|
|
)
|
|
|
|
def test_delete_photo(self):
|
|
"""
|
|
test deleting the photo
|
|
"""
|
|
|
|
# Ensure we have a photo
|
|
self.test_gravatar_photo_import()
|
|
|
|
url = reverse("delete_photo", args=[self.user.photo_set.first().id])
|
|
response = self.client.get(url, follow=True)
|
|
self.assertEqual(response.status_code, 200, "deleting photo does not work?")
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Photo deleted successfully",
|
|
"Photo deletion did not work?",
|
|
)
|
|
|
|
def test_delete_non_existing_photo(self):
|
|
"""
|
|
test deleting the photo
|
|
"""
|
|
|
|
# Ensure we have a photo
|
|
self.test_gravatar_photo_import()
|
|
|
|
url = reverse("delete_photo", args=[1234])
|
|
response = self.client.get(url, follow=True)
|
|
self.assertEqual(response.status_code, 200, "post to delete does not work?")
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"No such image or no permission to delete it",
|
|
"Deleting photo that does not exist, should return error message",
|
|
)
|
|
|
|
def test_too_many_unconfirmed_email(self):
|
|
"""
|
|
Request too many unconfirmed email addresses, make sure we
|
|
cannot add more
|
|
"""
|
|
self.login()
|
|
# Avoid sending out mails
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
|
|
|
|
max_num_unconfirmed = getattr(
|
|
settings, "MAX_NUM_UNCONFIRMED_EMAILS", MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT
|
|
)
|
|
|
|
for i in range(max_num_unconfirmed + 1):
|
|
response = self.client.post( # noqa: F841
|
|
reverse("add_email"),
|
|
{
|
|
"email": "%i.%s" % (i, self.email),
|
|
},
|
|
follow=True,
|
|
) # Create test addresses + 1 too much
|
|
return self._check_form_validity(
|
|
response, "Too many unconfirmed mail addresses!", "__all__"
|
|
)
|
|
|
|
def test_add_mail_address_twice(self):
|
|
"""
|
|
Request the same mail address two times, should not lead to
|
|
having the same address twice
|
|
"""
|
|
self.login()
|
|
# Avoid sending out mails
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
|
|
|
|
for _ in range(2):
|
|
response = self.client.post( # noqa: F841
|
|
reverse("add_email"),
|
|
{
|
|
"email": self.email,
|
|
},
|
|
follow=True,
|
|
)
|
|
return self._check_form_validity(
|
|
response, "Address already added, currently unconfirmed", "email"
|
|
)
|
|
|
|
def test_add_already_confirmed_email_self(self): # pylint: disable=invalid-name
|
|
"""
|
|
Request adding mail address that is already confirmed (by someone)
|
|
"""
|
|
# Create test mail and confirm it, reuse test code
|
|
# Should set EMAIL_BACKEND, so no need to do it here
|
|
self.test_confirm_email()
|
|
|
|
response = self.client.post( # noqa: F841
|
|
reverse("add_email"),
|
|
{
|
|
"email": self.email,
|
|
},
|
|
follow=True,
|
|
)
|
|
|
|
return self._check_form_validity(
|
|
response, "Address already confirmed (by you)", "email"
|
|
)
|
|
|
|
def test_add_already_confirmed_email_other(self): # pylint: disable=invalid-name
|
|
"""
|
|
Request adding mail address that is already confirmed (by someone)
|
|
"""
|
|
# Create test mail and confirm it, reuse test code
|
|
# Should set EMAIL_BACKEND, so no need to do it here
|
|
self.test_confirm_email()
|
|
|
|
# Create another user and assign the mail address to that one
|
|
# in order to test the correct error message
|
|
otheruser = User.objects.create(username="otheruser")
|
|
confirmedemail = ConfirmedEmail.objects.last()
|
|
confirmedemail.user = otheruser
|
|
confirmedemail.save()
|
|
|
|
response = self.client.post( # noqa: F841
|
|
reverse("add_email"),
|
|
{
|
|
"email": self.email,
|
|
},
|
|
follow=True,
|
|
)
|
|
|
|
return self._check_form_validity(
|
|
response, "Address already confirmed (by someone else)", "email"
|
|
)
|
|
|
|
def test_remove_unconfirmed_non_existing_email(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Remove unconfirmed email that doesn't exist
|
|
"""
|
|
self.login()
|
|
url = reverse("remove_unconfirmed_email", args=[1234])
|
|
response = self.client.post(url, follow=True)
|
|
self.assertEqual(
|
|
response.status_code, 200, "unable to remove non existing address?"
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[0]),
|
|
"Address does not exist",
|
|
"Removing address that does not\
|
|
exist, should return error message!",
|
|
)
|
|
|
|
def test_upload_image(
|
|
self, test_only_one=True
|
|
): # pylint: disable=inconsistent-return-statements
|
|
"""
|
|
Test uploading image
|
|
"""
|
|
self.login()
|
|
url = reverse("upload_photo")
|
|
# rb => Read binary
|
|
with open(TEST_IMAGE_FILE, "rb") as photo_file:
|
|
photo_data = photo_file.read()
|
|
|
|
from django.core.files.uploadedfile import SimpleUploadedFile
|
|
|
|
uploaded_file = SimpleUploadedFile(
|
|
"deadbeef.png", photo_data, content_type="image/png"
|
|
)
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"photo": uploaded_file,
|
|
"not_porn": True,
|
|
"can_distribute": True,
|
|
},
|
|
follow=True,
|
|
)
|
|
if not test_only_one:
|
|
return response
|
|
self.assertEqual(
|
|
self.user.photo_set.count(), 1, "there must be exactly one photo now!"
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Successfully uploaded",
|
|
"A valid image should return a success message!",
|
|
)
|
|
self.assertEqual(
|
|
self.user.photo_set.first().format,
|
|
"png",
|
|
"Format must be png, since we uploaded a png!",
|
|
)
|
|
|
|
def test_upload_too_many_images(self):
|
|
"""
|
|
Test uploading more images than we are allowed
|
|
"""
|
|
for _ in range(settings.MAX_NUM_PHOTOS + 1):
|
|
response = self.test_upload_image(test_only_one=False)
|
|
self.assertEqual(
|
|
self.user.photo_set.count(),
|
|
settings.MAX_NUM_PHOTOS,
|
|
"there may not be more photos than allowed!",
|
|
)
|
|
# Take care we need to check the last message
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Maximum number of photos (%i) reached" % settings.MAX_NUM_PHOTOS,
|
|
"Adding more than allowed images, should return error message!",
|
|
)
|
|
|
|
def test_upload_too_big_image(self):
|
|
"""
|
|
Test uploading image that is too big
|
|
"""
|
|
self.login()
|
|
url = reverse("upload_photo")
|
|
# rb => Read binary
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"photo": io.StringIO("x" * (settings.MAX_PHOTO_SIZE + 1)),
|
|
"not_porn": True,
|
|
"can_distribute": True,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[0]),
|
|
"Image too big",
|
|
"Uploading too big image, should return error message!",
|
|
)
|
|
|
|
def test_upload_invalid_image(self):
|
|
"""
|
|
Test invalid image data
|
|
"""
|
|
self.login()
|
|
url = reverse("upload_photo")
|
|
# rb => Read binary
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"photo": io.StringIO("x"),
|
|
"not_porn": True,
|
|
"can_distribute": True,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[0]),
|
|
"Invalid Format",
|
|
"Invalid img data should return error message!",
|
|
)
|
|
|
|
def test_upload_invalid_image_format(self): # pylint: disable=invalid-name
|
|
"""
|
|
Test if invalid format is correctly detected
|
|
"""
|
|
self.login()
|
|
url = reverse("upload_photo")
|
|
# rb => Read binary
|
|
with open(os.path.join(settings.STATIC_ROOT, "img", "mm.svg"), "rb") as photo:
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"photo": photo,
|
|
"not_porn": True,
|
|
"can_distribute": True,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[0]),
|
|
"Invalid Format",
|
|
"Invalid img data should return error message!",
|
|
)
|
|
|
|
def test_upload_gif_image(self):
|
|
"""
|
|
Test if gif is correctly detected and can be viewed
|
|
"""
|
|
self._extracted_from_test_upload_webp_image_5(
|
|
"broken.gif",
|
|
"GIF upload failed?!",
|
|
"gif",
|
|
"Format must be gif, since we uploaded a GIF!",
|
|
)
|
|
|
|
def test_upload_jpg_image(self):
|
|
"""
|
|
Test if jpg is correctly detected and can be viewed
|
|
"""
|
|
self._extracted_from_test_upload_webp_image_5(
|
|
"broken.jpg",
|
|
"JPEG upload failed?!",
|
|
"jpg",
|
|
"Format must be jpeg, since we uploaded a jpeg!",
|
|
)
|
|
|
|
def test_upload_webp_image(self):
|
|
"""
|
|
Test if webp is correctly detected and can be viewed
|
|
"""
|
|
self._extracted_from_test_upload_webp_image_5(
|
|
"broken.webp",
|
|
"WEBP upload failed?!",
|
|
"webp",
|
|
"Format must be webp, since we uploaded a webp!",
|
|
)
|
|
|
|
def _extracted_from_test_upload_webp_image_5(
|
|
self, filename, message1, format, message2
|
|
):
|
|
"""
|
|
Helper function for common checks for gif, jpg, webp
|
|
"""
|
|
self.login()
|
|
url = reverse("upload_photo")
|
|
with open(os.path.join(settings.STATIC_ROOT, "img", filename), "rb") as photo:
|
|
response = self.client.post(
|
|
url,
|
|
{"photo": photo, "not_porn": True, "can_distribute": True},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[0]),
|
|
"Successfully uploaded",
|
|
message1,
|
|
)
|
|
self.assertEqual(self.user.photo_set.first().format, format, message2)
|
|
self.test_confirm_email()
|
|
self.user.confirmedemail_set.first().photo = self.user.photo_set.first()
|
|
urlobj = urlsplit(
|
|
libravatar_url(email=self.user.confirmedemail_set.first().email)
|
|
)
|
|
url = f"{urlobj.path}?{urlobj.query}"
|
|
response = self.client.get(url, follow=True)
|
|
self.assertEqual(response.status_code, 200, "unable to fetch avatar?")
|
|
|
|
def test_upload_unsupported_tif_image(self): # pylint: disable=invalid-name
|
|
"""
|
|
Test if unsupported format is correctly detected
|
|
"""
|
|
self.login()
|
|
url = reverse("upload_photo")
|
|
# rb => Read binary
|
|
with open(
|
|
os.path.join(settings.STATIC_ROOT, "img", "broken.tif"), "rb"
|
|
) as photo:
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"photo": photo,
|
|
"not_porn": True,
|
|
"can_distribute": True,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[0]),
|
|
"Invalid Format",
|
|
"Invalid img data should return error message!",
|
|
)
|
|
|
|
def test_automatic_photo_assign_to_confirmed_mail(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test if automatic assignment of photo works
|
|
"""
|
|
self.test_upload_image()
|
|
self.test_confirm_email()
|
|
confirmed = self.user.confirmedemail_set.first()
|
|
self.assertEqual(confirmed.photo, self.user.photo_set.first())
|
|
|
|
def test_assign_photo_to_email(self):
|
|
"""
|
|
Test assigning photo to mail address
|
|
"""
|
|
self.test_confirm_email()
|
|
self.test_upload_image()
|
|
self.assertIsNone(self.user.confirmedemail_set.first().photo)
|
|
url = reverse(
|
|
"assign_photo_email", args=[self.user.confirmedemail_set.first().id]
|
|
)
|
|
# The get is for the view - test context data
|
|
self.client.get(
|
|
url,
|
|
{
|
|
"photo_id": self.user.photo_set.first().id,
|
|
},
|
|
)
|
|
# The post is for the actual assigning
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"photo_id": self.user.photo_set.first().id,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "cannot assign photo?")
|
|
self.assertEqual(
|
|
self.user.confirmedemail_set.first().photo, self.user.photo_set.first()
|
|
)
|
|
|
|
def test_no_photo_to_email(self):
|
|
"""
|
|
Test assigning photo to mail address
|
|
"""
|
|
self.test_confirm_email()
|
|
url = reverse(
|
|
"assign_photo_email", args=[self.user.confirmedemail_set.first().id]
|
|
)
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"photoNone": True,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "cannot un-assign photo?")
|
|
self.assertEqual(self.user.confirmedemail_set.first().photo, None)
|
|
|
|
def test_assign_photo_to_email_wo_photo_for_testing_template(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test assign photo template
|
|
"""
|
|
self.test_confirm_email()
|
|
url = reverse(
|
|
"assign_photo_email", args=[self.user.confirmedemail_set.first().id]
|
|
)
|
|
# The get is for the view - test context data
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200, "cannot fetch page?")
|
|
|
|
def test_assign_invalid_photo_id_to_email(self): # pylint: disable=invalid-name
|
|
"""
|
|
Test if assigning an invalid photo id returns the correct error message
|
|
"""
|
|
self.test_confirm_email()
|
|
self.test_upload_image()
|
|
self.assertIsNone(self.user.confirmedemail_set.first().photo)
|
|
url = reverse(
|
|
"assign_photo_email", args=[self.user.confirmedemail_set.first().id]
|
|
)
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"photo_id": 1234,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "cannot post assign photo request?")
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Photo does not exist",
|
|
"Assign non existing photo, does not return error message?",
|
|
)
|
|
|
|
def test_post_to_assign_photo_without_photo_id(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test if assigning photo without id returns the correct error message
|
|
"""
|
|
self.test_confirm_email()
|
|
self.test_upload_image()
|
|
self.assertIsNone(self.user.confirmedemail_set.first().photo)
|
|
url = reverse(
|
|
"assign_photo_email", args=[self.user.confirmedemail_set.first().id]
|
|
)
|
|
response = self.client.post(url, {}, follow=True)
|
|
self.assertEqual(response.status_code, 200, "cannot post assign photo request?")
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Invalid request [photo_id] missing",
|
|
"Assign non existing photo, does not return error message?",
|
|
)
|
|
|
|
def test_assign_photo_to_non_existing_mail(self): # pylint: disable=invalid-name
|
|
"""
|
|
Test if assigning photo to mail address that doesn't exist returns
|
|
the correct error message
|
|
"""
|
|
self.test_upload_image()
|
|
url = reverse("assign_photo_email", args=[1234])
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"photo_id": self.user.photo_set.first().id,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "cannot post assign photo request?")
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Invalid request",
|
|
"Assign non existing photo, does not return error message?",
|
|
)
|
|
|
|
def test_import_photo_with_non_existing_email(self): # pylint: disable=invalid-name
|
|
"""
|
|
Test if import with non existing mail address returns
|
|
the correct error message
|
|
"""
|
|
self.login()
|
|
url = reverse("import_photo", args=[1234])
|
|
response = self.client.post(url, {}, follow=True)
|
|
self.assertEqual(response.status_code, 200, "cannot post import photo request?")
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[0]),
|
|
"Address does not exist",
|
|
"Import photo with non existing mail id,\
|
|
does not return error message?",
|
|
)
|
|
|
|
def test_import_nothing(self):
|
|
"""
|
|
Test if importing nothing causes the correct
|
|
error message to be returned
|
|
"""
|
|
self.test_confirm_email()
|
|
url = reverse("import_photo", args=[self.user.confirmedemail_set.first().id])
|
|
response = self.client.post(url, {}, follow=True)
|
|
self.assertEqual(response.status_code, 200, "cannot post import photo request?")
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Nothing importable",
|
|
"Importing with email that does not exist in Gravatar,\
|
|
should return an error message!",
|
|
)
|
|
|
|
def _manual_confirm(self):
|
|
"""
|
|
Helper method to confirm manually, because testing is really hard
|
|
"""
|
|
# Manual confirm, since testing is _really_ hard!
|
|
unconfirmed = self.user.unconfirmedopenid_set.first()
|
|
confirmed = ConfirmedOpenId()
|
|
confirmed.user = unconfirmed.user
|
|
confirmed.ip_address = "127.0.0.1"
|
|
confirmed.openid = unconfirmed.openid
|
|
confirmed.save()
|
|
unconfirmed.delete()
|
|
|
|
def test_add_openid(self, confirm=True):
|
|
"""
|
|
Test if adding an OpenID works
|
|
"""
|
|
|
|
self.login()
|
|
# Get page
|
|
response = self.client.get(reverse("add_openid"))
|
|
self.assertEqual(
|
|
response.status_code, 200, "Fetching page to add OpenID fails?"
|
|
)
|
|
|
|
response = self.client.post(
|
|
reverse("add_openid"),
|
|
{
|
|
"openid": self.openid,
|
|
},
|
|
)
|
|
self.assertEqual(response.status_code, 302, "OpenID must redirect")
|
|
|
|
if confirm:
|
|
self._manual_confirm()
|
|
|
|
def test_add_openid_twice(self):
|
|
"""
|
|
Test if adding OpenID a second time works - it shouldn't
|
|
"""
|
|
self.login()
|
|
# Get page
|
|
response = self.client.get(reverse("add_openid"))
|
|
self.assertEqual(
|
|
response.status_code, 200, "Fetching page to add OpenID fails?"
|
|
)
|
|
|
|
response = self.client.post(
|
|
reverse("add_openid"),
|
|
{
|
|
"openid": self.openid,
|
|
},
|
|
)
|
|
self.assertEqual(response.status_code, 302, "OpenID must redirect")
|
|
response = self.client.post(
|
|
reverse("add_openid"),
|
|
{
|
|
"openid": self.openid,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(
|
|
self.user.unconfirmedopenid_set.count(),
|
|
1,
|
|
"There must only be one unconfirmed ID!",
|
|
)
|
|
|
|
self._check_form_validity(
|
|
response, "OpenID already added, but not confirmed yet!", "openid"
|
|
)
|
|
# Manual confirm, since testing is _really_ hard!
|
|
unconfirmed = self.user.unconfirmedopenid_set.first()
|
|
confirmed = ConfirmedOpenId()
|
|
confirmed.user = unconfirmed.user
|
|
confirmed.ip_address = "127.0.0.1"
|
|
confirmed.openid = unconfirmed.openid
|
|
confirmed.save()
|
|
unconfirmed.delete()
|
|
|
|
# Try adding it again - although already confirmed
|
|
response = self.client.post(
|
|
reverse("add_openid"),
|
|
{
|
|
"openid": self.openid,
|
|
},
|
|
follow=True,
|
|
)
|
|
|
|
return self._check_form_validity(
|
|
response, "OpenID already added and confirmed!", "openid"
|
|
)
|
|
|
|
def _check_form_validity(self, response, message, field):
|
|
"""
|
|
Helper method to check form, used in several test functions,
|
|
deduplicating code
|
|
"""
|
|
|
|
self.assertTrue(
|
|
hasattr(response, "context"), "Response does not have a context"
|
|
)
|
|
result = response.context.get("form")
|
|
self.assertIsNotNone(result, "No form found in response context")
|
|
self.assertFalse(result.is_valid(), "Form should not be valid")
|
|
self.assertIn(message, result.errors.get(field, []))
|
|
return result
|
|
|
|
def test_assign_photo_to_openid(self):
|
|
"""
|
|
Test assignment of photo to openid
|
|
"""
|
|
self.test_add_openid()
|
|
self.test_upload_image()
|
|
self.assertIsNone(self.user.confirmedopenid_set.first().photo)
|
|
url = reverse(
|
|
"assign_photo_openid", args=[self.user.confirmedopenid_set.first().id]
|
|
)
|
|
# The get is for the view - test context data
|
|
self.client.get(
|
|
url,
|
|
{
|
|
"photo_id": self.user.photo_set.first().id,
|
|
},
|
|
)
|
|
# The post is for the actual assigning
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"photo_id": self.user.photo_set.first().id,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "cannot assign photo?")
|
|
self.assertEqual(
|
|
self.user.confirmedopenid_set.first().photo, self.user.photo_set.first()
|
|
)
|
|
|
|
def test_assign_photo_to_openid_wo_photo_for_testing_template(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test openid/photo assignment template
|
|
"""
|
|
self.test_add_openid()
|
|
url = reverse(
|
|
"assign_photo_openid", args=[self.user.confirmedopenid_set.first().id]
|
|
)
|
|
response = self.client.get(url)
|
|
self.assertEqual(response.status_code, 200, "cannot fetch page?")
|
|
|
|
def test_assign_invalid_photo_id_to_openid(self): # pylint: disable=invalid-name
|
|
"""
|
|
Test assigning invalid photo to openid returns
|
|
the correct error message
|
|
"""
|
|
self.test_add_openid()
|
|
self.assertIsNone(self.user.confirmedopenid_set.first().photo)
|
|
url = reverse(
|
|
"assign_photo_openid", args=[self.user.confirmedopenid_set.first().id]
|
|
)
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"photo_id": 1234,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "cannot post assign photo request?")
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Photo does not exist",
|
|
"Assign non existing photo, does not return error message?",
|
|
)
|
|
|
|
def test_post_to_assign_photo_openid_without_photo_id(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test POST assign photo to openid without photo id
|
|
returns the correct error message
|
|
"""
|
|
self.test_add_openid()
|
|
self.test_upload_image()
|
|
self.assertIsNone(self.user.confirmedopenid_set.first().photo)
|
|
url = reverse(
|
|
"assign_photo_openid", args=[self.user.confirmedopenid_set.first().id]
|
|
)
|
|
response = self.client.post(url, {}, follow=True)
|
|
self.assertEqual(response.status_code, 200, "cannot post assign photo request?")
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Invalid request [photo_id] missing",
|
|
"Assign non existing photo, does not return error message?",
|
|
)
|
|
|
|
def test_assign_photo_to_openid_non_existing_openid(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test assigning photo to openid that doesn't exist
|
|
returns the correct error message.
|
|
"""
|
|
self.test_upload_image()
|
|
url = reverse("assign_photo_openid", args=[1234])
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"photo_id": self.user.photo_set.first().id,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "cannot post assign photo request?")
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"Invalid request",
|
|
"Assign non existing photo, does not return error message?",
|
|
)
|
|
|
|
def test_remove_confirmed_openid(self): # pylint: disable=invalid-name
|
|
"""
|
|
Remove confirmed openid
|
|
"""
|
|
self.test_add_openid()
|
|
url = reverse(
|
|
"remove_confirmed_openid", args=[self.user.confirmedopenid_set.first().id]
|
|
)
|
|
response = self.client.post(url, follow=True)
|
|
self.assertEqual(
|
|
response.status_code, 200, "unable to remove confirmed openid?"
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"ID removed",
|
|
"Removing confirmed openid does not work?",
|
|
)
|
|
|
|
def test_remove_not_existing_confirmed_openid(self): # pylint: disable=invalid-name
|
|
"""
|
|
Try removing confirmed openid that doesn't exist
|
|
"""
|
|
self.login()
|
|
url = reverse("remove_confirmed_openid", args=[1234])
|
|
response = self.client.post(url, follow=True)
|
|
self.assertEqual(
|
|
response.status_code, 200, "removing id does not redirect to profile?"
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[0]),
|
|
"ID does not exist",
|
|
"Removing not existing (confirmed) address, should produce an\
|
|
error message!",
|
|
)
|
|
|
|
def test_remove_unconfirmed_openid(self):
|
|
"""
|
|
Remove unconfirmed openid
|
|
"""
|
|
self.test_add_openid(confirm=False)
|
|
url = reverse(
|
|
"remove_unconfirmed_openid",
|
|
args=[self.user.unconfirmedopenid_set.first().id],
|
|
)
|
|
response = self.client.post(url, follow=True)
|
|
self.assertEqual(
|
|
response.status_code, 200, "unable to remove unconfirmed address?"
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[-1]),
|
|
"ID removed",
|
|
"Removing unconfirmed mail does not work?",
|
|
)
|
|
|
|
def test_remove_unconfirmed_non_existing_openid(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Remove unconfirmed openid that doesn't exist
|
|
"""
|
|
self.login()
|
|
url = reverse("remove_unconfirmed_openid", args=[1234])
|
|
response = self.client.post(url, follow=True)
|
|
self.assertEqual(
|
|
response.status_code, 200, "unable to remove unconfirmed address?"
|
|
)
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[0]),
|
|
"ID does not exist",
|
|
"Removing an non existing openid should return an error message",
|
|
)
|
|
|
|
def test_openid_redirect_view(self):
|
|
"""
|
|
Test redirect view
|
|
"""
|
|
self.test_add_openid(confirm=False)
|
|
url = reverse(
|
|
"openid_redirection", args=[self.user.unconfirmedopenid_set.first().id]
|
|
)
|
|
response = self.client.get(url, follow=True)
|
|
self.assertEqual(
|
|
response.status_code, 200, "unable to remove unconfirmed address?"
|
|
)
|
|
# self.assertContains(
|
|
# response,
|
|
# 'OpenID discovery failed: ', 1, 200,
|
|
# 'This request must return an error in test mode'
|
|
# )
|
|
|
|
def test_set_photo_on_openid(self):
|
|
"""
|
|
Test the set_photo function on our ConfirmedOpenId model.
|
|
"""
|
|
self.test_add_openid()
|
|
self.test_upload_image()
|
|
self.assertIsNone(self.user.confirmedopenid_set.first().photo)
|
|
self.user.confirmedopenid_set.first().set_photo(self.user.photo_set.first())
|
|
self.assertEqual(
|
|
self.user.confirmedopenid_set.first().photo,
|
|
self.user.photo_set.first(),
|
|
"set_photo did not work!?",
|
|
)
|
|
|
|
def test_avatar_url_mail(self, do_upload_and_confirm=True, size=(80, 80)):
|
|
"""
|
|
Test fetching avatar via mail
|
|
"""
|
|
if do_upload_and_confirm:
|
|
self.test_upload_image()
|
|
self.test_confirm_email()
|
|
urlobj = urlsplit(
|
|
libravatar_url(
|
|
email=self.user.confirmedemail_set.first().email,
|
|
size=size[0],
|
|
)
|
|
)
|
|
url = f"{urlobj.path}?{urlobj.query}"
|
|
response = self.client.get(url, follow=True)
|
|
self.assertEqual(response.status_code, 200, "unable to fetch avatar?")
|
|
photodata = Image.open(BytesIO(response.content))
|
|
self.assertEqual(photodata.size, size, "Why is this not the correct size?")
|
|
|
|
def test_avatar_url_openid(self):
|
|
"""
|
|
Test fetching avatar via openid
|
|
"""
|
|
self.test_assign_photo_to_openid()
|
|
urlobj = urlsplit(
|
|
libravatar_url(
|
|
openid=self.user.confirmedopenid_set.first().openid,
|
|
size=80,
|
|
)
|
|
)
|
|
url = f"{urlobj.path}?{urlobj.query}"
|
|
response = self.client.get(url, follow=True)
|
|
self.assertEqual(response.status_code, 200, "unable to fetch avatar?")
|
|
photodata = Image.open(BytesIO(response.content))
|
|
self.assertEqual(photodata.size, (80, 80), "Why is this not the correct size?")
|
|
|
|
def test_avatar_url_non_existing_mail_digest(self): # pylint: disable=invalid-name
|
|
"""
|
|
Test fetching avatar via non existing mail digest
|
|
"""
|
|
self.test_upload_image()
|
|
self.test_confirm_email()
|
|
urlobj = urlsplit(
|
|
libravatar_url(
|
|
email=self.user.confirmedemail_set.first().email,
|
|
size=80,
|
|
)
|
|
)
|
|
# Simply delete it, then it's digest is 'correct', but
|
|
# the hash is no longer there
|
|
addr = self.user.confirmedemail_set.first().email
|
|
hashlib.md5(addr.strip().lower().encode("utf-8")).hexdigest()
|
|
|
|
self.user.confirmedemail_set.first().delete()
|
|
url = f"{urlobj.path}?{urlobj.query}"
|
|
self.client.get(url, follow=True)
|
|
# TODO: All these tests still fails under some circumstances - it needs further investigation
|
|
# self.assertEqual(
|
|
# response.redirect_chain[0][0],
|
|
# f"/gravatarproxy/{digest}?s=80",
|
|
# "Doesn't redirect to Gravatar?",
|
|
# )
|
|
# self.assertEqual(
|
|
# response.redirect_chain[0][1], 302, "Doesn't redirect with 302?"
|
|
# )
|
|
# self.assertEqual(
|
|
# response.redirect_chain[1][0],
|
|
# f"/avatar/{digest}?s=80&forcedefault=y",
|
|
# "Doesn't redirect with default forced on?",
|
|
# )
|
|
# self.assertEqual(
|
|
# response.redirect_chain[1][1], 302, "Doesn't redirect with 302?"
|
|
# )
|
|
# self.assertEqual(
|
|
# response.redirect_chain[2][0],
|
|
# "/static/img/nobody/80.png",
|
|
# "Doesn't redirect to static?",
|
|
# )
|
|
# self.assertRedirects(
|
|
# response=response,
|
|
# expected_url="/static/img/nobody/80.png",
|
|
# msg_prefix="Why does this not redirect to Gravatar?",
|
|
# )
|
|
# Eventually one should check if the data is the same
|
|
|
|
def test_avatar_url_non_existing_mail_digest_gravatarproxy_disabled(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test fetching avatar via non existing mail digest
|
|
"""
|
|
self.test_upload_image()
|
|
self.test_confirm_email()
|
|
urlobj = urlsplit(
|
|
libravatar_url(
|
|
email=self.user.confirmedemail_set.first().email,
|
|
size=80,
|
|
)
|
|
)
|
|
# Simply delete it, then it digest is 'correct', but
|
|
# the hash is no longer there
|
|
self.user.confirmedemail_set.first().delete()
|
|
url = f"{urlobj.path}?{urlobj.query}&gravatarproxy=n"
|
|
response = self.client.get(url, follow=True)
|
|
self.assertEqual(
|
|
response.redirect_chain[0][0],
|
|
"/static/img/nobody/80.png",
|
|
"Doesn't redirect to static?",
|
|
)
|
|
|
|
# self.assertRedirects(
|
|
# response=response,
|
|
# expected_url="/static/img/nobody/80.png",
|
|
# msg_prefix="Why does this not redirect to the default img?",
|
|
# )
|
|
# Eventually one should check if the data is the same
|
|
|
|
def test_avatar_url_non_existing_mail_digest_w_default_mm(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test fetching avatar via non existing mail digest and default 'mm'
|
|
"""
|
|
urlobj = urlsplit(
|
|
libravatar_url(
|
|
email="asdf@company.local",
|
|
size=80,
|
|
default="mm",
|
|
)
|
|
)
|
|
url = f"{urlobj.path}?{urlobj.query}"
|
|
self.client.get(url, follow=False)
|
|
|
|
def test_avatar_url_non_existing_mail_digest_w_default_mm_gravatarproxy_disabled(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test fetching avatar via non existing mail digest and default 'mm'
|
|
"""
|
|
urlobj = urlsplit(
|
|
libravatar_url(
|
|
email="asdf@company.local",
|
|
size=80,
|
|
default="mm",
|
|
)
|
|
)
|
|
url = f"{urlobj.path}?{urlobj.query}&gravatarproxy=n"
|
|
response = self.client.get(url, follow=True)
|
|
self.assertEqual(
|
|
response.redirect_chain[0][0],
|
|
"/static/img/mm/80.png",
|
|
"Doesn't redirect to static?",
|
|
)
|
|
|
|
# self.assertRedirects(
|
|
# response=response,
|
|
# expected_url="/static/img/mm/80.png",
|
|
# msg_prefix="Why does this not redirect to the default img?",
|
|
# )
|
|
# Eventually one should check if the data is the same
|
|
|
|
def test_avatar_url_non_existing_mail_digest_wo_default(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test fetching avatar via non existing mail digest and default 'mm'
|
|
"""
|
|
urlobj = urlsplit(
|
|
libravatar_url(
|
|
email="asdf@company.local",
|
|
size=80,
|
|
)
|
|
)
|
|
digest = hashlib.md5("asdf@company.local".lower().encode("utf-8")).hexdigest()
|
|
url = f"{urlobj.path}?{urlobj.query}"
|
|
response = self.client.get(url, follow=True)
|
|
self.assertEqual(
|
|
response.redirect_chain[0][0],
|
|
f"/gravatarproxy/{digest}?s=80",
|
|
"Doesn't redirect to Gravatar?",
|
|
)
|
|
self.assertEqual(
|
|
response.redirect_chain[0][1], 302, "Doesn't redirect with 302?"
|
|
)
|
|
self.assertEqual(
|
|
response.redirect_chain[1][0],
|
|
f"/avatar/{digest}?s=80&forcedefault=y",
|
|
"Doesn't redirect with default forced on?",
|
|
)
|
|
self.assertEqual(
|
|
response.redirect_chain[1][1], 302, "Doesn't redirect with 302?"
|
|
)
|
|
self.assertEqual(
|
|
response.redirect_chain[2][0],
|
|
"/static/img/nobody/80.png",
|
|
"Doesn't redirect to static?",
|
|
)
|
|
|
|
# self.assertRedirects(
|
|
# response=response,
|
|
# expected_url="/static/img/nobody/80.png",
|
|
# msg_prefix="Why does this not redirect to the default img?",
|
|
# )
|
|
# Eventually one should check if the data is the same
|
|
|
|
def test_avatar_url_non_existing_mail_digest_wo_default_gravatarproxy_disabled(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test fetching avatar via non existing mail digest and default 'mm'
|
|
"""
|
|
urlobj = urlsplit(
|
|
libravatar_url(
|
|
email="asdf@company.local",
|
|
size=80,
|
|
)
|
|
)
|
|
url = f"{urlobj.path}?{urlobj.query}&gravatarproxy=n"
|
|
response = self.client.get(url, follow=True)
|
|
self.assertEqual(
|
|
response.redirect_chain[0][0],
|
|
"/static/img/nobody/80.png",
|
|
"Doesn't redirect to static?",
|
|
)
|
|
|
|
# self.assertRedirects(
|
|
# response=response,
|
|
# expected_url="/static/img/nobody/80.png",
|
|
# msg_prefix="Why does this not redirect to the default img?",
|
|
# )
|
|
# Eventually one should check if the data is the same
|
|
|
|
def test_avatar_url_default(self): # pylint: disable=invalid-name
|
|
"""
|
|
Test fetching avatar for not existing mail with default specified
|
|
"""
|
|
urlobj = urlsplit(
|
|
libravatar_url(
|
|
"xxx@xxx.xxx",
|
|
size=80,
|
|
default="/static/img/nobody.png",
|
|
)
|
|
)
|
|
url = f"{urlobj.path}?{urlobj.query}"
|
|
url += "&gravatarproxy=n"
|
|
response = self.client.get(url, follow=False)
|
|
self.assertEqual(response.status_code, 302, "Doesn't redirect with 302?")
|
|
self.assertEqual(
|
|
response["Location"],
|
|
"/static/img/nobody.png",
|
|
"Doesn't redirect to static img?",
|
|
)
|
|
|
|
def test_avatar_url_default_gravatarproxy_disabled(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test fetching avatar for not existing mail with default specified
|
|
"""
|
|
urlobj = urlsplit(
|
|
libravatar_url(
|
|
"xxx@xxx.xxx",
|
|
size=80,
|
|
default="/static/img/nobody.png",
|
|
)
|
|
)
|
|
url = f"{urlobj.path}?{urlobj.query}&gravatarproxy=n"
|
|
response = self.client.get(url, follow=True)
|
|
self.assertEqual(
|
|
response.redirect_chain[0][0],
|
|
"/static/img/nobody.png",
|
|
"Doesn't redirect to static?",
|
|
)
|
|
|
|
def test_avatar_url_default_external(self): # pylint: disable=invalid-name
|
|
"""
|
|
Test fetching avatar for not existing mail with external default specified
|
|
This shall *not* redirect to the external site (CWE-601)!
|
|
"""
|
|
default = "http://host.tld/img.png"
|
|
size = 80
|
|
urlobj = urlsplit(
|
|
libravatar_url(
|
|
"xxx@xxx.xxx",
|
|
size=size,
|
|
default=default,
|
|
)
|
|
)
|
|
url = f"{urlobj.path}?{urlobj.query}"
|
|
response = self.client.get(url, follow=False)
|
|
self.assertRedirects(
|
|
response=response,
|
|
expected_url=f"/gravatarproxy/fb7a6d7f11365642d44ba66dc57df56f?s={size}",
|
|
fetch_redirect_response=False,
|
|
msg_prefix="Why does this not redirect to the default img?",
|
|
)
|
|
|
|
def test_avatar_url_default_external_trusted(self): # pylint: disable=invalid-name
|
|
"""
|
|
Test fetching avatar for not existing mail with external default specified
|
|
"""
|
|
default = "https://ui-avatars.com/api/blah"
|
|
urlobj = urlsplit(
|
|
libravatar_url(
|
|
"xxx@xxx.xxx",
|
|
size=80,
|
|
default=default,
|
|
)
|
|
)
|
|
url = f"{urlobj.path}?{urlobj.query}"
|
|
response = self.client.get(url, follow=False)
|
|
self.assertRedirects(
|
|
response=response,
|
|
expected_url="/gravatarproxy/fb7a6d7f11365642d44ba66dc57df56f?s=80&default=https://ui-avatars.com/api/blah",
|
|
fetch_redirect_response=False,
|
|
msg_prefix="Why does this not redirect to the default img?",
|
|
)
|
|
|
|
def test_avatar_url_default_external_gravatarproxy_disabled(
|
|
self,
|
|
): # pylint: disable=invalid-name
|
|
"""
|
|
Test fetching avatar for not existing mail with external default specified
|
|
This shall *not* redirect to the external site (CWE-601)!
|
|
"""
|
|
default = "http://host.tld/img.png"
|
|
urlobj = urlsplit(
|
|
libravatar_url(
|
|
"xxx@xxx.xxx",
|
|
size=80,
|
|
default=default,
|
|
)
|
|
)
|
|
url = f"{urlobj.path}?{urlobj.query}&gravatarproxy=n"
|
|
response = self.client.get(url, follow=False)
|
|
self.assertRedirects(
|
|
response=response,
|
|
expected_url="/static/img/nobody/80.png",
|
|
fetch_redirect_response=False,
|
|
msg_prefix="Why does this not redirect to the default img?",
|
|
)
|
|
|
|
def test_crop_photo(self):
|
|
"""
|
|
Test cropping photo
|
|
"""
|
|
self.test_upload_image()
|
|
self.test_confirm_email()
|
|
url = reverse("crop_photo", args=[self.user.photo_set.first().pk])
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"x": 10,
|
|
"y": 10,
|
|
"w": 20,
|
|
"h": 20,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "unable to crop?")
|
|
self.test_avatar_url_mail(do_upload_and_confirm=False, size=(20, 20))
|
|
img = Image.open(BytesIO(self.user.photo_set.first().data))
|
|
self.assertEqual(
|
|
img.size, (20, 20), "cropped to 20x20, but resulting image isn't 20x20!?"
|
|
)
|
|
|
|
def test_password_change_view(self):
|
|
"""
|
|
Test password change view
|
|
"""
|
|
self.login()
|
|
url = reverse("password_change")
|
|
response = self.client.get(url)
|
|
self.assertEqual(
|
|
response.status_code, 200, "unable to view password change view?"
|
|
)
|
|
|
|
def test_password_change_view_post_wrong_old_pw(self):
|
|
"""
|
|
Test password change view post
|
|
"""
|
|
self.login()
|
|
response = self.client.post(
|
|
reverse("password_change"),
|
|
{
|
|
"old_password": "xxx",
|
|
"new_password1": self.password,
|
|
"new_password2": self.password,
|
|
},
|
|
follow=True,
|
|
)
|
|
|
|
self.assertContains(
|
|
response,
|
|
"Your old password was entered incorrectly. Please enter it again.",
|
|
1,
|
|
200,
|
|
"Old password as entered incorrectly, site should raise an error",
|
|
)
|
|
|
|
def test_password_change_view_post_wrong_new_password1(self):
|
|
"""
|
|
Test password change view post
|
|
"""
|
|
self.login()
|
|
response = self.client.post(
|
|
reverse("password_change"),
|
|
{
|
|
"old_password": self.password,
|
|
"new_password1": f"{self.password}.",
|
|
"new_password2": self.password,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertContains(
|
|
response,
|
|
"The two password fields did",
|
|
1,
|
|
200,
|
|
"Old password was entered incorrectly, site should raise an error",
|
|
)
|
|
|
|
def test_password_change_view_post_wrong_new_password2(self):
|
|
"""
|
|
Test password change view post
|
|
"""
|
|
self.login()
|
|
response = self.client.post(
|
|
reverse("password_change"),
|
|
{
|
|
"old_password": self.password,
|
|
"new_password1": self.password,
|
|
"new_password2": f"{self.password}.",
|
|
},
|
|
follow=True,
|
|
)
|
|
|
|
self.assertContains(
|
|
response,
|
|
"The two password fields did",
|
|
1,
|
|
200,
|
|
"Old password as entered incorrectly, site should raise an error",
|
|
)
|
|
|
|
def test_password_change_view_post_common_password(self):
|
|
"""
|
|
Test password change view post
|
|
"""
|
|
self.login()
|
|
response = self.client.post(
|
|
reverse("password_change"),
|
|
{
|
|
"old_password": self.password,
|
|
"new_password1": "Hallo",
|
|
"new_password2": "Hallo",
|
|
},
|
|
follow=True,
|
|
)
|
|
|
|
self.assertContains(
|
|
response,
|
|
"This password is too common.",
|
|
1,
|
|
200,
|
|
"Common password, site should raise an error",
|
|
)
|
|
|
|
def test_profile_must_list_first_and_lastname(self):
|
|
"""
|
|
Test if profile view correctly lists first -/last name
|
|
"""
|
|
self.login()
|
|
response = self.client.get(reverse("profile"))
|
|
self.assertContains(
|
|
response,
|
|
self.first_name,
|
|
1,
|
|
200,
|
|
"First name not listed in profile page",
|
|
)
|
|
self.assertContains(
|
|
response,
|
|
self.last_name,
|
|
1,
|
|
200,
|
|
"Last name not listed in profile page",
|
|
)
|
|
self.assertContains(
|
|
response,
|
|
f"{self.first_name} {self.last_name}",
|
|
1,
|
|
200,
|
|
"First and last name not correctly listed in profile page",
|
|
)
|
|
|
|
def test_password_reset_page(self):
|
|
"""
|
|
Just test if the password reset page come up correctly
|
|
"""
|
|
response = self.client.get(reverse("password_reset"))
|
|
self.assertEqual(response.status_code, 200, "no 200 ok?")
|
|
|
|
def test_password_reset_wo_mail(self):
|
|
"""
|
|
Test if the password reset doesn't error out
|
|
if the mail address doesn't exist
|
|
"""
|
|
# Avoid sending out mails
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
|
|
|
# Empty database / eliminate existing users
|
|
User.objects.all().delete()
|
|
url = reverse("password_reset")
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"email": "asdf@asdf.local",
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "password reset page not working?")
|
|
self.assertEqual(
|
|
len(mail.outbox),
|
|
0,
|
|
"user does not exist, there should be no mail in the outbox!",
|
|
)
|
|
|
|
def test_password_reset_w_mail(self):
|
|
"""
|
|
Test if the password reset works correctly with email in
|
|
User object
|
|
"""
|
|
# Avoid sending out mails
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
|
|
|
url = reverse("password_reset")
|
|
# Our test user doesn't have an email address by default - but we need one set
|
|
self.user.email = "asdf@asdf.local"
|
|
self.user.save()
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"email": self.user.email,
|
|
},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "password reset page not working?")
|
|
self.assertEqual(
|
|
len(mail.outbox), 1, "User exists, there should be a mail in the outbox!"
|
|
)
|
|
self.assertEqual(
|
|
mail.outbox[0].to[0],
|
|
self.user.email,
|
|
"Sending mails to the wrong \
|
|
mail address?",
|
|
)
|
|
|
|
def test_password_reset_w_confirmed_mail(self):
|
|
"""
|
|
Test if the password reset works correctly with confirmed
|
|
mail
|
|
"""
|
|
# Avoid sending out mails
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
|
|
|
url = reverse("password_reset")
|
|
# Our test user doesn't have a confirmed mail identity - add one
|
|
self.user.confirmedemail_set.create(email="asdf@asdf.local")
|
|
self.user.save()
|
|
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"email": self.user.confirmedemail_set.first().email,
|
|
},
|
|
follow=True,
|
|
)
|
|
# Since the object is touched in another process, we need to refresh it
|
|
self.user.refresh_from_db()
|
|
self.assertEqual(response.status_code, 200, "password reset page not working?")
|
|
self.assertEqual(
|
|
self.user.email,
|
|
self.user.confirmedemail_set.first().email,
|
|
"The password reset view, should have corrected this!",
|
|
)
|
|
self.assertEqual(
|
|
len(mail.outbox), 1, "user exists, there should be a mail in the outbox!"
|
|
)
|
|
self.assertEqual(
|
|
mail.outbox[0].to[0],
|
|
self.user.email,
|
|
"why are we sending mails to the wrong mail address?",
|
|
)
|
|
|
|
def test_password_reset_w_confirmed_mail_no_password(self):
|
|
"""
|
|
Test password reset for user with confirmed email but no password set.
|
|
This tests the specific case that was failing with Django 4.2+ where
|
|
User.objects.make_random_password() was deprecated.
|
|
|
|
Reproduces the scenario where a user has a confirmed email address
|
|
but their password field is empty or starts with "!" (unusable password).
|
|
"""
|
|
# Avoid sending out mails
|
|
settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
|
|
|
|
# Create a user with no usable password (starts with "!")
|
|
test_user = User.objects.create_user(
|
|
username="testuser_no_pass",
|
|
email="", # No email in User model
|
|
)
|
|
# Set an unusable password (starts with "!")
|
|
test_user.set_unusable_password()
|
|
test_user.save()
|
|
|
|
# Add a confirmed email (this is the key scenario)
|
|
confirmed_email = test_user.confirmedemail_set.create(email="test@example.com")
|
|
|
|
# Verify the user has no usable password
|
|
self.assertTrue(
|
|
test_user.password.startswith("!"),
|
|
"Test user should have unusable password starting with '!'",
|
|
)
|
|
|
|
url = reverse("password_reset")
|
|
|
|
# Attempt password reset - this should work without AttributeError
|
|
response = self.client.post(
|
|
url,
|
|
{
|
|
"email": confirmed_email.email,
|
|
},
|
|
follow=True,
|
|
)
|
|
|
|
# Refresh user from database to see changes
|
|
test_user.refresh_from_db()
|
|
|
|
# Verify the request succeeded
|
|
self.assertEqual(response.status_code, 200, "password reset page not working?")
|
|
|
|
# Verify that the user now has a usable password (no longer starts with "!")
|
|
self.assertFalse(
|
|
test_user.password.startswith("!"),
|
|
"User should now have a usable password after reset",
|
|
)
|
|
|
|
# Verify the email was set on the user object
|
|
self.assertEqual(
|
|
test_user.email,
|
|
confirmed_email.email,
|
|
"The password reset view should have set the email on user object",
|
|
)
|
|
|
|
# Verify a reset email was sent
|
|
self.assertEqual(
|
|
len(mail.outbox), 1, "user exists, there should be a mail in the outbox!"
|
|
)
|
|
self.assertEqual(
|
|
mail.outbox[0].to[0],
|
|
test_user.email,
|
|
"reset email should be sent to the correct address",
|
|
)
|
|
|
|
# Clean up
|
|
test_user.delete()
|
|
|
|
def test_export(self):
|
|
"""
|
|
Test if export works
|
|
"""
|
|
|
|
# Create well known strings to check if export
|
|
# works as expected
|
|
self.user.confirmedemail_set.create(email="asdf@asdf.local")
|
|
self.user.confirmedopenid_set.create(openid="http://asdf.asdf.local")
|
|
self.user.save()
|
|
|
|
# Ensure we have a photo uploaded
|
|
self.test_upload_image()
|
|
|
|
self.login()
|
|
self.client.get(reverse("export"))
|
|
response = self.client.post(
|
|
reverse("export"),
|
|
{},
|
|
follow=False,
|
|
)
|
|
self.assertIsInstance(response.content, bytes)
|
|
fh = gzip.open(BytesIO(response.content), "rb")
|
|
content = fh.read()
|
|
fh.close()
|
|
root = xml.etree.ElementTree.fromstring(content)
|
|
self.assertEqual(root.tag, "{%s}user" % settings.SCHEMAROOT)
|
|
self.assertEqual(
|
|
root.findall("{%s}account" % settings.SCHEMAROOT)[0].items()[0][1],
|
|
self.user.username,
|
|
)
|
|
self.assertEqual(
|
|
root.findall("{%s}account" % settings.SCHEMAROOT)[0].items()[1][1],
|
|
self.user.password,
|
|
)
|
|
|
|
self.assertEqual(
|
|
root.findall("{%s}emails" % settings.SCHEMAROOT)[0][0].text,
|
|
self.user.confirmedemail_set.first().email,
|
|
)
|
|
self.assertEqual(
|
|
root.findall("{%s}openids" % settings.SCHEMAROOT)[0][0].text,
|
|
self.user.confirmedopenid_set.first().openid,
|
|
)
|
|
|
|
data = root.findall("{%s}photos" % settings.SCHEMAROOT)[0][0].text
|
|
|
|
data = data.strip("'")
|
|
data = data.strip("\\n")
|
|
data = data.lstrip("b'")
|
|
bindata = base64.decodebytes(bytes(data, "utf-8"))
|
|
image = Image.open(BytesIO(bindata))
|
|
self.assertTrue(hasattr(image, "png"))
|
|
|
|
def test_upload_export(self):
|
|
"""
|
|
Test if uploading export works
|
|
"""
|
|
|
|
# Ensure we have data in place
|
|
self.test_export()
|
|
|
|
self.login()
|
|
self.client.get(reverse("export"))
|
|
response = self.client.post(
|
|
reverse("export"),
|
|
{},
|
|
follow=False,
|
|
)
|
|
self.assertIsInstance(response.content, bytes)
|
|
|
|
fh_gzip = gzip.open(BytesIO(response.content), "rb")
|
|
fh = BytesIO(response.content)
|
|
|
|
response = self._uploading_export_check(
|
|
fh_gzip, "Unable to parse file: Not a gzipped file"
|
|
)
|
|
response = self._uploading_export_check(fh, "Choose items to be imported")
|
|
self.assertContains(
|
|
response,
|
|
"asdf@asdf.local",
|
|
2,
|
|
200,
|
|
"Upload didn't work?",
|
|
)
|
|
|
|
def _uploading_export_check(self, fh, message):
|
|
"""
|
|
Helper function to upload an export
|
|
"""
|
|
result = self.client.post(
|
|
reverse("upload_export"),
|
|
data={"not_porn": "on", "can_distribute": "on", "export_file": fh},
|
|
follow=True,
|
|
)
|
|
fh.close()
|
|
self.assertEqual(result.status_code, 200, "Upload worked")
|
|
self.assertContains(result, message, 1, 200, "Upload didn't work?")
|
|
|
|
return result
|
|
|
|
def test_preferences_page(self):
|
|
"""
|
|
Test if preferences page works
|
|
"""
|
|
|
|
self.login()
|
|
self.client.get(reverse("user_preference"))
|
|
|
|
def test_delete_user(self):
|
|
"""
|
|
Test if deleting user profile works
|
|
"""
|
|
|
|
self.login()
|
|
self.client.get(reverse("delete"))
|
|
response = self.client.post(
|
|
reverse("delete"),
|
|
data={"password": self.password},
|
|
follow=True,
|
|
)
|
|
self.assertEqual(response.status_code, 200, "Deletion worked")
|
|
self.assertEqual(User.objects.count(), 0, "No user there any more")
|
|
|
|
def test_confirm_already_confirmed(self):
|
|
"""
|
|
Try to confirm a mail address that has been confirmed (by another user)
|
|
"""
|
|
|
|
# Add mail address (stays unconfirmed)
|
|
self.test_add_email()
|
|
|
|
# Create a second user that will conflict
|
|
user2 = User.objects.create_user(
|
|
username=f"{self.username}1",
|
|
password=self.password,
|
|
first_name=self.first_name,
|
|
last_name=self.last_name,
|
|
)
|
|
ConfirmedEmail.objects.create(
|
|
email=self.email,
|
|
user=user2,
|
|
)
|
|
|
|
# Just to be sure
|
|
self.assertEqual(
|
|
self.user.unconfirmedemail_set.first().email,
|
|
user2.confirmedemail_set.first().email,
|
|
"Mail not the same?",
|
|
)
|
|
|
|
# This needs to be caught
|
|
with contextlib.suppress(AssertionError):
|
|
self.test_confirm_email()
|
|
# Request a random page, so we can access the messages
|
|
response = self.client.get(reverse("profile"))
|
|
self.assertEqual(
|
|
str(list(response.context[0]["messages"])[0]),
|
|
"This mail address has been taken already and cannot be confirmed",
|
|
"This should return an error message!",
|
|
)
|
|
|
|
|
|
class OpenIDErrorHandlingTestCase(TestCase):
|
|
"""
|
|
Test cases for OpenID error handling and error.html template coverage
|
|
"""
|
|
|
|
def setUp(self):
|
|
"""Set up test user and client"""
|
|
self.username = random_string()
|
|
self.password = random_string()
|
|
self.user = User.objects.create_user(
|
|
username=self.username,
|
|
password=self.password,
|
|
)
|
|
self.client = Client()
|
|
|
|
def login(self):
|
|
"""Login as test user"""
|
|
self.client.login(username=self.username, password=self.password)
|
|
|
|
def test_openid_discovery_failure_renders_error_template(self):
|
|
"""
|
|
Test that OpenID discovery failure renders error.html template
|
|
"""
|
|
from unittest.mock import patch, MagicMock
|
|
from openid.consumer import consumer
|
|
from ivatar.ivataraccount.models import UnconfirmedOpenId
|
|
|
|
self.login()
|
|
|
|
# Create an unconfirmed OpenID
|
|
unconfirmed = UnconfirmedOpenId.objects.create(
|
|
user=self.user,
|
|
openid="http://invalid-openid-provider.example.com/",
|
|
)
|
|
|
|
# Mock the OpenID consumer to raise DiscoveryFailure
|
|
with patch(
|
|
"ivatar.ivataraccount.views.consumer.Consumer"
|
|
) as mock_consumer_class:
|
|
mock_consumer = MagicMock()
|
|
mock_consumer_class.return_value = mock_consumer
|
|
# Create a proper DiscoveryFailure with required http_response parameter
|
|
mock_response = MagicMock()
|
|
mock_response.status_code = 404
|
|
discovery_failure = consumer.DiscoveryFailure(
|
|
"Invalid provider", mock_response
|
|
)
|
|
mock_consumer.begin.side_effect = discovery_failure
|
|
|
|
# Make request to openid_redirection view
|
|
response = self.client.get(
|
|
reverse("openid_redirection", args=[unconfirmed.id]), follow=True
|
|
)
|
|
|
|
# Verify we get redirected to profile with error message
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertRedirects(response, reverse("profile"))
|
|
|
|
# Check that error message is in the response
|
|
messages = list(response.context[0]["messages"])
|
|
self.assertTrue(
|
|
any("OpenID discovery failed" in str(msg) for msg in messages)
|
|
)
|
|
|
|
def test_openid_confirmation_failure_renders_error_template(self):
|
|
"""
|
|
Test that OpenID confirmation failure renders error.html template
|
|
"""
|
|
from unittest.mock import patch, MagicMock
|
|
from openid.consumer import consumer
|
|
from ivatar.ivataraccount.models import UnconfirmedOpenId
|
|
|
|
self.login()
|
|
|
|
# Create an unconfirmed OpenID
|
|
unconfirmed = UnconfirmedOpenId.objects.create(
|
|
user=self.user,
|
|
openid="http://test-provider.example.com/",
|
|
)
|
|
|
|
# Mock the OpenID consumer to return FAILURE status
|
|
with patch(
|
|
"ivatar.ivataraccount.views.consumer.Consumer"
|
|
) as mock_consumer_class:
|
|
mock_consumer = MagicMock()
|
|
mock_consumer_class.return_value = mock_consumer
|
|
|
|
# Create a mock response with FAILURE status
|
|
mock_response = MagicMock()
|
|
mock_response.status = consumer.FAILURE
|
|
mock_response.message = "Authentication failed"
|
|
mock_consumer.complete.return_value = mock_response
|
|
|
|
# Make request to confirm_openid view
|
|
response = self.client.get(
|
|
reverse("confirm_openid", args=[unconfirmed.id]), follow=True
|
|
)
|
|
|
|
# Verify we get redirected to profile with error message
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertRedirects(response, reverse("profile"))
|
|
|
|
# Check that error message is in the response
|
|
messages = list(response.context[0]["messages"])
|
|
self.assertTrue(any("Confirmation failed" in str(msg) for msg in messages))
|
|
|
|
def test_openid_cancellation_renders_error_template(self):
|
|
"""
|
|
Test that OpenID cancellation renders error.html template
|
|
"""
|
|
from unittest.mock import patch, MagicMock
|
|
from openid.consumer import consumer
|
|
from ivatar.ivataraccount.models import UnconfirmedOpenId
|
|
|
|
self.login()
|
|
|
|
# Create an unconfirmed OpenID
|
|
unconfirmed = UnconfirmedOpenId.objects.create(
|
|
user=self.user,
|
|
openid="http://test-provider.example.com/",
|
|
)
|
|
|
|
# Mock the OpenID consumer to return CANCEL status
|
|
with patch(
|
|
"ivatar.ivataraccount.views.consumer.Consumer"
|
|
) as mock_consumer_class:
|
|
mock_consumer = MagicMock()
|
|
mock_consumer_class.return_value = mock_consumer
|
|
|
|
# Create a mock response with CANCEL status
|
|
mock_response = MagicMock()
|
|
mock_response.status = consumer.CANCEL
|
|
mock_consumer.complete.return_value = mock_response
|
|
|
|
# Make request to confirm_openid view
|
|
response = self.client.get(
|
|
reverse("confirm_openid", args=[unconfirmed.id]), follow=True
|
|
)
|
|
|
|
# Verify we get redirected to profile with error message
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertRedirects(response, reverse("profile"))
|
|
|
|
# Check that error message is in the response
|
|
messages = list(response.context[0]["messages"])
|
|
self.assertTrue(any("Cancelled by user" in str(msg) for msg in messages))
|
|
|
|
def test_openid_unknown_error_renders_error_template(self):
|
|
"""
|
|
Test that unknown OpenID verification error renders error.html template
|
|
"""
|
|
from unittest.mock import patch, MagicMock
|
|
from ivatar.ivataraccount.models import UnconfirmedOpenId
|
|
|
|
self.login()
|
|
|
|
# Create an unconfirmed OpenID
|
|
unconfirmed = UnconfirmedOpenId.objects.create(
|
|
user=self.user,
|
|
openid="http://test-provider.example.com/",
|
|
)
|
|
|
|
# Mock the OpenID consumer to return unknown status
|
|
with patch(
|
|
"ivatar.ivataraccount.views.consumer.Consumer"
|
|
) as mock_consumer_class:
|
|
mock_consumer = MagicMock()
|
|
mock_consumer_class.return_value = mock_consumer
|
|
|
|
# Create a mock response with unknown status
|
|
mock_response = MagicMock()
|
|
mock_response.status = "UNKNOWN_STATUS"
|
|
mock_consumer.complete.return_value = mock_response
|
|
|
|
# Make request to confirm_openid view
|
|
response = self.client.get(
|
|
reverse("confirm_openid", args=[unconfirmed.id]), follow=True
|
|
)
|
|
|
|
# Verify we get redirected to profile with error message
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertRedirects(response, reverse("profile"))
|
|
|
|
# Check that error message is in the response
|
|
messages = list(response.context[0]["messages"])
|
|
self.assertTrue(
|
|
any("Unknown verification error" in str(msg) for msg in messages)
|
|
)
|
|
|
|
def test_openid_nonexistent_id_error(self):
|
|
"""
|
|
Test that accessing non-existent OpenID ID shows error message
|
|
"""
|
|
self.login()
|
|
|
|
# Try to access a non-existent OpenID ID
|
|
response = self.client.get(
|
|
reverse("openid_redirection", args=[99999]), follow=True
|
|
)
|
|
|
|
# Verify we get redirected to profile with error message
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertRedirects(response, reverse("profile"))
|
|
|
|
# Check that error message is in the response
|
|
messages = list(response.context[0]["messages"])
|
|
self.assertTrue(any("ID does not exist" in str(msg) for msg in messages))
|
|
|
|
def test_django_openid_auth_failure_template_coverage(self):
|
|
"""
|
|
Test that django-openid-auth failure template uses error.html
|
|
This test verifies the OpenID login page renders correctly
|
|
"""
|
|
# Try to access the OpenID login page
|
|
response = self.client.get(reverse("openid-login"))
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
# The login page should render successfully
|
|
self.assertContains(response, "OpenID Login")
|
|
|
|
def test_error_template_direct_rendering(self):
|
|
"""
|
|
Test error.html template directly to ensure it renders correctly
|
|
"""
|
|
from django.test import RequestFactory
|
|
from django.template import Context, Template
|
|
from django.contrib.auth.models import AnonymousUser
|
|
|
|
# Test with authenticated user
|
|
factory = RequestFactory()
|
|
request = factory.get("/")
|
|
request.user = self.user
|
|
|
|
# Test template with error message
|
|
template_content = """
|
|
{% extends 'error.html' %}
|
|
{% load i18n %}
|
|
{% block errormessage %}
|
|
{% trans 'Test error message:' %} {{ errormessage }}
|
|
{% endblock errormessage %}
|
|
"""
|
|
|
|
template = Template(template_content)
|
|
context = Context(
|
|
{
|
|
"request": request,
|
|
"errormessage": "This is a test error",
|
|
"user": self.user,
|
|
}
|
|
)
|
|
|
|
rendered = template.render(context)
|
|
|
|
# Verify the template renders without errors
|
|
self.assertIn("Error!", rendered)
|
|
self.assertIn("This is a test error", rendered)
|
|
# Check for the profile link in the navbar (not in the backlink block)
|
|
self.assertIn("/accounts/profile/", rendered)
|
|
|
|
# Test with anonymous user
|
|
request.user = AnonymousUser()
|
|
context = Context(
|
|
{
|
|
"request": request,
|
|
"errormessage": "This is a test error",
|
|
"user": AnonymousUser(),
|
|
}
|
|
)
|
|
|
|
rendered = template.render(context)
|
|
|
|
# Verify the template renders without errors for anonymous users
|
|
self.assertIn("Error!", rendered)
|
|
self.assertIn("This is a test error", rendered)
|
|
# Should not contain profile link for anonymous users
|
|
self.assertNotIn("/accounts/profile/", rendered)
|
|
|
|
def test_openid_failure_template_inheritance(self):
|
|
"""
|
|
Test that openid/failure.html properly extends error.html
|
|
"""
|
|
from django.test import RequestFactory
|
|
from django.template import Context, Template
|
|
|
|
factory = RequestFactory()
|
|
request = factory.get("/")
|
|
request.user = self.user
|
|
|
|
# Test the openid/failure.html template
|
|
template_content = """
|
|
{% extends 'error.html' %}
|
|
{% load i18n %}
|
|
{% block errormessage %}
|
|
{% trans 'OpenID error:' %} {{ message }}
|
|
{% endblock errormessage %}
|
|
"""
|
|
|
|
template = Template(template_content)
|
|
context = Context(
|
|
{
|
|
"request": request,
|
|
"message": "Authentication failed",
|
|
"user": self.user,
|
|
}
|
|
)
|
|
|
|
rendered = template.render(context)
|
|
|
|
# Verify the template renders correctly
|
|
self.assertIn("Error!", rendered)
|
|
self.assertIn("OpenID error:", rendered)
|
|
self.assertIn("Authentication failed", rendered)
|
|
# Check for the profile link in the navbar (not in the backlink block)
|
|
self.assertIn("/accounts/profile/", rendered)
|