gestioCOF/gestioncof/tests/test_models.py
Aurélien Delobelle 05eeb6a25c core -- Install django-allauth-ens
Refer to allauth doc for an accurate features list:
  http://django-allauth.readthedocs.io/en/latest/

Users can now change their password, ask for a password reset, or set
one if they don't have one.

In particular, it allows users whose account has been created via a
clipper authentication to configure a password before losing their
clipper. Even if they have already lost it, they are able to get one
using the "Reset password" functionality.

Allauth multiple emails management is deactivated. Requests to the
related url redirect to the home page.

All the login and logout views are replaced by the allauth' ones. It
also concerns the Django and Wagtail admin sites.

Note that users are no longer logged out of the clipper CAS server when
they authenticated via this server. Instead a message suggests the user
to disconnect.

Clipper connections and `login_clipper`
---------------------------------------

- Non-empty `login_clipper` are now unique among `CofProfile` instances.
- They are created once for users with a non-empty 'login_clipper' (with
the data migration 0014_create_clipper_connections).
- The `login_clipper` of CofProfile instances are sync with their
clipper connections:
    * `CofProfile.sync_clipper_connections` method updates the
connections based on `login_clipper`.
    * Signals receivers `sync_clipper…` update `login_clipper` based on
connections creations/updates/deletions.

Misc
----

- Add NullCharField (model field) which allows to use `unique=True` on
CharField (even with empty strings).
- Parts of kfet mixins for TestCase are now in shared.tests.testcase,
  as they are used elsewhere than in the kfet app.
2018-10-21 17:09:12 +02:00

155 lines
5.3 KiB
Python

# -*- coding: utf-8 -*-
from allauth.socialaccount.models import SocialAccount
from django.db import IntegrityError, transaction
from django.test import TestCase
from gestioncof.models import CofProfile, User
class UserTests(TestCase):
def test_has_profile(self):
"""
A User always has a related CofProfile and a CofProfile always has a
related User.
"""
u = User.objects.create_user("foo", "", "")
# Creating a User creates a related CofProfile.
self.assertTrue(hasattr(u, "profile"))
# there's no point in having a cofprofile without a user associated.
p = u.profile
u.delete()
with transaction.atomic():
with self.assertRaises(CofProfile.DoesNotExist):
p.refresh_from_db()
# there's no point in having a user without a cofprofile associated.
u = User.objects.create_user("foo", "", "")
u.profile.delete()
with transaction.atomic():
with self.assertRaises(User.DoesNotExist):
u.refresh_from_db()
class CofProfileTests(TestCase):
def setUp(self):
self.u = User.objects.create_user("user", "", "")
self.p = self.u.profile
def test_login_clipper_sync_clipper_connections(self):
"""
The value of `login_clipper` is sync with the identifier of the most
recently used clipper connection.
"""
def socialaccount_transform(o):
"""
Function to compare QuerySet of `SocialAccount`, instead of the
default, `repr`, only depends on the involved user.
"""
return "{provider} {username} {uid}".format(
provider=o.provider, username=o.user.username, uid=o.uid
)
def assertClipperAccountQuerysetEquals(user, *args, **kwargs):
kwargs.setdefault("transform", socialaccount_transform)
qs = SocialAccount.objects.filter(provider="clipper", user=user).order_by(
"last_login"
)
self.assertQuerysetEqual(qs, *args, **kwargs)
# Not saved in the database.
self.assertEqual(CofProfile().login_clipper, "")
# This CofProfile has been created without any value for login_clipper.
u, p = self.u, self.p
self.assertEqual(p.login_clipper, "")
# Filling value for the first time triggers the creation of a clipper
# connection (SocialAccount) for the related user.
p.login_clipper = "theclipper"
p.save()
self.assertEqual(p.login_clipper, "theclipper")
assertClipperAccountQuerysetEquals(u, ["clipper user theclipper"])
# Assigning a new value updates the existing connection.
p.login_clipper = "anotherclipper"
p.save()
self.assertEqual(p.login_clipper, "anotherclipper")
assertClipperAccountQuerysetEquals(u, ["clipper user anotherclipper"])
# Creating a clipper connection, using SocialAccount, sets the used
# identifier as value.
conn = SocialAccount.objects.create(provider="clipper", user=u, uid="clip")
self.assertEqual(p.login_clipper, "clip")
assertClipperAccountQuerysetEquals(
u, ["clipper user anotherclipper", "clipper user clip"]
)
# Removing a clipper connection sets the identifier most recently
# used one as value.
conn.delete()
p.refresh_from_db()
self.assertEqual(p.login_clipper, "anotherclipper")
assertClipperAccountQuerysetEquals(u, ["clipper user anotherclipper"])
# If the deletion of SocialAccount(s) leaves no clipper connection,
# flush the value.
SocialAccount.objects.filter(provider="clipper", user=u).delete()
p.refresh_from_db()
self.assertEqual(p.login_clipper, "")
# Assigning an identifier already in use updates the related connection
# as the most recently used.
SocialAccount.objects.bulk_create(
[
SocialAccount(provider="clipper", user=u, uid="theclipper"),
SocialAccount(provider="clipper", user=u, uid="clip"),
]
)
# Note 'clip' is the most recent one, and value is empty because
# bulk_create didn't trigger post_save signals.
p.login_clipper = "theclipper"
p.save()
self.assertEqual(p.login_clipper, "theclipper")
assertClipperAccountQuerysetEquals(
u, ["clipper user clip", "clipper user theclipper"]
)
# Flushing value removes all clipper connections, and set value to None
# to avoid failure due to the unicity constraint.
p.login_clipper = ""
p.save()
self.assertEqual(p.login_clipper, "")
self.assertFalse(
SocialAccount.objects.filter(provider="clipper", user=u).exists()
)
# Value is unique among all CofProfile instances…
p.login_clipper = "theclipper"
p.save()
p2 = User.objects.create_user("user2", "", "").profile
p2.login_clipper = "theclipper"
with transaction.atomic():
with self.assertRaises(IntegrityError):
p2.save()
# …except for '' (stored as NULL).
p.login_clipper = ""
p.save()
p2.login_clipper = ""
p2.save()