| from sqlalchemy import create_engine |
| from sqlalchemy.orm import sessionmaker |
| from sqlalchemy.pool import StaticPool |
|
|
| from fastapi import HTTPException |
|
|
| from app import dependencies |
| from app.core.database import Base |
| from app.core.security import decode_access_token, verify_password |
| from app.models.department import Department |
| from app.models.user import User |
| from app.schemas.auth import LoginRequest, SignupRequest |
| from app.services.auth_service import ( |
| authenticate_user, |
| build_token_response, |
| create_user, |
| ) |
|
|
|
|
| def _make_session(): |
| engine = create_engine( |
| "sqlite+pysqlite:///:memory:", |
| connect_args={"check_same_thread": False}, |
| poolclass=StaticPool, |
| ) |
| TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) |
| Base.metadata.create_all(bind=engine) |
| return engine, TestingSessionLocal() |
|
|
|
|
| def test_signup_creates_user_with_hashed_password(): |
| engine, db = _make_session() |
| try: |
| user = create_user( |
| db, |
| SignupRequest( |
| email="student@example.com", |
| password="password123", |
| full_name="Student User", |
| department_name="Student", |
| ), |
| ) |
|
|
| assert user.id is not None |
| assert user.email == "student@example.com" |
| assert user.full_name == "Student User" |
| assert user.role == "STUDENT" |
| assert user.department_id is None |
| assert user.hashed_password != "password123" |
| assert verify_password("password123", user.hashed_password) |
| finally: |
| db.close() |
| Base.metadata.drop_all(bind=engine) |
|
|
|
|
| def test_signup_student_sets_student_role_without_department(): |
| engine, db = _make_session() |
| try: |
| user = create_user( |
| db, |
| SignupRequest( |
| email="student2@example.com", |
| password="password123", |
| full_name="Student User", |
| department_name="Student", |
| ), |
| ) |
|
|
| assert user.role == "STUDENT" |
| assert user.department_id is None |
| finally: |
| db.close() |
| Base.metadata.drop_all(bind=engine) |
|
|
|
|
| def test_login_returns_access_token(): |
| engine, db = _make_session() |
| try: |
| created_user = create_user( |
| db, |
| SignupRequest( |
| email="teacher@example.com", |
| password="password123", |
| full_name="Teacher User", |
| department_name="Student", |
| ), |
| ) |
|
|
| user = authenticate_user( |
| db, |
| LoginRequest(email="teacher@example.com", password="password123"), |
| ) |
| token_response = build_token_response(user) |
| claims = decode_access_token(token_response.access_token) |
|
|
| assert user.id == created_user.id |
| assert token_response.token_type == "bearer" |
| assert claims["sub"] == str(created_user.id) |
| finally: |
| db.close() |
| Base.metadata.drop_all(bind=engine) |
|
|
|
|
| def test_signup_rejects_duplicate_email(): |
| engine, db = _make_session() |
| try: |
| create_user( |
| db, |
| SignupRequest( |
| email="duplicate@example.com", |
| password="password123", |
| department_name="Student", |
| ), |
| ) |
|
|
| try: |
| create_user( |
| db, |
| SignupRequest( |
| email="duplicate@example.com", |
| password="password123", |
| department_name="Student", |
| ), |
| ) |
| raise AssertionError("Expected duplicate email to fail") |
| except ValueError as exc: |
| assert "already registered" in str(exc) |
| finally: |
| db.close() |
| Base.metadata.drop_all(bind=engine) |
|
|
|
|
| def test_signup_staff_sets_staff_role_and_department_id(): |
| engine, db = _make_session() |
| try: |
| department = Department( |
| name="Examination Department", |
| code="EXAM", |
| description="Handles examinations", |
| keywords="exam, timetable, seating", |
| ) |
| db.add(department) |
| db.commit() |
| db.refresh(department) |
|
|
| user = create_user( |
| db, |
| SignupRequest( |
| email="staff@example.com", |
| password="password123", |
| full_name="Exam Staff", |
| department_name="Examination Department", |
| ), |
| ) |
|
|
| assert user.role == "Examination Department" |
| assert user.department_id == department.id |
| finally: |
| db.close() |
| Base.metadata.drop_all(bind=engine) |
|
|
|
|
| def test_signup_response_includes_department_name_for_staff(): |
| engine, db = _make_session() |
| try: |
| department = Department( |
| name="Finance Department", |
| code="FIN", |
| description="Handles finance", |
| keywords="finance, budget, fees", |
| ) |
| db.add(department) |
| db.commit() |
| db.refresh(department) |
|
|
| user = create_user( |
| db, |
| SignupRequest( |
| email="finance.staff@example.com", |
| password="password123", |
| full_name="Finance Staff", |
| department_name="Finance Department", |
| ), |
| ) |
|
|
| token_response = build_token_response(user) |
|
|
| assert token_response.user.department_name == "Finance Department" |
| finally: |
| db.close() |
| Base.metadata.drop_all(bind=engine) |
|
|
|
|
| def test_signup_defaults_new_users_to_non_admin(): |
| engine, db = _make_session() |
| try: |
| user = create_user( |
| db, |
| SignupRequest( |
| email="normal.user@example.com", |
| password="password123", |
| full_name="Normal User", |
| department_name="Student", |
| ), |
| ) |
|
|
| assert user.is_admin is False |
| finally: |
| db.close() |
| Base.metadata.drop_all(bind=engine) |
|
|
|
|
| def test_get_current_admin_user_returns_admin_user(): |
| engine, db = _make_session() |
| try: |
| admin_user = User( |
| email="admin@example.com", |
| full_name="Admin User", |
| hashed_password="hashed", |
| role="ADMIN", |
| is_admin=True, |
| department_id=None, |
| ) |
| db.add(admin_user) |
| db.commit() |
| db.refresh(admin_user) |
|
|
| current_admin = dependencies.get_current_admin_user(current_user=admin_user) |
|
|
| assert current_admin.id == admin_user.id |
| assert current_admin.is_admin is True |
| finally: |
| db.close() |
| Base.metadata.drop_all(bind=engine) |
|
|
|
|
| def test_get_current_admin_user_rejects_non_admin_user(): |
| engine, db = _make_session() |
| try: |
| normal_user = User( |
| email="user@example.com", |
| full_name="Normal User", |
| hashed_password="hashed", |
| role="STUDENT", |
| is_admin=False, |
| department_id=None, |
| ) |
| db.add(normal_user) |
| db.commit() |
| db.refresh(normal_user) |
|
|
| try: |
| dependencies.get_current_admin_user(current_user=normal_user) |
| raise AssertionError("Expected admin check to fail") |
| except HTTPException as exc: |
| assert exc.status_code == 403 |
| assert "Admin access required" in str(exc.detail) |
| finally: |
| db.close() |
| Base.metadata.drop_all(bind=engine) |
|
|