"""Tests for UserRepository.""" from uuid import uuid4 import asyncpg import pytest from app.repositories.user import UserRepository class TestUserRepository: """Tests for UserRepository conforming to SPECS.md.""" async def test_create_user_returns_user_data(self, db_conn: asyncpg.Connection) -> None: """Creating a user returns the user data with id, email, created_at.""" repo = UserRepository(db_conn) user_id = uuid4() email = "test@example.com" password_hash = "hashed_password_123" result = await repo.create(user_id, email, password_hash) assert result["id"] == user_id assert result["email"] == email assert result["created_at"] is not None async def test_create_user_stores_password_hash(self, db_conn: asyncpg.Connection) -> None: """Password hash is stored correctly in the database.""" repo = UserRepository(db_conn) user_id = uuid4() email = "hash_test@example.com" password_hash = "bcrypt_hashed_value" await repo.create(user_id, email, password_hash) user = await repo.get_by_id(user_id) assert user["password_hash"] == password_hash async def test_create_user_email_must_be_unique(self, db_conn: asyncpg.Connection) -> None: """Email uniqueness constraint per SPECS.md users table.""" repo = UserRepository(db_conn) email = "duplicate@example.com" await repo.create(uuid4(), email, "hash1") with pytest.raises(asyncpg.UniqueViolationError): await repo.create(uuid4(), email, "hash2") async def test_get_by_id_returns_user(self, db_conn: asyncpg.Connection) -> None: """get_by_id returns the correct user.""" repo = UserRepository(db_conn) user_id = uuid4() email = "getbyid@example.com" await repo.create(user_id, email, "hash") result = await repo.get_by_id(user_id) assert result is not None assert result["id"] == user_id assert result["email"] == email async def test_get_by_id_returns_none_for_nonexistent(self, db_conn: asyncpg.Connection) -> None: """get_by_id returns None for non-existent user.""" repo = UserRepository(db_conn) result = await repo.get_by_id(uuid4()) assert result is None async def test_get_by_email_returns_user(self, db_conn: asyncpg.Connection) -> None: """get_by_email returns the correct user.""" repo = UserRepository(db_conn) user_id = uuid4() email = "getbyemail@example.com" await repo.create(user_id, email, "hash") result = await repo.get_by_email(email) assert result is not None assert result["id"] == user_id assert result["email"] == email async def test_get_by_email_returns_none_for_nonexistent(self, db_conn: asyncpg.Connection) -> None: """get_by_email returns None for non-existent email.""" repo = UserRepository(db_conn) result = await repo.get_by_email("nonexistent@example.com") assert result is None async def test_get_by_email_is_case_sensitive(self, db_conn: asyncpg.Connection) -> None: """Email lookup is case-sensitive (stored as provided).""" repo = UserRepository(db_conn) email = "CaseSensitive@Example.com" await repo.create(uuid4(), email, "hash") # Exact match works result = await repo.get_by_email(email) assert result is not None # Different case returns None result = await repo.get_by_email(email.lower()) assert result is None async def test_exists_by_email_returns_true_when_exists(self, db_conn: asyncpg.Connection) -> None: """exists_by_email returns True when email exists.""" repo = UserRepository(db_conn) email = "exists@example.com" await repo.create(uuid4(), email, "hash") result = await repo.exists_by_email(email) assert result is True async def test_exists_by_email_returns_false_when_not_exists(self, db_conn: asyncpg.Connection) -> None: """exists_by_email returns False when email doesn't exist.""" repo = UserRepository(db_conn) result = await repo.exists_by_email("notexists@example.com") assert result is False async def test_user_id_is_uuid_primary_key(self, db_conn: asyncpg.Connection) -> None: """User ID must be a valid UUID (primary key).""" repo = UserRepository(db_conn) user_id = uuid4() await repo.create(user_id, "pk_test@example.com", "hash") # Duplicate ID should fail with pytest.raises(asyncpg.UniqueViolationError): await repo.create(user_id, "other@example.com", "hash")