Coverage for ivatar/ivataraccount/test_views.py: 100%

636 statements  

« prev     ^ index     » next       coverage.py v7.4.4, created at 2024-04-22 23:10 +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 

21 

22from libravatar import libravatar_url 

23 

24from PIL import Image 

25 

26os.environ["DJANGO_SETTINGS_MODULE"] = "ivatar.settings" 

27django.setup() 

28 

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 

34 

35# pylint: enable=wrong-import-position 

36 

37TEST_IMAGE_FILE = os.path.join(settings.STATIC_ROOT, "img", "deadbeef.png") 

38 

39 

40class Tester(TestCase): # pylint: disable=too-many-public-methods 

41 """ 

42 Main test class 

43 """ 

44 

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() 

54 

55 def login(self): 

56 """ 

57 Login as user 

58 """ 

59 self.client.login(username=self.username, password=self.password) 

60 

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 ) 

72 

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) 

93 

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 ) 

120 

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 ) 

137 

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 ) 

144 

145 self.assertIsNotNone( 

146 authenticate( 

147 username=self.username, 

148 password=self.password, 

149 ), 

150 "cannot authenticate with new password!?", 

151 ) 

152 

153 self.login() 

154 response = self.client.get(reverse("profile")) 

155 self.assertEqual(response.context[0]["user"].is_anonymous, False) 

156 

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 ) 

184 

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?") 

204 

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 ) 

215 

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 ) 

242 

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 ) 

269 

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 ) 

299 

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 ) 

316 

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 ) 

344 

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 

362 

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 ) 

382 

383 def test_raw_image(self): 

384 """ 

385 test raw image view (as seen in profile <img src= 

386 """ 

387 

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!?") 

396 

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 ) 

403 

404 def test_delete_photo(self): 

405 """ 

406 test deleting the photo 

407 """ 

408 

409 # Ensure we have a photo 

410 self.test_gravatar_photo_import() 

411 

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 ) 

420 

421 def test_delete_inexisting_photo(self): 

422 """ 

423 test deleting the photo 

424 """ 

425 

426 # Ensure we have a photo 

427 self.test_gravatar_photo_import() 

428 

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 ) 

437 

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" 

446 

447 max_num_unconfirmed = getattr( 

448 settings, "MAX_NUM_UNCONFIRMED_EMAILS", MAX_NUM_UNCONFIRMED_EMAILS_DEFAULT 

449 ) 

450 

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 # ) 

464 

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" 

473 

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 # ) 

487 

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() 

495 

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 # ) 

508 

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() 

516 

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() 

523 

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 # ) 

536 

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 ) 

555 

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 

591 

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 ) 

609 

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 ) 

631 

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 ) 

653 

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 ) 

676 

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?") 

717 

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?") 

758 

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?") 

799 

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 ) 

824 

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()) 

835 

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 ) 

865 

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) 

883 

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?") 

897 

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 ) 

921 

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 ) 

941 

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 ) 

962 

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 ) 

978 

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 ) 

994 

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 ) 

1005 

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") 

1013 

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() 

1023 

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 ) 

1034 

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 ) 

1054 

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 # ) 

1060 

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() 

1069 

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 # ) 

1083 

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 ) 

1113 

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?") 

1126 

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 ) 

1150 

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 ) 

1171 

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 ) 

1194 

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 ) 

1212 

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 ) 

1229 

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 ) 

1248 

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 ) 

1264 

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 # ) 

1282 

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 ) 

1296 

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?") 

1315 

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?") 

1332 

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() 

1349 

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 

1380 

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 ) 

1405 

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 

1412 

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) 

1428 

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 ) 

1449 

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 

1456 

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 ) 

1493 

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 

1500 

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 ) 

1520 

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 

1527 

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 ) 

1549 

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 ) 

1573 

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 ) 

1596 

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 ) 

1617 

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 ) 

1641 

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 ) 

1665 

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 ) 

1676 

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 ) 

1691 

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 ) 

1699 

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 ) 

1721 

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 ) 

1736 

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 ) 

1744 

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 ) 

1759 

1760 self.assertContains( 

1761 response, 

1762 "This password is too common.", 

1763 1, 

1764 200, 

1765 "Common password, site should raise an error", 

1766 ) 

1767 

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 ) 

1795 

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?") 

1802 

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" 

1810 

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 ) 

1827 

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" 

1835 

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 ) 

1857 

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" 

1865 

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() 

1870 

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 ) 

1894 

1895 def test_export(self): 

1896 """ 

1897 Test if export works 

1898 """ 

1899 

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() 

1905 

1906 # Ensure we have a photo uploaded 

1907 self.test_upload_image() 

1908 

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 ) 

1930 

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 ) 

1939 

1940 data = root.findall("{%s}photos" % settings.SCHEMAROOT)[0][0].text 

1941 

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")) 

1948 

1949 def test_upload_export(self): 

1950 """ 

1951 Test if uploading export works 

1952 """ 

1953 

1954 # Ensure we have data in place 

1955 self.test_export() 

1956 

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) 

1965 

1966 fh_gzip = gzip.open(BytesIO(response.content), "rb") 

1967 fh = BytesIO(response.content) 

1968 

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 ) 

1983 

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() 

1991 

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 ) 

2007 

2008 def test_prefs_page(self): 

2009 """ 

2010 Test if preferences page works 

2011 """ 

2012 

2013 self.login() 

2014 self.client.get(reverse("user_preference")) 

2015 

2016 def test_delete_user(self): 

2017 """ 

2018 Test if deleting user profile works 

2019 """ 

2020 

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") 

2030 

2031 def test_confirm_already_confirmed(self): 

2032 """ 

2033 Try to confirm a mail address that has been confirmed (by another user) 

2034 """ 

2035 

2036 # Add mail address (stays unconfirmed) 

2037 self.test_add_email() 

2038 

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 ) 

2050 

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 ) 

2057 

2058 # This needs to be cought 

2059 try: 

2060 self.test_confirm_email() 

2061 except AssertionError: 

2062 pass 

2063 

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 )