Coverage for ivatar/ivataraccount/test_views.py: 100%
635 statements
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-26 00:11 +0000
« prev ^ index » next coverage.py v7.6.9, created at 2024-12-26 00:11 +0000
1# -*- coding: utf-8 -*-
2"""
3Test our views in ivatar.ivataraccount.views and ivatar.views
4"""
5# pylint: disable=too-many-lines
6from urllib.parse import urlsplit
7from io import BytesIO
8import io
9import os
10import gzip
11import xml.etree.ElementTree
12import base64
13import django
14from django.test import TestCase
15from django.test import Client
16from django.urls import reverse
17from django.core import mail
18from django.contrib.auth.models import User
19from django.contrib.auth import authenticate
20import hashlib
22from libravatar import libravatar_url
24from PIL import Image
26os.environ["DJANGO_SETTINGS_MODULE"] = "ivatar.settings"
27django.setup()
29# pylint: disable=wrong-import-position
30from ivatar import settings
31from ivatar.ivataraccount.forms import MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT
32from ivatar.ivataraccount.models import Photo, ConfirmedOpenId, ConfirmedEmail
33from ivatar.utils import random_string
35# pylint: enable=wrong-import-position
37TEST_IMAGE_FILE = os.path.join(settings.STATIC_ROOT, "img", "deadbeef.png")
40class Tester(TestCase): # pylint: disable=too-many-public-methods
41 """
42 Main test class
43 """
45 client = Client()
46 user = None
47 username = random_string()
48 password = random_string()
49 email = "%s@%s.%s" % (username, random_string(), random_string(2))
50 # Dunno why random tld doesn't work, but I'm too lazy now to investigate
51 openid = "http://%s.%s.%s/" % (username, random_string(), "org")
52 first_name = random_string()
53 last_name = random_string()
55 def login(self):
56 """
57 Login as user
58 """
59 self.client.login(username=self.username, password=self.password)
61 def setUp(self):
62 """
63 Prepare for tests.
64 - Create user
65 """
66 self.user = User.objects.create_user(
67 username=self.username,
68 password=self.password,
69 first_name=self.first_name,
70 last_name=self.last_name,
71 )
73 def test_new_user(self):
74 """
75 Create a new user
76 """
77 response = self.client.get(reverse("new_account"))
78 self.assertEqual(response.status_code, 200, "no 200 ok?")
79 # Empty database / eliminate existing users
80 User.objects.all().delete()
81 url = reverse("new_account")
82 response = self.client.post(
83 url,
84 {
85 "username": self.username,
86 "password1": self.password,
87 "password2": self.password,
88 },
89 follow=True,
90 )
91 self.assertEqual(response.status_code, 200, "unable to create user?")
92 self.assertEqual(response.context[0]["user"].username, self.username)
94 def test_new_user_twice(self):
95 """
96 Try to create a user that already exists
97 """
98 response = self.client.get(reverse("new_account"))
99 self.assertEqual(response.status_code, 200, "no 200 ok?")
100 # Due to setUp(), we already have this user!
101 url = reverse("new_account")
102 response = self.client.post(
103 url,
104 {
105 "username": self.username,
106 "password1": self.password,
107 "password2": self.password,
108 },
109 follow=True,
110 )
111 self.assertEqual(response.status_code, 200, "unable to create user?")
112 self.assertEqual(response.context[0]["user"].username, "")
113 self.assertContains(
114 response,
115 "A user with that username already exists.",
116 1,
117 200,
118 "can we create a user a second time???",
119 )
121 def test_set_password(self):
122 """
123 Change the user password
124 """
125 self.login()
126 response = self.client.get(reverse("password_set"))
127 self.assertEqual(response.status_code, 200, "no 200 ok?")
128 self.password = random_string()
129 response = self.client.post(
130 reverse("password_set"),
131 {
132 "new_password1": self.password,
133 "new_password2": self.password,
134 },
135 follow=True,
136 )
138 self.assertEqual(response.status_code, 200, "cannot change password?")
139 self.assertEqual(
140 str(list(response.context[0]["messages"])[0]),
141 "password changed successfully - please login again",
142 "password change not successful?",
143 )
145 self.assertIsNotNone(
146 authenticate(
147 username=self.username,
148 password=self.password,
149 ),
150 "cannot authenticate with new password!?",
151 )
153 self.login()
154 response = self.client.get(reverse("profile"))
155 self.assertEqual(response.context[0]["user"].is_anonymous, False)
157 def test_add_email(self):
158 """
159 Add e-mail address
160 """
161 self.login()
162 response = self.client.get(reverse("add_email"))
163 self.assertEqual(response.status_code, 200, "no 200 ok?")
164 # Avoid sending out mails
165 settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
166 response = self.client.post(
167 reverse("add_email"),
168 {
169 "email": self.email,
170 },
171 follow=True,
172 )
173 self.assertEqual(response.status_code, 200, "cannot add email?")
174 self.assertEqual(
175 len(response.context[0]["messages"]),
176 1,
177 "there must not be more or less than ONE (1) message",
178 )
179 self.assertEqual(
180 str(list(response.context[0]["messages"])[0]),
181 "Address added successfully",
182 "unable to add mail address?",
183 )
185 def test_confirm_email(self):
186 """
187 Confirm unconfirmed email
188 """
189 self.login()
190 # Avoid sending out mails
191 settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
192 response = self.client.post(
193 reverse("add_email"),
194 {
195 "email": self.email,
196 },
197 follow=True,
198 )
199 unconfirmed = self.user.unconfirmedemail_set.first()
200 verification_key = unconfirmed.verification_key
201 url = reverse("confirm_email", args=[verification_key])
202 response = self.client.get(url)
203 self.assertEqual(response.status_code, 200, "unable to confirm mail address?")
205 self.assertEqual(
206 self.user.unconfirmedemail_set.count(),
207 0,
208 "there must not be any unconfirmed address, after confirming it",
209 )
210 self.assertEqual(
211 self.user.confirmedemail_set.count(),
212 1,
213 "there must not be more or less than ONE (1) confirmed address!",
214 )
216 def test_confirm_email_w_invalid_auth_key(self): # pylint: disable=invalid-name
217 """
218 Test confirmation with invalid auth key
219 """
220 self.login()
221 # Avoid sending out mails
222 settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
223 response = self.client.post(
224 reverse("add_email"),
225 {
226 "email": self.email,
227 },
228 follow=True,
229 )
230 url = reverse("confirm_email", args=["x"])
231 response = self.client.get(url, follow=True)
232 self.assertEqual(
233 response.status_code,
234 200,
235 "Not able to request confirmation - without verification key?",
236 )
237 self.assertEqual(
238 str(list(response.context[0]["messages"])[-1]),
239 "Verification key incorrect",
240 "Confirm w/o verification key does not produce error message?",
241 )
243 def test_confirm_email_w_inexisting_auth_key(self): # pylint: disable=invalid-name
244 """
245 Test confirmation with inexisting auth key
246 """
247 self.login()
248 # Avoid sending out mails
249 settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
250 response = self.client.post(
251 reverse("add_email"),
252 {
253 "email": self.email,
254 },
255 follow=True,
256 )
257 url = reverse("confirm_email", args=["x" * 64])
258 response = self.client.get(url, follow=True)
259 self.assertEqual(
260 response.status_code,
261 200,
262 "Not able to request confirmation - without verification key?",
263 )
264 self.assertEqual(
265 str(list(response.context[0]["messages"])[-1]),
266 "Verification key does not exist",
267 "Confirm w/o inexisting key does not produce error message?",
268 )
270 def test_remove_confirmed_email(self):
271 """
272 Remove confirmed email
273 """
274 self.login()
275 # Avoid sending out mails
276 settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
277 response = self.client.post(
278 reverse("add_email"),
279 {
280 "email": self.email,
281 },
282 ) # Create test address
283 unconfirmed = self.user.unconfirmedemail_set.first()
284 verification_key = unconfirmed.verification_key
285 url = reverse("confirm_email", args=[verification_key])
286 self.client.get(url) # Confirm
287 url = reverse(
288 "remove_confirmed_email", args=[self.user.confirmedemail_set.first().id]
289 )
290 response = self.client.post(url, follow=True)
291 self.assertEqual(
292 response.status_code, 200, "unable to remove confirmed address?"
293 )
294 self.assertEqual(
295 str(list(response.context[0]["messages"])[-1]),
296 "Address removed",
297 "Removing confirmed mail does not work?",
298 )
300 def test_remove_not_existing_confirmed_email(self): # pylint: disable=invalid-name
301 """
302 Try removing confirmed mail that doesn't exist
303 """
304 self.login()
305 url = reverse("remove_confirmed_email", args=[1234])
306 response = self.client.post(url, follow=True)
307 self.assertEqual(
308 response.status_code, 200, "removing email does not redirect to profile?"
309 )
310 self.assertEqual(
311 str(list(response.context[0]["messages"])[0]),
312 "Address does not exist",
313 "Removing not existing (confirmed) address, should produce an\
314 error message!",
315 )
317 def test_remove_unconfirmed_email(self):
318 """
319 Remove unconfirmed email
320 """
321 self.login()
322 # Avoid sending out mails
323 settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
324 response = self.client.post(
325 reverse("add_email"),
326 {
327 "email": self.email,
328 },
329 ) # Create test address
330 url = reverse(
331 "remove_unconfirmed_email", args=[self.user.unconfirmedemail_set.first().id]
332 )
333 response = self.client.post(url, follow=True)
334 self.assertEqual(
335 response.status_code, 200, "unable to remove unconfirmed address?"
336 )
337 # Take care, since we do not fetch any page now, the message we need
338 # to check is the _second_ (aka [1], since first is [0])
339 self.assertEqual(
340 str(list(response.context[0]["messages"])[1]),
341 "Address removed",
342 "Removing unconfirmed mail does not work?",
343 )
345 def test_gravatar_photo_import(self):
346 """
347 import photo from Gravatar (with known mail address)
348 """
349 self.login()
350 # Avoid sending out mails
351 settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
352 response = self.client.post(
353 reverse("add_email"),
354 {
355 "email": "oliver@linux-kernel.at", # Whohu, static :-[
356 },
357 ) # Create test address
358 unconfirmed = self.user.unconfirmedemail_set.first()
359 verification_key = unconfirmed.verification_key
360 url = reverse("confirm_email", args=[verification_key])
361 self.client.get(url) # Confirm
363 url = reverse("import_photo", args=[self.user.confirmedemail_set.first().id])
364 response = self.client.post(
365 url,
366 {
367 "photo_Gravatar": 1,
368 },
369 follow=True,
370 )
371 self.assertEqual(
372 response.status_code, 200, "unable to import photo from Gravatar?"
373 )
374 self.assertEqual(
375 str(list(response.context[0]["messages"])[-1]),
376 "Gravatar image successfully imported",
377 "Importing gravatar photo did not work?",
378 )
379 self.assertIsInstance(
380 self.user.photo_set.first(), Photo, "why is there no Photo (instance)?"
381 )
383 def test_raw_image(self):
384 """
385 test raw image view (as seen in profile <img src=
386 """
388 # Ensure we have a photo
389 self.test_gravatar_photo_import()
390 response = self.client.get(
391 reverse("raw_image", args=[self.user.photo_set.first().id])
392 )
393 self.assertEqual(response.status_code, 200, "cannot fetch photo?")
394 # Probably not the best way to access the content type
395 self.assertEqual(response["Content-Type"], "image/jpg", "Content type wrong!?")
397 self.assertEqual(
398 response.content,
399 self.user.photo_set.first().data,
400 "raw_image should return the same content as if we\
401 read it directly from the DB",
402 )
404 def test_delete_photo(self):
405 """
406 test deleting the photo
407 """
409 # Ensure we have a photo
410 self.test_gravatar_photo_import()
412 url = reverse("delete_photo", args=[self.user.photo_set.first().id])
413 response = self.client.get(url, follow=True)
414 self.assertEqual(response.status_code, 200, "deleting photo does not work?")
415 self.assertEqual(
416 str(list(response.context[0]["messages"])[-1]),
417 "Photo deleted successfully",
418 "Photo deletion did not work?",
419 )
421 def test_delete_inexisting_photo(self):
422 """
423 test deleting the photo
424 """
426 # Ensure we have a photo
427 self.test_gravatar_photo_import()
429 url = reverse("delete_photo", args=[1234])
430 response = self.client.get(url, follow=True)
431 self.assertEqual(response.status_code, 200, "post to delete does not work?")
432 self.assertEqual(
433 str(list(response.context[0]["messages"])[-1]),
434 "No such image or no permission to delete it",
435 "Deleting photo that does not exist, should return error message",
436 )
438 def test_too_many_unconfirmed_email(self):
439 """
440 Request too many unconfirmed email addresses, make sure we
441 cannot add more
442 """
443 self.login()
444 # Avoid sending out mails
445 settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
447 max_num_unconfirmed = getattr(
448 settings, "MAX_NUM_UNCONFIRMED_EMAILS", MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT
449 )
451 for i in range(max_num_unconfirmed + 1):
452 response = self.client.post(
453 reverse("add_email"),
454 {
455 "email": "%i.%s" % (i, self.email),
456 },
457 follow=True,
458 ) # Create test addresses + 1 too much
459 # TODO: This test isn't super criticial, but needs to be fixed
460 # Currently issues an error with an unbound form
461 # self.assertFormError(
462 # response, "form", None, "Too many unconfirmed mail addresses!"
463 # )
465 def test_add_mail_address_twice(self):
466 """
467 Request the same mail address two times, should not lead to
468 having the same address twice
469 """
470 self.login()
471 # Avoid sending out mails
472 settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
474 for _ in range(2):
475 response = self.client.post(
476 reverse("add_email"),
477 {
478 "email": self.email,
479 },
480 follow=True,
481 )
482 # TODO: This test isn't super criticial, but needs to be fixed
483 # Currently issues an error with an unbound form
484 # self.assertFormError(
485 # response, "form", "email", "Address already added, currently unconfirmed"
486 # )
488 def test_add_already_confirmed_email_self(self): # pylint: disable=invalid-name
489 """
490 Request adding mail address that is already confirmed (by someone)
491 """
492 # Create test mail and confirm it, reuse test code
493 # Should set EMAIL_BACKEND, so no need to do it here
494 self.test_confirm_email()
496 response = self.client.post(
497 reverse("add_email"),
498 {
499 "email": self.email,
500 },
501 follow=True,
502 )
503 # TODO: This test isn't super criticial, but needs to be fixed
504 # Currently issues an error with an unbound form
505 # self.assertFormError(
506 # response, "form", "email", "Address already confirmed (by you)"
507 # )
509 def test_add_already_confirmed_email_other(self): # pylint: disable=invalid-name
510 """
511 Request adding mail address that is already confirmed (by someone)
512 """
513 # Create test mail and confirm it, reuse test code
514 # Should set EMAIL_BACKEND, so no need to do it here
515 self.test_confirm_email()
517 # Create another user and assign the mail address to that one
518 # in order to test the correct error message
519 otheruser = User.objects.create(username="otheruser")
520 confirmedemail = ConfirmedEmail.objects.last()
521 confirmedemail.user = otheruser
522 confirmedemail.save()
524 response = self.client.post(
525 reverse("add_email"),
526 {
527 "email": self.email,
528 },
529 follow=True,
530 )
531 # TODO: This test isn't super criticial, but needs to be fixed
532 # Currently issues an error with an unbound form
533 # self.assertFormError(
534 # response, "form", "email", "Address already confirmed (by someone)"
535 # )
537 def test_remove_unconfirmed_non_existing_email(
538 self,
539 ): # pylint: disable=invalid-name
540 """
541 Remove unconfirmed email that doesn't exist
542 """
543 self.login()
544 url = reverse("remove_unconfirmed_email", args=[1234])
545 response = self.client.post(url, follow=True)
546 self.assertEqual(
547 response.status_code, 200, "unable to remove non existing address?"
548 )
549 self.assertEqual(
550 str(list(response.context[0]["messages"])[0]),
551 "Address does not exist",
552 "Removing address that does not\
553 exist, should return error message!",
554 )
556 def test_upload_image(
557 self, test_only_one=True
558 ): # pylint: disable=inconsistent-return-statements
559 """
560 Test uploading image
561 """
562 self.login()
563 url = reverse("upload_photo")
564 # rb => Read binary
565 with open(TEST_IMAGE_FILE, "rb") as photo:
566 response = self.client.post(
567 url,
568 {
569 "photo": photo,
570 "not_porn": True,
571 "can_distribute": True,
572 },
573 follow=True,
574 )
575 if test_only_one:
576 self.assertEqual(
577 self.user.photo_set.count(), 1, "there must be exactly one photo now!"
578 )
579 self.assertEqual(
580 str(list(response.context[0]["messages"])[-1]),
581 "Successfully uploaded",
582 "A valid image should return a success message!",
583 )
584 self.assertEqual(
585 self.user.photo_set.first().format,
586 "png",
587 "Format must be png, since we uploaded a png!",
588 )
589 else:
590 return response
592 def test_upload_too_many_images(self):
593 """
594 Test uploading more images than we are allowed
595 """
596 for _ in range(settings.MAX_NUM_PHOTOS + 1):
597 response = self.test_upload_image(test_only_one=False)
598 self.assertEqual(
599 self.user.photo_set.count(),
600 settings.MAX_NUM_PHOTOS,
601 "there may not be more photos than allowed!",
602 )
603 # Take care we need to check the last message
604 self.assertEqual(
605 str(list(response.context[0]["messages"])[-1]),
606 "Maximum number of photos (%i) reached" % settings.MAX_NUM_PHOTOS,
607 "Adding more than allowed images, should return error message!",
608 )
610 def test_upload_too_big_image(self):
611 """
612 Test uploading image that is too big
613 """
614 self.login()
615 url = reverse("upload_photo")
616 # rb => Read binary
617 response = self.client.post(
618 url,
619 {
620 "photo": io.StringIO("x" * (settings.MAX_PHOTO_SIZE + 1)),
621 "not_porn": True,
622 "can_distribute": True,
623 },
624 follow=True,
625 )
626 self.assertEqual(
627 str(list(response.context[0]["messages"])[0]),
628 "Image too big",
629 "Uploading too big image, should return error message!",
630 )
632 def test_upload_invalid_image(self):
633 """
634 Test invalid image data
635 """
636 self.login()
637 url = reverse("upload_photo")
638 # rb => Read binary
639 response = self.client.post(
640 url,
641 {
642 "photo": io.StringIO("x"),
643 "not_porn": True,
644 "can_distribute": True,
645 },
646 follow=True,
647 )
648 self.assertEqual(
649 str(list(response.context[0]["messages"])[0]),
650 "Invalid Format",
651 "Invalid img data should return error message!",
652 )
654 def test_upload_invalid_image_format(self): # pylint: disable=invalid-name
655 """
656 Test if invalid format is correctly detected
657 """
658 self.login()
659 url = reverse("upload_photo")
660 # rb => Read binary
661 with open(os.path.join(settings.STATIC_ROOT, "img", "mm.svg"), "rb") as photo:
662 response = self.client.post(
663 url,
664 {
665 "photo": photo,
666 "not_porn": True,
667 "can_distribute": True,
668 },
669 follow=True,
670 )
671 self.assertEqual(
672 str(list(response.context[0]["messages"])[0]),
673 "Invalid Format",
674 "Invalid img data should return error message!",
675 )
677 def test_upload_gif_image(self):
678 """
679 Test if gif is correctly detected and can be viewed
680 """
681 self.login()
682 url = reverse("upload_photo")
683 # rb => Read binary
684 # Broken is _not_ broken - it's just an 'x' :-)
685 with open(
686 os.path.join(settings.STATIC_ROOT, "img", "broken.gif"), "rb"
687 ) as photo:
688 response = self.client.post(
689 url,
690 {
691 "photo": photo,
692 "not_porn": True,
693 "can_distribute": True,
694 },
695 follow=True,
696 )
697 self.assertEqual(
698 str(list(response.context[0]["messages"])[0]),
699 "Successfully uploaded",
700 "GIF upload failed?!",
701 )
702 self.assertEqual(
703 self.user.photo_set.first().format,
704 "gif",
705 "Format must be gif, since we uploaded a GIF!",
706 )
707 self.test_confirm_email()
708 self.user.confirmedemail_set.first().photo = self.user.photo_set.first()
709 urlobj = urlsplit(
710 libravatar_url(
711 email=self.user.confirmedemail_set.first().email,
712 )
713 )
714 url = "%s?%s" % (urlobj.path, urlobj.query)
715 response = self.client.get(url, follow=True)
716 self.assertEqual(response.status_code, 200, "unable to fetch avatar?")
718 def test_upload_jpg_image(self):
719 """
720 Test if jpg is correctly detected and can be viewed
721 """
722 self.login()
723 url = reverse("upload_photo")
724 # rb => Read binary
725 # Broken is _not_ broken - it's just an 'x' :-)
726 with open(
727 os.path.join(settings.STATIC_ROOT, "img", "broken.jpg"), "rb"
728 ) as photo:
729 response = self.client.post(
730 url,
731 {
732 "photo": photo,
733 "not_porn": True,
734 "can_distribute": True,
735 },
736 follow=True,
737 )
738 self.assertEqual(
739 str(list(response.context[0]["messages"])[0]),
740 "Successfully uploaded",
741 "JPEG upload failed?!",
742 )
743 self.assertEqual(
744 self.user.photo_set.first().format,
745 "jpg",
746 "Format must be jpeg, since we uploaded a jpeg!",
747 )
748 self.test_confirm_email()
749 self.user.confirmedemail_set.first().photo = self.user.photo_set.first()
750 urlobj = urlsplit(
751 libravatar_url(
752 email=self.user.confirmedemail_set.first().email,
753 )
754 )
755 url = "%s?%s" % (urlobj.path, urlobj.query)
756 response = self.client.get(url, follow=True)
757 self.assertEqual(response.status_code, 200, "unable to fetch avatar?")
759 def test_upload_webp_image(self):
760 """
761 Test if webp is correctly detected and can be viewed
762 """
763 self.login()
764 url = reverse("upload_photo")
765 # rb => Read binary
766 # Broken is _not_ broken - it's just an 'x' :-)
767 with open(
768 os.path.join(settings.STATIC_ROOT, "img", "broken.webp"), "rb"
769 ) as photo:
770 response = self.client.post(
771 url,
772 {
773 "photo": photo,
774 "not_porn": True,
775 "can_distribute": True,
776 },
777 follow=True,
778 )
779 self.assertEqual(
780 str(list(response.context[0]["messages"])[0]),
781 "Successfully uploaded",
782 "WEBP upload failed?!",
783 )
784 self.assertEqual(
785 self.user.photo_set.first().format,
786 "webp",
787 "Format must be webp, since we uploaded a webp!",
788 )
789 self.test_confirm_email()
790 self.user.confirmedemail_set.first().photo = self.user.photo_set.first()
791 urlobj = urlsplit(
792 libravatar_url(
793 email=self.user.confirmedemail_set.first().email,
794 )
795 )
796 url = "%s?%s" % (urlobj.path, urlobj.query)
797 response = self.client.get(url, follow=True)
798 self.assertEqual(response.status_code, 200, "unable to fetch avatar?")
800 def test_upload_unsupported_tif_image(self): # pylint: disable=invalid-name
801 """
802 Test if unsupported format is correctly detected
803 """
804 self.login()
805 url = reverse("upload_photo")
806 # rb => Read binary
807 with open(
808 os.path.join(settings.STATIC_ROOT, "img", "hackergotchi_test.tif"), "rb"
809 ) as photo:
810 response = self.client.post(
811 url,
812 {
813 "photo": photo,
814 "not_porn": True,
815 "can_distribute": True,
816 },
817 follow=True,
818 )
819 self.assertEqual(
820 str(list(response.context[0]["messages"])[0]),
821 "Invalid Format",
822 "Invalid img data should return error message!",
823 )
825 def test_automatic_photo_assign_to_confirmed_mail(
826 self,
827 ): # pylint: disable=invalid-name
828 """
829 Test if automatic assignment of photo works
830 """
831 self.test_upload_image()
832 self.test_confirm_email()
833 confirmed = self.user.confirmedemail_set.first()
834 self.assertEqual(confirmed.photo, self.user.photo_set.first())
836 def test_assign_photo_to_email(self):
837 """
838 Test assigning photo to mail address
839 """
840 self.test_confirm_email()
841 self.test_upload_image()
842 self.assertIsNone(self.user.confirmedemail_set.first().photo)
843 url = reverse(
844 "assign_photo_email", args=[self.user.confirmedemail_set.first().id]
845 )
846 # The get is for the view - test context data
847 self.client.get(
848 url,
849 {
850 "photo_id": self.user.photo_set.first().id,
851 },
852 )
853 # The post is for the actual assigning
854 response = self.client.post(
855 url,
856 {
857 "photo_id": self.user.photo_set.first().id,
858 },
859 follow=True,
860 )
861 self.assertEqual(response.status_code, 200, "cannot assign photo?")
862 self.assertEqual(
863 self.user.confirmedemail_set.first().photo, self.user.photo_set.first()
864 )
866 def test_no_photo_to_email(self):
867 """
868 Test assigning photo to mail address
869 """
870 self.test_confirm_email()
871 url = reverse(
872 "assign_photo_email", args=[self.user.confirmedemail_set.first().id]
873 )
874 response = self.client.post(
875 url,
876 {
877 "photoNone": True,
878 },
879 follow=True,
880 )
881 self.assertEqual(response.status_code, 200, "cannot un-assign photo?")
882 self.assertEqual(self.user.confirmedemail_set.first().photo, None)
884 def test_assign_photo_to_email_wo_photo_for_testing_template(
885 self,
886 ): # pylint: disable=invalid-name
887 """
888 Test assign photo template
889 """
890 self.test_confirm_email()
891 url = reverse(
892 "assign_photo_email", args=[self.user.confirmedemail_set.first().id]
893 )
894 # The get is for the view - test context data
895 response = self.client.get(url)
896 self.assertEqual(response.status_code, 200, "cannot fetch page?")
898 def test_assign_invalid_photo_id_to_email(self): # pylint: disable=invalid-name
899 """
900 Test if assigning an invalid photo id returns the correct error message
901 """
902 self.test_confirm_email()
903 self.test_upload_image()
904 self.assertIsNone(self.user.confirmedemail_set.first().photo)
905 url = reverse(
906 "assign_photo_email", args=[self.user.confirmedemail_set.first().id]
907 )
908 response = self.client.post(
909 url,
910 {
911 "photo_id": 1234,
912 },
913 follow=True,
914 )
915 self.assertEqual(response.status_code, 200, "cannot post assign photo request?")
916 self.assertEqual(
917 str(list(response.context[0]["messages"])[-1]),
918 "Photo does not exist",
919 "Assign non existing photo, does not return error message?",
920 )
922 def test_post_to_assign_photo_without_photo_id(
923 self,
924 ): # pylint: disable=invalid-name
925 """
926 Test if assigning photo without id returns the correct error message
927 """
928 self.test_confirm_email()
929 self.test_upload_image()
930 self.assertIsNone(self.user.confirmedemail_set.first().photo)
931 url = reverse(
932 "assign_photo_email", args=[self.user.confirmedemail_set.first().id]
933 )
934 response = self.client.post(url, {}, follow=True)
935 self.assertEqual(response.status_code, 200, "cannot post assign photo request?")
936 self.assertEqual(
937 str(list(response.context[0]["messages"])[-1]),
938 "Invalid request [photo_id] missing",
939 "Assign non existing photo, does not return error message?",
940 )
942 def test_assign_photo_to_inexisting_mail(self): # pylint: disable=invalid-name
943 """
944 Test if assigning photo to mail address that doesn't exist returns
945 the correct error message
946 """
947 self.test_upload_image()
948 url = reverse("assign_photo_email", args=[1234])
949 response = self.client.post(
950 url,
951 {
952 "photo_id": self.user.photo_set.first().id,
953 },
954 follow=True,
955 )
956 self.assertEqual(response.status_code, 200, "cannot post assign photo request?")
957 self.assertEqual(
958 str(list(response.context[0]["messages"])[-1]),
959 "Invalid request",
960 "Assign non existing photo, does not return error message?",
961 )
963 def test_import_photo_with_inexisting_email(self): # pylint: disable=invalid-name
964 """
965 Test if import with inexisting mail address returns
966 the correct error message
967 """
968 self.login()
969 url = reverse("import_photo", args=[1234])
970 response = self.client.post(url, {}, follow=True)
971 self.assertEqual(response.status_code, 200, "cannot post import photo request?")
972 self.assertEqual(
973 str(list(response.context[0]["messages"])[0]),
974 "Address does not exist",
975 "Import photo with inexisting mail id,\
976 does not return error message?",
977 )
979 def test_import_nothing(self):
980 """
981 Test if importing nothing causes the correct
982 error message to be returned
983 """
984 self.test_confirm_email()
985 url = reverse("import_photo", args=[self.user.confirmedemail_set.first().id])
986 response = self.client.post(url, {}, follow=True)
987 self.assertEqual(response.status_code, 200, "cannot post import photo request?")
988 self.assertEqual(
989 str(list(response.context[0]["messages"])[-1]),
990 "Nothing importable",
991 "Importing with email that does not exist in Gravatar,\
992 should return an error message!",
993 )
995 def test_add_openid(self, confirm=True):
996 """
997 Test if adding an OpenID works
998 """
999 self.login()
1000 # Get page
1001 response = self.client.get(reverse("add_openid"))
1002 self.assertEqual(
1003 response.status_code, 200, "Fetching page to add OpenID fails?"
1004 )
1006 response = self.client.post(
1007 reverse("add_openid"),
1008 {
1009 "openid": self.openid,
1010 },
1011 )
1012 self.assertEqual(response.status_code, 302, "OpenID must redirect")
1014 if confirm:
1015 # Manual confirm, since testing is _really_ hard!
1016 unconfirmed = self.user.unconfirmedopenid_set.first()
1017 confirmed = ConfirmedOpenId()
1018 confirmed.user = unconfirmed.user
1019 confirmed.ip_address = "127.0.0.1"
1020 confirmed.openid = unconfirmed.openid
1021 confirmed.save()
1022 unconfirmed.delete()
1024 def test_add_openid_twice(self):
1025 """
1026 Test if adding OpenID a second time works - it shouldn't
1027 """
1028 self.login()
1029 # Get page
1030 response = self.client.get(reverse("add_openid"))
1031 self.assertEqual(
1032 response.status_code, 200, "Fetching page to add OpenID fails?"
1033 )
1035 response = self.client.post(
1036 reverse("add_openid"),
1037 {
1038 "openid": self.openid,
1039 },
1040 )
1041 self.assertEqual(response.status_code, 302, "OpenID must redirect")
1042 response = self.client.post(
1043 reverse("add_openid"),
1044 {
1045 "openid": self.openid,
1046 },
1047 follow=True,
1048 )
1049 self.assertEqual(
1050 self.user.unconfirmedopenid_set.count(),
1051 1,
1052 "There must only be one unconfirmed ID!",
1053 )
1055 # TODO: This test isn't super criticial, but needs to be fixed
1056 # Currently issues an error with an unbound form
1057 # self.assertFormError(
1058 # response, "form", "openid", "OpenID already added, but not confirmed yet!"
1059 # )
1061 # Manual confirm, since testing is _really_ hard!
1062 unconfirmed = self.user.unconfirmedopenid_set.first()
1063 confirmed = ConfirmedOpenId()
1064 confirmed.user = unconfirmed.user
1065 confirmed.ip_address = "127.0.0.1"
1066 confirmed.openid = unconfirmed.openid
1067 confirmed.save()
1068 unconfirmed.delete()
1070 # Try adding it again - although already confirmed
1071 response = self.client.post(
1072 reverse("add_openid"),
1073 {
1074 "openid": self.openid,
1075 },
1076 follow=True,
1077 )
1078 # TODO: This test isn't super criticial, but needs to be fixed
1079 # Currently issues an error with an unbound form
1080 # self.assertFormError(
1081 # response, "form", "openid", "OpenID already added and confirmed!"
1082 # )
1084 def test_assign_photo_to_openid(self):
1085 """
1086 Test assignment of photo to openid
1087 """
1088 self.test_add_openid()
1089 self.test_upload_image()
1090 self.assertIsNone(self.user.confirmedopenid_set.first().photo)
1091 url = reverse(
1092 "assign_photo_openid", args=[self.user.confirmedopenid_set.first().id]
1093 )
1094 # The get is for the view - test context data
1095 self.client.get(
1096 url,
1097 {
1098 "photo_id": self.user.photo_set.first().id,
1099 },
1100 )
1101 # The post is for the actual assigning
1102 response = self.client.post(
1103 url,
1104 {
1105 "photo_id": self.user.photo_set.first().id,
1106 },
1107 follow=True,
1108 )
1109 self.assertEqual(response.status_code, 200, "cannot assign photo?")
1110 self.assertEqual(
1111 self.user.confirmedopenid_set.first().photo, self.user.photo_set.first()
1112 )
1114 def test_assign_photo_to_openid_wo_photo_for_testing_template(
1115 self,
1116 ): # pylint: disable=invalid-name
1117 """
1118 Test openid/photo assignment template
1119 """
1120 self.test_add_openid()
1121 url = reverse(
1122 "assign_photo_openid", args=[self.user.confirmedopenid_set.first().id]
1123 )
1124 response = self.client.get(url)
1125 self.assertEqual(response.status_code, 200, "cannot fetch page?")
1127 def test_assign_invalid_photo_id_to_openid(self): # pylint: disable=invalid-name
1128 """
1129 Test assigning invalid photo to openid returns
1130 the correct error message
1131 """
1132 self.test_add_openid()
1133 self.assertIsNone(self.user.confirmedopenid_set.first().photo)
1134 url = reverse(
1135 "assign_photo_openid", args=[self.user.confirmedopenid_set.first().id]
1136 )
1137 response = self.client.post(
1138 url,
1139 {
1140 "photo_id": 1234,
1141 },
1142 follow=True,
1143 )
1144 self.assertEqual(response.status_code, 200, "cannot post assign photo request?")
1145 self.assertEqual(
1146 str(list(response.context[0]["messages"])[-1]),
1147 "Photo does not exist",
1148 "Assign non existing photo, does not return error message?",
1149 )
1151 def test_post_to_assign_photo_openid_without_photo_id(
1152 self,
1153 ): # pylint: disable=invalid-name
1154 """
1155 Test POST assign photo to openid without photo id
1156 returns the correct error message
1157 """
1158 self.test_add_openid()
1159 self.test_upload_image()
1160 self.assertIsNone(self.user.confirmedopenid_set.first().photo)
1161 url = reverse(
1162 "assign_photo_openid", args=[self.user.confirmedopenid_set.first().id]
1163 )
1164 response = self.client.post(url, {}, follow=True)
1165 self.assertEqual(response.status_code, 200, "cannot post assign photo request?")
1166 self.assertEqual(
1167 str(list(response.context[0]["messages"])[-1]),
1168 "Invalid request [photo_id] missing",
1169 "Assign non existing photo, does not return error message?",
1170 )
1172 def test_assign_photo_to_openid_inexisting_openid(
1173 self,
1174 ): # pylint: disable=invalid-name
1175 """
1176 Test assigning photo to openid that doesn't exist
1177 returns the correct error message.
1178 """
1179 self.test_upload_image()
1180 url = reverse("assign_photo_openid", args=[1234])
1181 response = self.client.post(
1182 url,
1183 {
1184 "photo_id": self.user.photo_set.first().id,
1185 },
1186 follow=True,
1187 )
1188 self.assertEqual(response.status_code, 200, "cannot post assign photo request?")
1189 self.assertEqual(
1190 str(list(response.context[0]["messages"])[-1]),
1191 "Invalid request",
1192 "Assign non existing photo, does not return error message?",
1193 )
1195 def test_remove_confirmed_openid(self): # pylint: disable=invalid-name
1196 """
1197 Remove confirmed openid
1198 """
1199 self.test_add_openid()
1200 url = reverse(
1201 "remove_confirmed_openid", args=[self.user.confirmedopenid_set.first().id]
1202 )
1203 response = self.client.post(url, follow=True)
1204 self.assertEqual(
1205 response.status_code, 200, "unable to remove confirmed openid?"
1206 )
1207 self.assertEqual(
1208 str(list(response.context[0]["messages"])[-1]),
1209 "ID removed",
1210 "Removing confirmed openid does not work?",
1211 )
1213 def test_remove_not_existing_confirmed_openid(self): # pylint: disable=invalid-name
1214 """
1215 Try removing confirmed openid that doesn't exist
1216 """
1217 self.login()
1218 url = reverse("remove_confirmed_openid", args=[1234])
1219 response = self.client.post(url, follow=True)
1220 self.assertEqual(
1221 response.status_code, 200, "removing id does not redirect to profile?"
1222 )
1223 self.assertEqual(
1224 str(list(response.context[0]["messages"])[0]),
1225 "ID does not exist",
1226 "Removing not existing (confirmed) address, should produce an\
1227 error message!",
1228 )
1230 def test_remove_unconfirmed_openid(self):
1231 """
1232 Remove unconfirmed openid
1233 """
1234 self.test_add_openid(confirm=False)
1235 url = reverse(
1236 "remove_unconfirmed_openid",
1237 args=[self.user.unconfirmedopenid_set.first().id],
1238 )
1239 response = self.client.post(url, follow=True)
1240 self.assertEqual(
1241 response.status_code, 200, "unable to remove unconfirmed address?"
1242 )
1243 self.assertEqual(
1244 str(list(response.context[0]["messages"])[-1]),
1245 "ID removed",
1246 "Removing unconfirmed mail does not work?",
1247 )
1249 def test_remove_unconfirmed_inexisting_openid(self): # pylint: disable=invalid-name
1250 """
1251 Remove unconfirmed openid that doesn't exist
1252 """
1253 self.login()
1254 url = reverse("remove_unconfirmed_openid", args=[1234])
1255 response = self.client.post(url, follow=True)
1256 self.assertEqual(
1257 response.status_code, 200, "unable to remove unconfirmed address?"
1258 )
1259 self.assertEqual(
1260 str(list(response.context[0]["messages"])[0]),
1261 "ID does not exist",
1262 "Removing an inexisting openid should return an error message",
1263 )
1265 def test_openid_redirect_view(self):
1266 """
1267 Test redirect view
1268 """
1269 self.test_add_openid(confirm=False)
1270 url = reverse(
1271 "openid_redirection", args=[self.user.unconfirmedopenid_set.first().id]
1272 )
1273 response = self.client.get(url, follow=True)
1274 self.assertEqual(
1275 response.status_code, 200, "unable to remove unconfirmed address?"
1276 )
1277 # self.assertContains(
1278 # response,
1279 # 'OpenID discovery failed: ', 1, 200,
1280 # 'This request must return an error in test mode'
1281 # )
1283 def test_set_photo_on_openid(self):
1284 """
1285 Test the set_photo function on our ConfirmedOpenId model.
1286 """
1287 self.test_add_openid()
1288 self.test_upload_image()
1289 self.assertIsNone(self.user.confirmedopenid_set.first().photo)
1290 self.user.confirmedopenid_set.first().set_photo(self.user.photo_set.first())
1291 self.assertEqual(
1292 self.user.confirmedopenid_set.first().photo,
1293 self.user.photo_set.first(),
1294 "set_photo did not work!?",
1295 )
1297 def test_avatar_url_mail(self, do_upload_and_confirm=True, size=(80, 80)):
1298 """
1299 Test fetching avatar via mail
1300 """
1301 if do_upload_and_confirm:
1302 self.test_upload_image()
1303 self.test_confirm_email()
1304 urlobj = urlsplit(
1305 libravatar_url(
1306 email=self.user.confirmedemail_set.first().email,
1307 size=size[0],
1308 )
1309 )
1310 url = "%s?%s" % (urlobj.path, urlobj.query)
1311 response = self.client.get(url, follow=True)
1312 self.assertEqual(response.status_code, 200, "unable to fetch avatar?")
1313 photodata = Image.open(BytesIO(response.content))
1314 self.assertEqual(photodata.size, size, "Why is this not the correct size?")
1316 def test_avatar_url_openid(self):
1317 """
1318 Test fetching avatar via openid
1319 """
1320 self.test_assign_photo_to_openid()
1321 urlobj = urlsplit(
1322 libravatar_url(
1323 openid=self.user.confirmedopenid_set.first().openid,
1324 size=80,
1325 )
1326 )
1327 url = "%s?%s" % (urlobj.path, urlobj.query)
1328 response = self.client.get(url, follow=True)
1329 self.assertEqual(response.status_code, 200, "unable to fetch avatar?")
1330 photodata = Image.open(BytesIO(response.content))
1331 self.assertEqual(photodata.size, (80, 80), "Why is this not the correct size?")
1333 def test_avatar_url_inexisting_mail_digest(self): # pylint: disable=invalid-name
1334 """
1335 Test fetching avatar via inexisting mail digest
1336 """
1337 self.test_upload_image()
1338 self.test_confirm_email()
1339 urlobj = urlsplit(
1340 libravatar_url(
1341 email=self.user.confirmedemail_set.first().email,
1342 size=80,
1343 )
1344 )
1345 # Simply delete it, then it's digest is 'correct', but
1346 # the hash is no longer there
1347 addr = self.user.confirmedemail_set.first().email
1348 digest = hashlib.md5(addr.strip().lower().encode("utf-8")).hexdigest()
1350 self.user.confirmedemail_set.first().delete()
1351 url = "%s?%s" % (urlobj.path, urlobj.query)
1352 response = self.client.get(url, follow=True)
1353 self.assertEqual(
1354 response.redirect_chain[0][0],
1355 "/gravatarproxy/%s?s=80" % digest,
1356 "Doesn't redirect to Gravatar?",
1357 )
1358 self.assertEqual(
1359 response.redirect_chain[0][1], 302, "Doesn't redirect with 302?"
1360 )
1361 self.assertEqual(
1362 response.redirect_chain[1][0],
1363 "/avatar/%s?s=80&forcedefault=y" % digest,
1364 "Doesn't redirect with default forced on?",
1365 )
1366 self.assertEqual(
1367 response.redirect_chain[1][1], 302, "Doesn't redirect with 302?"
1368 )
1369 self.assertEqual(
1370 response.redirect_chain[2][0],
1371 "/static/img/nobody/80.png",
1372 "Doesn't redirect to static?",
1373 )
1374 # self.assertRedirects(
1375 # response=response,
1376 # expected_url="/static/img/nobody/80.png",
1377 # msg_prefix="Why does this not redirect to Gravatar?",
1378 # )
1379 # Eventually one should check if the data is the same
1381 def test_avatar_url_inexisting_mail_digest_gravatarproxy_disabled(
1382 self,
1383 ): # pylint: disable=invalid-name
1384 """
1385 Test fetching avatar via inexisting mail digest
1386 """
1387 self.test_upload_image()
1388 self.test_confirm_email()
1389 urlobj = urlsplit(
1390 libravatar_url(
1391 email=self.user.confirmedemail_set.first().email,
1392 size=80,
1393 )
1394 )
1395 # Simply delete it, then it digest is 'correct', but
1396 # the hash is no longer there
1397 self.user.confirmedemail_set.first().delete()
1398 url = "%s?%s&gravatarproxy=n" % (urlobj.path, urlobj.query)
1399 response = self.client.get(url, follow=True)
1400 self.assertEqual(
1401 response.redirect_chain[0][0],
1402 "/static/img/nobody/80.png",
1403 "Doesn't redirect to static?",
1404 )
1406 # self.assertRedirects(
1407 # response=response,
1408 # expected_url="/static/img/nobody/80.png",
1409 # msg_prefix="Why does this not redirect to the default img?",
1410 # )
1411 # Eventually one should check if the data is the same
1413 def test_avatar_url_inexisting_mail_digest_w_default_mm(
1414 self,
1415 ): # pylint: disable=invalid-name
1416 """
1417 Test fetching avatar via inexisting mail digest and default 'mm'
1418 """
1419 urlobj = urlsplit(
1420 libravatar_url(
1421 email="asdf@company.local",
1422 size=80,
1423 default="mm",
1424 )
1425 )
1426 url = "%s?%s" % (urlobj.path, urlobj.query)
1427 self.client.get(url, follow=False)
1429 def test_avatar_url_inexisting_mail_digest_w_default_mm_gravatarproxy_disabled(
1430 self,
1431 ): # pylint: disable=invalid-name
1432 """
1433 Test fetching avatar via inexisting mail digest and default 'mm'
1434 """
1435 urlobj = urlsplit(
1436 libravatar_url(
1437 email="asdf@company.local",
1438 size=80,
1439 default="mm",
1440 )
1441 )
1442 url = "%s?%s&gravatarproxy=n" % (urlobj.path, urlobj.query)
1443 response = self.client.get(url, follow=True)
1444 self.assertEqual(
1445 response.redirect_chain[0][0],
1446 "/static/img/mm/80.png",
1447 "Doesn't redirect to static?",
1448 )
1450 # self.assertRedirects(
1451 # response=response,
1452 # expected_url="/static/img/mm/80.png",
1453 # msg_prefix="Why does this not redirect to the default img?",
1454 # )
1455 # Eventually one should check if the data is the same
1457 def test_avatar_url_inexisting_mail_digest_wo_default(
1458 self,
1459 ): # pylint: disable=invalid-name
1460 """
1461 Test fetching avatar via inexisting mail digest and default 'mm'
1462 """
1463 urlobj = urlsplit(
1464 libravatar_url(
1465 email="asdf@company.local",
1466 size=80,
1467 )
1468 )
1469 digest = hashlib.md5("asdf@company.local".lower().encode("utf-8")).hexdigest()
1470 url = "%s?%s" % (urlobj.path, urlobj.query)
1471 response = self.client.get(url, follow=True)
1472 self.assertEqual(
1473 response.redirect_chain[0][0],
1474 "/gravatarproxy/%s?s=80" % digest,
1475 "Doesn't redirect to Gravatar?",
1476 )
1477 self.assertEqual(
1478 response.redirect_chain[0][1], 302, "Doesn't redirect with 302?"
1479 )
1480 self.assertEqual(
1481 response.redirect_chain[1][0],
1482 "/avatar/%s?s=80&forcedefault=y" % digest,
1483 "Doesn't redirect with default forced on?",
1484 )
1485 self.assertEqual(
1486 response.redirect_chain[1][1], 302, "Doesn't redirect with 302?"
1487 )
1488 self.assertEqual(
1489 response.redirect_chain[2][0],
1490 "/static/img/nobody/80.png",
1491 "Doesn't redirect to static?",
1492 )
1494 # self.assertRedirects(
1495 # response=response,
1496 # expected_url="/static/img/nobody/80.png",
1497 # msg_prefix="Why does this not redirect to the default img?",
1498 # )
1499 # Eventually one should check if the data is the same
1501 def test_avatar_url_inexisting_mail_digest_wo_default_gravatarproxy_disabled(
1502 self,
1503 ): # pylint: disable=invalid-name
1504 """
1505 Test fetching avatar via inexisting mail digest and default 'mm'
1506 """
1507 urlobj = urlsplit(
1508 libravatar_url(
1509 email="asdf@company.local",
1510 size=80,
1511 )
1512 )
1513 url = "%s?%s&gravatarproxy=n" % (urlobj.path, urlobj.query)
1514 response = self.client.get(url, follow=True)
1515 self.assertEqual(
1516 response.redirect_chain[0][0],
1517 "/static/img/nobody/80.png",
1518 "Doesn't redirect to static?",
1519 )
1521 # self.assertRedirects(
1522 # response=response,
1523 # expected_url="/static/img/nobody/80.png",
1524 # msg_prefix="Why does this not redirect to the default img?",
1525 # )
1526 # Eventually one should check if the data is the same
1528 def test_avatar_url_default(self): # pylint: disable=invalid-name
1529 """
1530 Test fetching avatar for not existing mail with default specified
1531 """
1532 # TODO - Find a new way
1533 # Do not run this test, since static serving isn't allowed in testing mode
1534 return
1535 urlobj = urlsplit(
1536 libravatar_url(
1537 "xxx@xxx.xxx",
1538 size=80,
1539 default="/static/img/nobody.png",
1540 )
1541 )
1542 url = "%s?%s" % (urlobj.path, urlobj.query)
1543 response = self.client.get(url, follow=False)
1544 self.assertRedirects(
1545 response=response,
1546 expected_url="/static/img/nobody.png",
1547 msg_prefix="Why does this not redirect to nobody img?",
1548 )
1550 def test_avatar_url_default_gravatarproxy_disabled(
1551 self,
1552 ): # pylint: disable=invalid-name
1553 """
1554 Test fetching avatar for not existing mail with default specified
1555 """
1556 # TODO - Find a new way
1557 # Do not run this test, since static serving isn't allowed in testing mode
1558 return
1559 urlobj = urlsplit(
1560 libravatar_url(
1561 "xxx@xxx.xxx",
1562 size=80,
1563 default="/static/img/nobody.png",
1564 )
1565 )
1566 url = "%s?%s&gravatarproxy=n" % (urlobj.path, urlobj.query)
1567 response = self.client.get(url, follow=True)
1568 self.assertRedirects(
1569 response=response,
1570 expected_url="/static/img/nobody.png",
1571 msg_prefix="Why does this not redirect to the default img?",
1572 )
1574 def test_avatar_url_default_external(self): # pylint: disable=invalid-name
1575 """
1576 Test fetching avatar for not existing mail with external default specified
1577 This shall *not* redirect to the external site (CWE-601)!
1578 """
1579 default = "http://host.tld/img.png"
1580 size = 80
1581 urlobj = urlsplit(
1582 libravatar_url(
1583 "xxx@xxx.xxx",
1584 size=size,
1585 default=default,
1586 )
1587 )
1588 url = "%s?%s" % (urlobj.path, urlobj.query)
1589 response = self.client.get(url, follow=False)
1590 self.assertRedirects(
1591 response=response,
1592 expected_url="/gravatarproxy/fb7a6d7f11365642d44ba66dc57df56f?s=%s" % size,
1593 fetch_redirect_response=False,
1594 msg_prefix="Why does this not redirect to the default img?",
1595 )
1597 def test_avatar_url_default_external_trusted(self): # pylint: disable=invalid-name
1598 """
1599 Test fetching avatar for not existing mail with external default specified
1600 """
1601 default = "https://ui-avatars.com/api/blah"
1602 urlobj = urlsplit(
1603 libravatar_url(
1604 "xxx@xxx.xxx",
1605 size=80,
1606 default=default,
1607 )
1608 )
1609 url = "%s?%s" % (urlobj.path, urlobj.query)
1610 response = self.client.get(url, follow=False)
1611 self.assertRedirects(
1612 response=response,
1613 expected_url="/gravatarproxy/fb7a6d7f11365642d44ba66dc57df56f?s=80&default=https://ui-avatars.com/api/blah",
1614 fetch_redirect_response=False,
1615 msg_prefix="Why does this not redirect to the default img?",
1616 )
1618 def test_avatar_url_default_external_gravatarproxy_disabled(
1619 self,
1620 ): # pylint: disable=invalid-name
1621 """
1622 Test fetching avatar for not existing mail with external default specified
1623 This shall *not* redirect to the external site (CWE-601)!
1624 """
1625 default = "http://host.tld/img.png"
1626 urlobj = urlsplit(
1627 libravatar_url(
1628 "xxx@xxx.xxx",
1629 size=80,
1630 default=default,
1631 )
1632 )
1633 url = "%s?%s&gravatarproxy=n" % (urlobj.path, urlobj.query)
1634 response = self.client.get(url, follow=False)
1635 self.assertRedirects(
1636 response=response,
1637 expected_url="/static/img/nobody/80.png",
1638 fetch_redirect_response=False,
1639 msg_prefix="Why does this not redirect to the default img?",
1640 )
1642 def test_crop_photo(self):
1643 """
1644 Test cropping photo
1645 """
1646 self.test_upload_image()
1647 self.test_confirm_email()
1648 url = reverse("crop_photo", args=[self.user.photo_set.first().pk])
1649 response = self.client.post(
1650 url,
1651 {
1652 "x": 10,
1653 "y": 10,
1654 "w": 20,
1655 "h": 20,
1656 },
1657 follow=True,
1658 )
1659 self.assertEqual(response.status_code, 200, "unable to crop?")
1660 self.test_avatar_url_mail(do_upload_and_confirm=False, size=(20, 20))
1661 img = Image.open(BytesIO(self.user.photo_set.first().data))
1662 self.assertEqual(
1663 img.size, (20, 20), "cropped to 20x20, but resulting image isn't 20x20!?"
1664 )
1666 def test_password_change_view(self):
1667 """
1668 Test password change view
1669 """
1670 self.login()
1671 url = reverse("password_change")
1672 response = self.client.get(url)
1673 self.assertEqual(
1674 response.status_code, 200, "unable to view password change view?"
1675 )
1677 def test_password_change_view_post_wrong_old_pw(self):
1678 """
1679 Test password change view post
1680 """
1681 self.login()
1682 response = self.client.post(
1683 reverse("password_change"),
1684 {
1685 "old_password": "xxx",
1686 "new_password1": self.password,
1687 "new_password2": self.password,
1688 },
1689 follow=True,
1690 )
1692 self.assertContains(
1693 response,
1694 "Your old password was entered incorrectly. Please enter it again.",
1695 1,
1696 200,
1697 "Old password as entered incorrectly, site should raise an error",
1698 )
1700 def test_password_change_view_post_wrong_new_password1(self):
1701 """
1702 Test password change view post
1703 """
1704 self.login()
1705 response = self.client.post(
1706 reverse("password_change"),
1707 {
1708 "old_password": self.password,
1709 "new_password1": self.password + ".",
1710 "new_password2": self.password,
1711 },
1712 follow=True,
1713 )
1714 self.assertContains(
1715 response,
1716 "The two password fields didn",
1717 1,
1718 200,
1719 "Old password was entered incorrectly, site should raise an error",
1720 )
1722 def test_password_change_view_post_wrong_new_password2(self):
1723 """
1724 Test password change view post
1725 """
1726 self.login()
1727 response = self.client.post(
1728 reverse("password_change"),
1729 {
1730 "old_password": self.password,
1731 "new_password1": self.password,
1732 "new_password2": self.password + ".",
1733 },
1734 follow=True,
1735 )
1737 self.assertContains(
1738 response,
1739 "The two password fields didn",
1740 1,
1741 200,
1742 "Old password as entered incorrectly, site should raise an error",
1743 )
1745 def test_password_change_view_post_common_password(self):
1746 """
1747 Test password change view post
1748 """
1749 self.login()
1750 response = self.client.post(
1751 reverse("password_change"),
1752 {
1753 "old_password": self.password,
1754 "new_password1": "Hallo",
1755 "new_password2": "Hallo",
1756 },
1757 follow=True,
1758 )
1760 self.assertContains(
1761 response,
1762 "This password is too common.",
1763 1,
1764 200,
1765 "Common password, site should raise an error",
1766 )
1768 def test_profile_must_list_first_and_lastname(self):
1769 """
1770 Test if profile view correctly lists first -/last name
1771 """
1772 self.login()
1773 response = self.client.get(reverse("profile"))
1774 self.assertContains(
1775 response,
1776 self.first_name,
1777 1,
1778 200,
1779 "First name not listed in profile page",
1780 )
1781 self.assertContains(
1782 response,
1783 self.last_name,
1784 1,
1785 200,
1786 "Last name not listed in profile page",
1787 )
1788 self.assertContains(
1789 response,
1790 self.first_name + " " + self.last_name,
1791 1,
1792 200,
1793 "First and last name not correctly listed in profile page",
1794 )
1796 def test_password_reset_page(self):
1797 """
1798 Just test if the password reset page come up correctly
1799 """
1800 response = self.client.get(reverse("password_reset"))
1801 self.assertEqual(response.status_code, 200, "no 200 ok?")
1803 def test_password_reset_wo_mail(self):
1804 """
1805 Test if the password reset doesn't error out
1806 if the mail address doesn't exist
1807 """
1808 # Avoid sending out mails
1809 settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
1811 # Empty database / eliminate existing users
1812 User.objects.all().delete()
1813 url = reverse("password_reset")
1814 response = self.client.post(
1815 url,
1816 {
1817 "email": "asdf@asdf.local",
1818 },
1819 follow=True,
1820 )
1821 self.assertEqual(response.status_code, 200, "password reset page not working?")
1822 self.assertEqual(
1823 len(mail.outbox),
1824 0,
1825 "user does not exist, there should be no mail in the outbox!",
1826 )
1828 def test_password_reset_w_mail(self):
1829 """
1830 Test if the password reset works correctly with email in
1831 User object
1832 """
1833 # Avoid sending out mails
1834 settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
1836 url = reverse("password_reset")
1837 # Our test user doesn't have an email address by default - but we need one set
1838 self.user.email = "asdf@asdf.local"
1839 self.user.save()
1840 response = self.client.post(
1841 url,
1842 {
1843 "email": self.user.email,
1844 },
1845 follow=True,
1846 )
1847 self.assertEqual(response.status_code, 200, "password reset page not working?")
1848 self.assertEqual(
1849 len(mail.outbox), 1, "User exists, there should be a mail in the outbox!"
1850 )
1851 self.assertEqual(
1852 mail.outbox[0].to[0],
1853 self.user.email,
1854 "Sending mails to the wrong \
1855 mail address?",
1856 )
1858 def test_password_reset_w_confirmed_mail(self):
1859 """
1860 Test if the password reset works correctly with confirmed
1861 mail
1862 """
1863 # Avoid sending out mails
1864 settings.EMAIL_BACKEND = "django.core.mail.backends.locmem.EmailBackend"
1866 url = reverse("password_reset")
1867 # Our test user doesn't have a confirmed mail identity - add one
1868 self.user.confirmedemail_set.create(email="asdf@asdf.local")
1869 self.user.save()
1871 response = self.client.post(
1872 url,
1873 {
1874 "email": self.user.confirmedemail_set.first().email,
1875 },
1876 follow=True,
1877 )
1878 # Since the object is touched in another process, we need to refresh it
1879 self.user.refresh_from_db()
1880 self.assertEqual(response.status_code, 200, "password reset page not working?")
1881 self.assertEqual(
1882 self.user.email,
1883 self.user.confirmedemail_set.first().email,
1884 "The password reset view, should have corrected this!",
1885 )
1886 self.assertEqual(
1887 len(mail.outbox), 1, "user exists, there should be a mail in the outbox!"
1888 )
1889 self.assertEqual(
1890 mail.outbox[0].to[0],
1891 self.user.email,
1892 "why are we sending mails to the wrong mail address?",
1893 )
1895 def test_export(self):
1896 """
1897 Test if export works
1898 """
1900 # Create well known strings to check if export
1901 # works as expected
1902 self.user.confirmedemail_set.create(email="asdf@asdf.local")
1903 self.user.confirmedopenid_set.create(openid="http://asdf.asdf.local")
1904 self.user.save()
1906 # Ensure we have a photo uploaded
1907 self.test_upload_image()
1909 self.login()
1910 self.client.get(reverse("export"))
1911 response = self.client.post(
1912 reverse("export"),
1913 {},
1914 follow=False,
1915 )
1916 self.assertIsInstance(response.content, bytes)
1917 fh = gzip.open(BytesIO(response.content), "rb")
1918 content = fh.read()
1919 fh.close()
1920 root = xml.etree.ElementTree.fromstring(content)
1921 self.assertEqual(root.tag, "{%s}user" % settings.SCHEMAROOT)
1922 self.assertEqual(
1923 root.findall("{%s}account" % settings.SCHEMAROOT)[0].items()[0][1],
1924 self.user.username,
1925 )
1926 self.assertEqual(
1927 root.findall("{%s}account" % settings.SCHEMAROOT)[0].items()[1][1],
1928 self.user.password,
1929 )
1931 self.assertEqual(
1932 root.findall("{%s}emails" % settings.SCHEMAROOT)[0][0].text,
1933 self.user.confirmedemail_set.first().email,
1934 )
1935 self.assertEqual(
1936 root.findall("{%s}openids" % settings.SCHEMAROOT)[0][0].text,
1937 self.user.confirmedopenid_set.first().openid,
1938 )
1940 data = root.findall("{%s}photos" % settings.SCHEMAROOT)[0][0].text
1942 data = data.strip("'")
1943 data = data.strip("\\n")
1944 data = data.lstrip("b'")
1945 bindata = base64.decodebytes(bytes(data, "utf-8"))
1946 image = Image.open(BytesIO(bindata))
1947 self.assertTrue(hasattr(image, "png"))
1949 def test_upload_export(self):
1950 """
1951 Test if uploading export works
1952 """
1954 # Ensure we have data in place
1955 self.test_export()
1957 self.login()
1958 self.client.get(reverse("export"))
1959 response = self.client.post(
1960 reverse("export"),
1961 {},
1962 follow=False,
1963 )
1964 self.assertIsInstance(response.content, bytes)
1966 fh_gzip = gzip.open(BytesIO(response.content), "rb")
1967 fh = BytesIO(response.content)
1969 response = self.client.post(
1970 reverse("upload_export"),
1971 data={"not_porn": "on", "can_distribute": "on", "export_file": fh_gzip},
1972 follow=True,
1973 )
1974 fh_gzip.close()
1975 self.assertEqual(response.status_code, 200, "Upload worked")
1976 self.assertContains(
1977 response,
1978 "Unable to parse file: Not a gzipped file",
1979 1,
1980 200,
1981 "Upload didn't work?",
1982 )
1984 # Second test - correctly gzipped content
1985 response = self.client.post(
1986 reverse("upload_export"),
1987 data={"not_porn": "on", "can_distribute": "on", "export_file": fh},
1988 follow=True,
1989 )
1990 fh.close()
1992 self.assertEqual(response.status_code, 200, "Upload worked")
1993 self.assertContains(
1994 response,
1995 "Choose items to be imported",
1996 1,
1997 200,
1998 "Upload didn't work?",
1999 )
2000 self.assertContains(
2001 response,
2002 "asdf@asdf.local",
2003 2,
2004 200,
2005 "Upload didn't work?",
2006 )
2008 def test_prefs_page(self):
2009 """
2010 Test if preferences page works
2011 """
2013 self.login()
2014 self.client.get(reverse("user_preference"))
2016 def test_delete_user(self):
2017 """
2018 Test if deleting user profile works
2019 """
2021 self.login()
2022 self.client.get(reverse("delete"))
2023 response = self.client.post(
2024 reverse("delete"),
2025 data={"password": self.password},
2026 follow=True,
2027 )
2028 self.assertEqual(response.status_code, 200, "Deletion worked")
2029 self.assertEqual(User.objects.count(), 0, "No user there any more")
2031 def test_confirm_already_confirmed(self):
2032 """
2033 Try to confirm a mail address that has been confirmed (by another user)
2034 """
2036 # Add mail address (stays unconfirmed)
2037 self.test_add_email()
2039 # Create a second user that will conflict
2040 user2 = User.objects.create_user(
2041 username=self.username + "1",
2042 password=self.password,
2043 first_name=self.first_name,
2044 last_name=self.last_name,
2045 )
2046 ConfirmedEmail.objects.create(
2047 email=self.email,
2048 user=user2,
2049 )
2051 # Just to be sure
2052 self.assertEqual(
2053 self.user.unconfirmedemail_set.first().email,
2054 user2.confirmedemail_set.first().email,
2055 "Mail not the same?",
2056 )
2058 # This needs to be cought
2059 try:
2060 self.test_confirm_email()
2061 except AssertionError:
2062 pass
2064 # Request a random page, so we can access the messages
2065 response = self.client.get(reverse("profile"))
2066 self.assertEqual(
2067 str(list(response.context[0]["messages"])[0]),
2068 "This mail address has been taken already and cannot be confirmed",
2069 "This should return an error message!",
2070 )