Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: Login/logout tests #408

Merged
merged 12 commits into from
May 13, 2024
5 changes: 3 additions & 2 deletions backend/authentication/urls.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView, TokenVerifyView
from authentication.views import CASViewSet
from authentication.views import CASViewSet, TestUser

router = DefaultRouter()
router.register("cas", CASViewSet, "cas")
router.register("test-user", TestUser, "test-user")

urlpatterns = [
# AUTH endpoints.
path("", include(router.urls)),
# TOKEN endpoints.
path("token/", TokenObtainPairView.as_view(), name="token"),
path("token/refresh/", TokenRefreshView.as_view(), name="token-refresh"),
path("token/verify/", TokenVerifyView.as_view(), name="token-verify")
path("token/verify/", TokenVerifyView.as_view(), name="token-verify"),
]
40 changes: 39 additions & 1 deletion backend/authentication/views.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
from authentication.cas.client import client
from authentication.permissions import IsDebug
from authentication.serializers import CASTokenObtainSerializer, UserSerializer
from django.contrib.auth import logout
from authentication.models import User
from authentication.signals import user_created
from django.contrib.auth import logout, login
from django.shortcuts import redirect
from rest_framework.decorators import action
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response
from rest_framework.viewsets import ViewSet
from rest_framework.status import HTTP_200_OK
from ypovoli import settings


Expand Down Expand Up @@ -59,3 +62,38 @@ def echo(self, request: Request) -> Response:
return Response(token_serializer.validated_data)

raise AuthenticationFailed(token_serializer.errors)


def create_user(self, request) -> Response:
"""General function to create a user, log them in and which returns an empty html page"""
# log in user, or retrieve if they already exist
user, created = User.objects.get_or_create(id=settings.TEST_USER_DATA["id"], defaults=settings.TEST_USER_DATA)

# if it has just been created, send the signal to user_created Signal(), to also activate it as a student
if created:
user_created.send(sender=self, attributes=settings.TEST_USER_ATTRIBUTES, user=user)

# login the user
login(request, user)

# return Response with empty html page
return Response('<!DOCTYPE html><html></html>',
status=HTTP_200_OK, headers={"Location": "/"}, content_type="text/html")


class TestUser(ViewSet):
"""View meant to be able to log in quickly for tests on server in debug mode"""

permission_classes = [IsDebug]

@action(detail=False, methods=['GET'], permission_classes=[IsDebug], url_path='admin')
def login_admin(self, request, *__) -> Response:
"""This endpoint lets you log in an admin"""
settings.TEST_USER_DATA["is_staff"] = True
return create_user(self, request)

@action(detail=False, methods=['GET'], permission_classes=[IsDebug], url_path='student')
def login_student(self, request, *__) -> Response:
"""This endpoint lets you log in as a student who's not an admin"""
settings.TEST_USER_DATA["is_staff"] = False
return create_user(self, request)
12 changes: 12 additions & 0 deletions backend/ypovoli/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,19 @@
ROOT_DIR = environ.get("DJANGO_ROOT_DIR", "")
MEDIA_ROOT = os.path.normpath(os.path.join("data/production"))

# TESTING
TESTING_BASE_LINK = "http://testserver"
TEST_USER_DATA = {
"id": "1234",
"username": "test",
"email": "test@test",
"first_name": "test",
"last_name": "test",
}
TEST_USER_ATTRIBUTES = {
**TEST_USER_DATA,
"ugentStudentID": "1234"
}

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = environ.get("DJANGO_SECRET_KEY", "lnZZ2xHc6HjU5D85GDE3Nnu4CJsBnm")
Expand Down
24 changes: 12 additions & 12 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/src/components/layout/base/BaseHeader.vue
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ const items = computed(() => [
<div>
<!-- User information -->
<template v-if="user !== null">
<RouterLink :to="{ name: 'logout' }" class="text-white">
<RouterLink :to="{ name: 'logout' }" class="text-white" id="logout">
<span class="hidden md:inline">
{{ t('layout.header.user', [user.getFullName()]) }}
</span>
Expand Down
1 change: 1 addition & 0 deletions frontend/src/router/guards/authentication.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export async function AuthenticationGuard(to: RouteLocationNormalized): Promise<
const { refreshUser } = useAuthStore();
const { intent, isAuthenticated } = storeToRefs(useAuthStore());

console.log(to.name as string);
bsilkyn marked this conversation as resolved.
Show resolved Hide resolved
if (!isAuthenticated.value && !['login', 'verify'].includes(to.name as string)) {
try {
await refreshUser();
Expand Down
28 changes: 28 additions & 0 deletions frontend/src/test/e2e/login.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
describe('login', () => {
it('redirects to login page when not logged in', () => {
// visit dashboard
cy.visit('/');
// should redirect to login page
cy.url().should('match', /^.*\/auth\/login$/);
});
it('does not redirect to login page when logged in', () => {
// log in as a test student
cy.visit('/api/auth/test-user/student/');
// visit dashboard
cy.visit('/');
// should not be redirected to login page
cy.url().should('not.match', /^.*\/auth\/login$/);
// logout for cleaning up
cy.get('#logout').click();
});
it('redirects to login page after logging out', () => {
// log in as a test student
cy.visit('/api/auth/test-user/student/');
// visit dashboard
cy.visit('/');
// logout
cy.get('#logout').click();
// should be on login page now
cy.url().should('match', /^.*\/auth\/login$/);
});
});
1 change: 1 addition & 0 deletions frontend/src/views/authentication/LoginView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const { t } = useI18n();
:icon="PrimeIcons.ARROW_RIGHT"
:label="t('views.login.button')"
severity="secondary"
id="login"
/>
</a>
</div>
Expand Down