Skip to content

Commit

Permalink
fix unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
SKairinos committed Dec 14, 2023
1 parent cfdc9ca commit 80622a9
Show file tree
Hide file tree
Showing 12 changed files with 95 additions and 56 deletions.
14 changes: 11 additions & 3 deletions codeforlife/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,29 +88,37 @@ def get_queryset(self):
)

def filter(self, *args, **kwargs):
"""A stub that return our custom queryset."""
"""A stub that returns our custom queryset."""

return t.cast(
WarehouseModel.QuerySet,
super().filter(*args, **kwargs),
)

def exclude(self, *args, **kwargs):
"""A stub that return our custom queryset."""
"""A stub that returns our custom queryset."""

return t.cast(
WarehouseModel.QuerySet,
super().exclude(*args, **kwargs),
)

def all(self):
"""A stub that return our custom queryset."""
"""A stub that returns our custom queryset."""

return t.cast(
WarehouseModel.QuerySet,
super().all(),
)

def none(self):
"""A stub that returns our custom queryset."""

return t.cast(
WarehouseModel.QuerySet,
super().none(),
)

objects: Manager = Manager()

# Default for how long to wait before a model is deleted.
Expand Down
18 changes: 12 additions & 6 deletions codeforlife/tests/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,9 @@ def generic(
status_code_assertion: StatusCodeAssertion = None,
**extra,
):
wsgi_response = super().generic(
method, path, data, content_type, secure, **extra
response = t.cast(
Response,
super().generic(method, path, data, content_type, secure, **extra),
)

# Use a custom kwarg to handle the common case of checking the
Expand All @@ -49,13 +50,18 @@ def generic(
elif isinstance(status_code_assertion, int):
expected_status_code = status_code_assertion
status_code_assertion = (
lambda status_code: status_code == expected_status_code
# pylint: disable-next=unnecessary-lambda-assignment
lambda status_code: status_code
== expected_status_code
)

# pylint: disable=no-member
assert status_code_assertion(
wsgi_response.status_code
), f"Unexpected status code: {wsgi_response.status_code}."
response.status_code
), f"Unexpected status code: {response.status_code}."
# pylint: enable=no-member

return wsgi_response
return response

def login(self, **credentials):
assert super().login(
Expand Down
14 changes: 9 additions & 5 deletions codeforlife/user/auth/backends/email_and_password.py
Original file line number Diff line number Diff line change
@@ -1,31 +1,35 @@
import typing as t

from django.contrib.auth.backends import BaseBackend
from django.http.request import HttpRequest

from ....request import WSGIRequest
from ...models import User


class EmailAndPasswordBackend(BaseBackend):
"""Authenticates if the password belongs to the anon user's email."""

def authenticate(
self,
request: WSGIRequest,
request: t.Optional[HttpRequest],
email: t.Optional[str] = None,
password: t.Optional[str] = None,
**kwargs
):
if email is None or password is None:
return
return None

try:
user = User.objects.get(email=email)
if user.check_password(password):
return user
except User.DoesNotExist:
return
return None

return None

def get_user(self, user_id: int):
try:
return User.objects.get(id=user_id)
except User.DoesNotExist:
return
return None
15 changes: 10 additions & 5 deletions codeforlife/user/auth/backends/otp_bypass_token.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,29 @@
import typing as t

from django.contrib.auth.backends import BaseBackend
from django.http.request import HttpRequest

from ....request import WSGIRequest
from ...models import AuthFactor, User


class OtpBypassTokenBackend(BaseBackend):
"""Authenticates if the OTP bypass token belongs to the identified user."""

def authenticate(
self,
request: WSGIRequest,
request: t.Optional[HttpRequest],
token: t.Optional[str] = None,
**kwargs,
):
if (
token is None
request is None
or token is None
or not isinstance(request.user, User)
or not request.user.session.session_auth_factors.filter(
auth_factor__type=AuthFactor.Type.OTP
).exists()
):
return
return None

for otp_bypass_token in request.user.otp_bypass_tokens.all():
if otp_bypass_token.check_token(token):
Expand All @@ -31,8 +34,10 @@ def authenticate(

return request.user

return None

def get_user(self, user_id: int):
try:
return User.objects.get(id=user_id)
except User.DoesNotExist:
return
return None
15 changes: 10 additions & 5 deletions codeforlife/user/fixtures/users.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
"pk": 1,
"fields": {
"last_saved_at": "2023-01-01 00:00:00.0+00:00",
"is_active": true,
"first_name": "John",
"last_name": "Doe",
"email": "[email protected]",
"password": "password",
"password": "pbkdf2_sha256$260000$a2nFLqpwD88sOeZ7wsQskW$WIJACyDluEJWKMPsO/jawrR0sHXHmYebDJoyUihKJxU=",
"teacher": 1
}
},
Expand All @@ -16,10 +17,11 @@
"pk": 2,
"fields": {
"last_saved_at": "2023-01-01 00:00:00.0+00:00",
"is_active": true,
"first_name": "Jane",
"last_name": "Doe",
"email": "[email protected]",
"password": "password",
"password": "pbkdf2_sha256$260000$a2nFLqpwD88sOeZ7wsQskW$WIJACyDluEJWKMPsO/jawrR0sHXHmYebDJoyUihKJxU=",
"teacher": 2
}
},
Expand All @@ -28,8 +30,9 @@
"pk": 3,
"fields": {
"last_saved_at": "2023-01-01 00:00:00.0+00:00",
"is_active": true,
"first_name": "SpongeBob",
"password": "password",
"password": "pbkdf2_sha256$260000$a2nFLqpwD88sOeZ7wsQskW$WIJACyDluEJWKMPsO/jawrR0sHXHmYebDJoyUihKJxU=",
"student": 1
}
},
Expand All @@ -38,8 +41,9 @@
"pk": 4,
"fields": {
"last_saved_at": "2023-01-01 00:00:00.0+00:00",
"is_active": true,
"first_name": "Patrick",
"password": "password",
"password": "pbkdf2_sha256$260000$a2nFLqpwD88sOeZ7wsQskW$WIJACyDluEJWKMPsO/jawrR0sHXHmYebDJoyUihKJxU=",
"student": 2
}
},
Expand All @@ -48,10 +52,11 @@
"pk": 5,
"fields": {
"last_saved_at": "2023-01-01 00:00:00.0+00:00",
"is_active": true,
"first_name": "Indiana",
"last_name": "Jones",
"email": "[email protected]",
"password": "password"
"password": "pbkdf2_sha256$260000$a2nFLqpwD88sOeZ7wsQskW$WIJACyDluEJWKMPsO/jawrR0sHXHmYebDJoyUihKJxU="
}
}
]
2 changes: 1 addition & 1 deletion codeforlife/user/models/otp_bypass_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ def check_token(self, token: str):
A boolean designating if the token is matches.
"""

if not self.delete_after and check_password(token, self.token):
if check_password(token, self.token):
self.delete()
return True
return False
5 changes: 4 additions & 1 deletion codeforlife/user/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,10 @@ def is_authenticated(self):
"""Check if the user has any pending auth factors."""

try:
return not self.session.session_auth_factors.exists()
return (
self.is_active
and not self.session.session_auth_factors.exists()
)
except _session.Session.DoesNotExist:
return False

Expand Down
4 changes: 2 additions & 2 deletions codeforlife/user/serializers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@


class UserSerializer(serializers.ModelSerializer):
student = StudentSerializer(source="new_student")
teacher = TeacherSerializer(source="new_teacher")
student = StudentSerializer()
teacher = TeacherSerializer()

class Meta:
model = User
Expand Down
8 changes: 4 additions & 4 deletions codeforlife/user/tests/views/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ class TestUserViewSet(APITestCase):

def _login_teacher(self):
return self.client.login_teacher(
email="maxplanck@codeforlife.com",
password="Password1",
email="jane.doe@codeforlife.com",
password="password",
is_admin=False,
)

def _login_admin_teacher(self):
return self.client.login_teacher(
email="alberteinstein@codeforlife.com",
password="Password1",
email="john.doe@codeforlife.com",
password="password",
is_admin=True,
)

Expand Down
1 change: 1 addition & 0 deletions codeforlife/user/views/klass.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from ..serializers import ClassSerializer


# pylint: disable-next=missing-class-docstring,too-many-ancestors
class ClassViewSet(ModelViewSet):
http_method_names = ["get"]
lookup_field = "access_code"
Expand Down
1 change: 1 addition & 0 deletions codeforlife/user/views/school.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from ..serializers import SchoolSerializer


# pylint: disable-next=missing-class-docstring,too-many-ancestors
class SchoolViewSet(ModelViewSet):
http_method_names = ["get"]
serializer_class = SchoolSerializer
Expand Down
54 changes: 30 additions & 24 deletions codeforlife/user/views/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,39 @@
from ..serializers import UserSerializer


# pylint: disable-next=missing-class-docstring,too-many-ancestors
class UserViewSet(ModelViewSet):
http_method_names = ["get"]
serializer_class = UserSerializer
filterset_class = UserFilterSet

def get_queryset(self):
user: User = self.request.user
if user.is_student:
if user.student.class_field is None:
return User.objects.filter(id=user.id)

return User.objects.filter(
new_student__class_field=user.student.class_field
)

teachers = User.objects.filter(
new_teacher__school=user.teacher.school_id
)
students = (
User.objects.filter(
# TODO: add school foreign key to student model.
new_student__class_field__teacher__school=user.teacher.school_id,
)
if user.teacher.is_admin
else User.objects.filter(
new_student__class_field__teacher=user.teacher
)
)

return teachers | students
user = self.request.user
if not isinstance(user, User):
return User.objects.none()

if user.student:
return User.objects.filter(student__klass_id=user.student.klass_id)

if user.teacher:
teachers = User.objects.none()
students = User.objects.none()

if user.teacher.school_id:
teachers = User.objects.filter(
teacher__school_id=user.teacher.school_id
)

students = (
User.objects.filter(
student__school_id=user.teacher.school_id
)
if user.teacher.is_admin
else User.objects.filter(
student__klass__teacher=user.teacher
)
)

return teachers | students

return User.objects.filter(id=user.id)

0 comments on commit 80622a9

Please sign in to comment.