import time from datetime import timedelta import pytest from django.conf import settings from django.test import Client, RequestFactory from django.utils import timezone from portal.accounts.middleware import AbsoluteSessionLimitMiddleware from portal.accounts.models import UserSession from portal.accounts.session_store import CREATED_AT, SessionStore from portal.accounts.sessions import SessionUser, current_user, end_sessions, start_session pytestmark = pytest.mark.django_db def make_session(sam): request = RequestFactory().get("/") request.session = SessionStore() start_session(request, sam) request.session.save() return request def test_username_column_is_lowercase_key_and_sam_keeps_case(): request = make_session("Alice") row = UserSession.objects.get(session_key=request.session.session_key) assert row.username == "alice" assert current_user(request) == SessionUser(key="alice", sam="Alice") def test_start_session_rotates_key_and_drops_old_data(): request = RequestFactory().get("/") request.session = SessionStore() request.session["stale"] = 1 request.session.save() old_key = request.session.session_key start_session(request, "alice") request.session.save() assert request.session.session_key != old_key assert "stale" not in request.session assert not UserSession.objects.filter(session_key=old_key).exists() def test_end_sessions_only_target_user(): for _ in range(3): make_session("a") for _ in range(2): make_session("b") assert end_sessions("a") == 3 assert UserSession.objects.filter(username="a").count() == 0 assert UserSession.objects.filter(username="b").count() == 2 def test_end_sessions_except_session_key_and_unknown_user(): keep = make_session("a").session.session_key make_session("a") assert end_sessions(SessionUser("a", "A"), except_session_key=keep) == 1 assert list(UserSession.objects.values_list("session_key", flat=True)) == [keep] assert end_sessions("nobody") == 0 def test_idle_limit_expired_session_is_gone(): request = make_session("a") UserSession.objects.update(expire_date=timezone.now() - timedelta(seconds=1)) assert SessionStore(request.session.session_key).get(CREATED_AT) is None def test_settings_defaults(): assert settings.SESSION_COOKIE_AGE == 3600 assert settings.SESSION_ABSOLUTE_SECONDS == 14400 assert settings.SESSION_SAVE_EVERY_REQUEST is True def _client_with_session(created_at, adapters): adapters.directory.add_user("alice") store = SessionStore() request = RequestFactory().get("/") request.session = store start_session(request, "alice") store[CREATED_AT] = created_at store.save() client = Client() client.cookies[settings.SESSION_COOKIE_NAME] = store.session_key return client, store.session_key def test_absolute_limit_removes_session_and_clears_cookie(adapters): assert any(m.endswith("AbsoluteSessionLimitMiddleware") for m in settings.MIDDLEWARE) too_old = time.time() - settings.SESSION_ABSOLUTE_SECONDS - 5 client, key = _client_with_session(too_old, adapters) response = client.get("/healthz") assert not UserSession.objects.filter(session_key=key).exists() assert response.cookies[settings.SESSION_COOKIE_NAME].value == "" def test_session_within_limits_survives_a_request(adapters): client, key = _client_with_session(time.time() - 60, adapters) client.get("/healthz") assert UserSession.objects.filter(session_key=key).exists() def test_middleware_callable_directly(): request = make_session("alice") AbsoluteSessionLimitMiddleware(lambda r: None)(request) assert current_user(request) is not None def test_request_renews_expire_date_to_one_hour_ahead(adapters): client, key = _client_with_session(time.time() - 60, adapters) UserSession.objects.filter(session_key=key).update( expire_date=timezone.now() + timedelta(minutes=5) ) client.get("/healthz") remaining = UserSession.objects.get(session_key=key).expire_date - timezone.now() assert timedelta(minutes=59) < remaining <= timedelta(hours=1) def test_request_with_idle_expired_session_has_no_session_and_clears_cookie(adapters): client, key = _client_with_session(time.time() - 60, adapters) UserSession.objects.filter(session_key=key).update( expire_date=timezone.now() - timedelta(seconds=1) ) response = client.get("/healthz") assert response.cookies[settings.SESSION_COOKIE_NAME].value == ""