"""Unit tests for auth service (no DB required).""" import uuid from unittest.mock import AsyncMock, MagicMock, patch import pytest from rehearsalhub.services.auth import ( AuthService, create_access_token, decode_token, hash_password, verify_password, ) def test_hash_and_verify_password(): plain = "supersecret123" hashed = hash_password(plain) assert verify_password(plain, hashed) assert not verify_password("wrongpassword", hashed) def test_create_and_decode_token(): member_id = str(uuid.uuid4()) email = "test@example.com" token = create_access_token(member_id, email) payload = decode_token(token) assert payload["sub"] == member_id assert payload["email"] == email def test_decode_invalid_token_raises(): from jose import JWTError with pytest.raises(Exception): decode_token("not.a.valid.token") @pytest.mark.asyncio async def test_login_returns_token(mock_session): from rehearsalhub.db.models import Member member = MagicMock(spec=Member) member.id = uuid.uuid4() member.email = "user@example.com" member.password_hash = hash_password("correctpassword") with patch( "rehearsalhub.repositories.member.MemberRepository.get_by_email", new_callable=AsyncMock, return_value=member, ): svc = AuthService(mock_session) result = await svc.login("user@example.com", "correctpassword") assert result is not None assert result.access_token assert result.token_type == "bearer" @pytest.mark.asyncio async def test_login_wrong_password_returns_none(mock_session): from rehearsalhub.db.models import Member member = MagicMock(spec=Member) member.id = uuid.uuid4() member.email = "user@example.com" member.password_hash = hash_password("correctpassword") with patch( "rehearsalhub.repositories.member.MemberRepository.get_by_email", new_callable=AsyncMock, return_value=member, ): svc = AuthService(mock_session) result = await svc.login("user@example.com", "wrongpassword") assert result is None @pytest.mark.asyncio async def test_login_unknown_email_returns_none(mock_session): with patch( "rehearsalhub.repositories.member.MemberRepository.get_by_email", new_callable=AsyncMock, return_value=None, ): svc = AuthService(mock_session) result = await svc.login("nobody@example.com", "anypassword") assert result is None @pytest.mark.asyncio async def test_register_duplicate_email_raises(mock_session): from rehearsalhub.schemas.auth import RegisterRequest with patch( "rehearsalhub.repositories.member.MemberRepository.email_exists", new_callable=AsyncMock, return_value=True, ): svc = AuthService(mock_session) with pytest.raises(ValueError, match="already registered"): await svc.register( RegisterRequest( email="dup@example.com", password="pass123", display_name="Dup", ) )