Initial commit
This commit is contained in:
74
backend/tests/unit/test_services/test_book_service.py
Normal file
74
backend/tests/unit/test_services/test_book_service.py
Normal file
@@ -0,0 +1,74 @@
|
||||
"""Tests for BookService"""
|
||||
|
||||
import pytest
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
import aiofiles.os as aios
|
||||
|
||||
from chitai.schemas import BookCreate
|
||||
from chitai.services import BookService
|
||||
from chitai.database import models as m
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestBookServiceCRUD:
|
||||
"""Test CRUD operation for libraries."""
|
||||
|
||||
async def test_update_book(
|
||||
self, books_service: BookService, test_library: m.Library
|
||||
) -> None:
|
||||
book_data = BookCreate(
|
||||
library_id=1,
|
||||
title="Fellowship of the Ring",
|
||||
authors=["J.R.R Tolkien"],
|
||||
tags=["Fantasy"],
|
||||
identifiers={"isbn-13": "9780261102354"},
|
||||
pages=427,
|
||||
)
|
||||
|
||||
book = await books_service.to_model_on_create(book_data.model_dump())
|
||||
|
||||
# Add path manually as it won't be generated (not using the create function, but manually inserting into db)
|
||||
book.path = f"{test_library.root_path}/J.R.R Tolkien/The Fellowship of the Ring"
|
||||
await aios.makedirs(book.path)
|
||||
|
||||
books_service.repository.session.add(book)
|
||||
await books_service.repository.session.commit()
|
||||
await books_service.repository.session.refresh(book)
|
||||
|
||||
await books_service.update(
|
||||
book.id,
|
||||
{
|
||||
"title": "The Fellowship of the Ring",
|
||||
"identifiers": {"isbn-10": "9780261102354"},
|
||||
"edition": 3,
|
||||
"publisher": "Tolkien Estate",
|
||||
"series": "The Lord of the Rings",
|
||||
"series_position": "1",
|
||||
"tags": ["Fantasy", "Adventure"],
|
||||
},
|
||||
test_library,
|
||||
)
|
||||
|
||||
updated_book = await books_service.get(book.id)
|
||||
|
||||
# Assert updated information is correct
|
||||
|
||||
assert updated_book.title == "The Fellowship of the Ring"
|
||||
assert (
|
||||
updated_book.path
|
||||
== f"{test_library.root_path}/J.R.R Tolkien/The Lord of the Rings/01 - The Fellowship of the Ring"
|
||||
)
|
||||
|
||||
assert len(updated_book.identifiers)
|
||||
assert updated_book.identifiers[0].value == "9780261102354"
|
||||
|
||||
assert updated_book.edition == 3
|
||||
assert updated_book.publisher.name == "Tolkien Estate"
|
||||
|
||||
assert len(updated_book.tags) == 2
|
||||
|
||||
|
||||
# book = await books_service.create(book_data.model_dump())
|
||||
298
backend/tests/unit/test_services/test_bookshelf_service.py
Normal file
298
backend/tests/unit/test_services/test_bookshelf_service.py
Normal file
@@ -0,0 +1,298 @@
|
||||
"""Tests for LibraryService"""
|
||||
|
||||
import pytest
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from chitai.services import ShelfService
|
||||
from chitai.database import models as m
|
||||
|
||||
|
||||
import pytest
|
||||
from sqlalchemy import select
|
||||
|
||||
from chitai.services.bookshelf import ShelfService
|
||||
from chitai.services import BookService
|
||||
from chitai.database.models.book_list import BookList, BookListLink
|
||||
from chitai.database import models as m
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def db_session(bookshelf_service: ShelfService) -> AsyncSession:
|
||||
return bookshelf_service.repository.session
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
async def test_books(db_session: AsyncSession) -> list[m.Book]:
|
||||
"""Create test books in the database."""
|
||||
|
||||
library = m.Library(
|
||||
name="Default Library",
|
||||
slug="default-library",
|
||||
root_path="./path",
|
||||
path_template="{author}/{title}",
|
||||
read_only=False,
|
||||
)
|
||||
|
||||
db_session.add(library)
|
||||
|
||||
books = [m.Book(title=f"Book {i}", library_id=1) for i in range(1, 8)]
|
||||
db_session.add_all(books)
|
||||
|
||||
user = m.User(email="test_user@example.com", password="password123")
|
||||
db_session.add(user)
|
||||
|
||||
await db_session.flush()
|
||||
await db_session.commit()
|
||||
return books
|
||||
|
||||
|
||||
class TestAddBooks:
|
||||
async def test_add_books_to_empty_shelf(
|
||||
self, bookshelf_service: ShelfService, db_session
|
||||
) -> None:
|
||||
"""Successfully add books to an empty shelf."""
|
||||
# Create a shelf
|
||||
shelf = BookList(title="Test Shelf", user_id=1)
|
||||
db_session.add(shelf)
|
||||
await db_session.flush()
|
||||
|
||||
# Add books
|
||||
await bookshelf_service.add_books(shelf.id, [1, 2, 3])
|
||||
|
||||
# Verify books were added
|
||||
links = await db_session.execute(
|
||||
select(BookListLink).where(BookListLink.list_id == shelf.id)
|
||||
)
|
||||
added_links = links.scalars().all()
|
||||
assert len(added_links) == 3
|
||||
assert {link.book_id for link in added_links} == {1, 2, 3}
|
||||
|
||||
async def test_add_books_preserves_positions(
|
||||
self, bookshelf_service: ShelfService, db_session
|
||||
) -> None:
|
||||
"""Verify books are assigned correct positions on the shelf."""
|
||||
shelf = BookList(title="Test Shelf", user_id=1)
|
||||
db_session.add(shelf)
|
||||
await db_session.flush()
|
||||
|
||||
await bookshelf_service.add_books(shelf.id, [1, 2, 3])
|
||||
|
||||
links = await db_session.execute(
|
||||
select(BookListLink)
|
||||
.where(BookListLink.list_id == shelf.id)
|
||||
.order_by(BookListLink.position)
|
||||
)
|
||||
added_links = links.scalars().all()
|
||||
|
||||
# Verify positions are sequential starting from 0
|
||||
assert [link.position for link in added_links] == [0, 1, 2]
|
||||
|
||||
async def test_add_books_to_shelf_with_existing_books(
|
||||
self, bookshelf_service: ShelfService, db_session
|
||||
) -> None:
|
||||
"""Adding books to a shelf with existing books assigns correct positions."""
|
||||
shelf = BookList(title="Test Shelf", user_id=1)
|
||||
db_session.add(shelf)
|
||||
await db_session.flush()
|
||||
|
||||
# Add initial books
|
||||
await bookshelf_service.add_books(shelf.id, [1, 2])
|
||||
|
||||
# Add more books
|
||||
await bookshelf_service.add_books(shelf.id, [3, 4])
|
||||
|
||||
links = await db_session.execute(
|
||||
select(BookListLink)
|
||||
.where(BookListLink.list_id == shelf.id)
|
||||
.order_by(BookListLink.position)
|
||||
)
|
||||
added_links = links.scalars().all()
|
||||
|
||||
# Verify new books continue from position 2
|
||||
assert len(added_links) == 4
|
||||
assert [link.position for link in added_links] == [0, 1, 2, 3]
|
||||
|
||||
async def test_add_duplicate_books_is_idempotent(
|
||||
self, bookshelf_service: ShelfService, db_session: AsyncSession
|
||||
) -> None:
|
||||
"""Adding books already on shelf should not create duplicates."""
|
||||
shelf = BookList(title="Test Shelf", user_id=1)
|
||||
db_session.add(shelf)
|
||||
await db_session.flush()
|
||||
|
||||
# Add books
|
||||
await bookshelf_service.add_books(shelf.id, [1, 2, 3])
|
||||
|
||||
# Try to add overlapping books
|
||||
await bookshelf_service.add_books(shelf.id, [2, 3, 4])
|
||||
|
||||
links = await db_session.execute(
|
||||
select(BookListLink).where(BookListLink.list_id == shelf.id)
|
||||
)
|
||||
added_links = links.scalars().all()
|
||||
|
||||
# Should have 4 books total (1, 2, 3, 4), not 7
|
||||
assert len(added_links) == 4
|
||||
assert {link.book_id for link in added_links} == {1, 2, 3, 4}
|
||||
|
||||
async def test_add_books_raises_on_nonexistent_shelf(
|
||||
self, bookshelf_service: ShelfService
|
||||
) -> None:
|
||||
"""Adding books to nonexistent shelf raises error."""
|
||||
with pytest.raises(Exception): # Could be SQLAlchemyError or specific error
|
||||
await bookshelf_service.add_books(99999, [1, 2, 3])
|
||||
|
||||
async def test_add_nonexistent_books_raises_error(
|
||||
self, bookshelf_service: ShelfService, db_session: AsyncSession
|
||||
) -> None:
|
||||
"""Adding nonexistent books raises ValueError."""
|
||||
shelf = BookList(title="Test Shelf", user_id=1)
|
||||
db_session.add(shelf)
|
||||
await db_session.flush()
|
||||
|
||||
with pytest.raises(ValueError, match="One or more books not found"):
|
||||
await bookshelf_service.add_books(shelf.id, [99999, 99998])
|
||||
|
||||
async def test_add_partial_nonexistent_books_raises_error(
|
||||
self, bookshelf_service: ShelfService, db_session: AsyncSession
|
||||
) -> None:
|
||||
"""Adding a mix of existent and nonexistent books raises error."""
|
||||
shelf = BookList(title="Test Shelf", user_id=1)
|
||||
db_session.add(shelf)
|
||||
await db_session.flush()
|
||||
|
||||
# Book 1 might exist, but 99999 doesn't
|
||||
with pytest.raises(ValueError, match="One or more books not found"):
|
||||
await bookshelf_service.add_books(shelf.id, [1, 99999])
|
||||
|
||||
async def test_add_empty_book_list(
|
||||
self, bookshelf_service: ShelfService, db_session: AsyncSession
|
||||
) -> None:
|
||||
"""Adding empty book list should return without error."""
|
||||
shelf = BookList(title="Test Shelf", user_id=1)
|
||||
db_session.add(shelf)
|
||||
await db_session.flush()
|
||||
|
||||
# Should not raise
|
||||
await bookshelf_service.add_books(shelf.id, [])
|
||||
|
||||
|
||||
class TestRemoveBooks:
|
||||
async def test_remove_books_from_shelf(
|
||||
self, bookshelf_service: ShelfService, db_session: AsyncSession
|
||||
) -> None:
|
||||
"""Successfully remove books from a shelf."""
|
||||
shelf = BookList(title="Test Shelf", user_id=1)
|
||||
db_session.add(shelf)
|
||||
await db_session.flush()
|
||||
|
||||
# Add books
|
||||
await bookshelf_service.add_books(shelf.id, [1, 2, 3, 4])
|
||||
|
||||
# Remove some books
|
||||
await bookshelf_service.remove_books(shelf.id, [2, 3])
|
||||
|
||||
links = await db_session.execute(
|
||||
select(BookListLink).where(BookListLink.list_id == shelf.id)
|
||||
)
|
||||
remaining_links = links.scalars().all()
|
||||
|
||||
assert len(remaining_links) == 2
|
||||
assert {link.book_id for link in remaining_links} == {1, 4}
|
||||
|
||||
async def test_remove_all_books_from_shelf(
|
||||
self, bookshelf_service: ShelfService, db_session: AsyncSession
|
||||
) -> None:
|
||||
"""Removing all books should leave shelf empty."""
|
||||
shelf = BookList(title="Test Shelf", user_id=1)
|
||||
db_session.add(shelf)
|
||||
await db_session.flush()
|
||||
|
||||
await bookshelf_service.add_books(shelf.id, [1, 2, 3])
|
||||
await bookshelf_service.remove_books(shelf.id, [1, 2, 3])
|
||||
|
||||
links = await db_session.execute(
|
||||
select(BookListLink).where(BookListLink.list_id == shelf.id)
|
||||
)
|
||||
remaining_links = links.scalars().all()
|
||||
|
||||
assert len(remaining_links) == 0
|
||||
|
||||
async def test_remove_nonexistent_book_is_idempotent(
|
||||
self, bookshelf_service: ShelfService, db_session: AsyncSession
|
||||
) -> None:
|
||||
"""Removing books not on shelf should not raise error."""
|
||||
shelf = BookList(title="Test Shelf", user_id=1)
|
||||
db_session.add(shelf)
|
||||
await db_session.flush()
|
||||
|
||||
await bookshelf_service.add_books(shelf.id, [1, 2, 3])
|
||||
|
||||
# Remove books that don't exist on shelf - should not raise
|
||||
await bookshelf_service.remove_books(shelf.id, [99, 100])
|
||||
|
||||
links = await db_session.execute(
|
||||
select(BookListLink).where(BookListLink.list_id == shelf.id)
|
||||
)
|
||||
remaining_links = links.scalars().all()
|
||||
|
||||
# Original books should still be there
|
||||
assert len(remaining_links) == 3
|
||||
|
||||
async def test_remove_books_raises_on_nonexistent_shelf(
|
||||
self, bookshelf_service: ShelfService
|
||||
) -> None:
|
||||
"""Removing books from nonexistent shelf raises error."""
|
||||
with pytest.raises(Exception):
|
||||
await bookshelf_service.remove_books(99999, [1, 2, 3])
|
||||
|
||||
async def test_remove_empty_book_list(
|
||||
self, bookshelf_service: ShelfService, db_session: AsyncSession
|
||||
) -> None:
|
||||
"""Removing empty book list should return without error."""
|
||||
shelf = BookList(title="Test Shelf", user_id=1)
|
||||
db_session.add(shelf)
|
||||
await db_session.flush()
|
||||
|
||||
await bookshelf_service.add_books(shelf.id, [1, 2, 3])
|
||||
|
||||
# Should not raise
|
||||
await bookshelf_service.remove_books(shelf.id, [])
|
||||
|
||||
links = await db_session.execute(
|
||||
select(BookListLink).where(BookListLink.list_id == shelf.id)
|
||||
)
|
||||
remaining_links = links.scalars().all()
|
||||
|
||||
# All books should still be there
|
||||
assert len(remaining_links) == 3
|
||||
|
||||
async def test_add_remove_add_sequence(
|
||||
self, bookshelf_service: ShelfService, db_session
|
||||
) -> None:
|
||||
"""Add books, remove from middle, then add more - positions should be maintained."""
|
||||
shelf = BookList(title="Test Shelf", user_id=1)
|
||||
db_session.add(shelf)
|
||||
await db_session.flush()
|
||||
|
||||
# Add initial books [1, 2, 3, 4, 5]
|
||||
await bookshelf_service.add_books(shelf.id, [1, 2, 3, 4, 5])
|
||||
|
||||
# Remove books from middle [2, 3, 4]
|
||||
await bookshelf_service.remove_books(shelf.id, [2, 3, 4])
|
||||
|
||||
# Add more books [6, 7]
|
||||
await bookshelf_service.add_books(shelf.id, [6, 7])
|
||||
|
||||
links = await db_session.execute(
|
||||
select(BookListLink)
|
||||
.where(BookListLink.list_id == shelf.id)
|
||||
.order_by(BookListLink.position)
|
||||
)
|
||||
final_links = links.scalars().all()
|
||||
|
||||
# Should have [1, 5, 6, 7] with positions [0, 1, 2, 3]
|
||||
assert len(final_links) == 4
|
||||
assert [link.book_id for link in final_links] == [1, 5, 6, 7]
|
||||
assert [link.position for link in final_links] == [0, 1, 2, 3]
|
||||
153
backend/tests/unit/test_services/test_library_service.py
Normal file
153
backend/tests/unit/test_services/test_library_service.py
Normal file
@@ -0,0 +1,153 @@
|
||||
"""Tests for LibraryService"""
|
||||
|
||||
import pytest
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from chitai.schemas.library import LibraryCreate
|
||||
from chitai.services import LibraryService
|
||||
from chitai.database import models as m
|
||||
from chitai.services.utils import DirectoryDoesNotExist
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestLibraryServiceCRUD:
|
||||
"""Test CRUD operation for libraries."""
|
||||
|
||||
async def test_create_library(
|
||||
self, library_service: LibraryService, tmp_path: Path
|
||||
) -> None:
|
||||
"""Test creating a library with a valid root path."""
|
||||
|
||||
library_path = f"{tmp_path}/books"
|
||||
library_data = LibraryCreate(
|
||||
name="Test Library",
|
||||
root_path=library_path,
|
||||
path_template="{author}/{title}",
|
||||
read_only=False,
|
||||
)
|
||||
|
||||
library = await library_service.create(library_data)
|
||||
|
||||
assert library.name == "Test Library"
|
||||
assert library.root_path == library_path
|
||||
assert library.path_template == "{author}/{title}"
|
||||
assert library.description == None
|
||||
assert library.read_only == False
|
||||
|
||||
# Check if directory was created
|
||||
assert Path(library.root_path).is_dir()
|
||||
|
||||
async def test_create_library_root_path_permission_error(
|
||||
self, library_service: LibraryService, tmp_path: Path
|
||||
) -> None:
|
||||
"""Test creating a library with a root path that is not permitted."""
|
||||
|
||||
# Change permissions on the temp path
|
||||
tmp_path.chmod(0o544)
|
||||
|
||||
library_path = f"{tmp_path}/books"
|
||||
library_data = LibraryCreate(
|
||||
name="Test Library",
|
||||
root_path=library_path,
|
||||
path_template="{author}/{title}",
|
||||
read_only=False,
|
||||
)
|
||||
|
||||
with pytest.raises(PermissionError) as exc_info:
|
||||
library = await library_service.create(library_data)
|
||||
|
||||
# Check if directory was created
|
||||
assert not Path(library_path).exists()
|
||||
|
||||
# Change permissions back
|
||||
tmp_path.chmod(0o755)
|
||||
|
||||
async def test_create_library_read_only_path_exists(
|
||||
self, library_service: LibraryService, tmp_path: Path
|
||||
) -> None:
|
||||
"""Test creating a read-only library with a root path that exists."""
|
||||
|
||||
# Create the path beforehand
|
||||
library_path = f"{tmp_path}/books"
|
||||
Path(library_path).mkdir()
|
||||
|
||||
library_data = LibraryCreate(
|
||||
name="Test Library",
|
||||
root_path=library_path,
|
||||
path_template="{author}/{title}",
|
||||
read_only=True,
|
||||
)
|
||||
|
||||
library = await library_service.create(library_data)
|
||||
|
||||
assert library.name == "Test Library"
|
||||
assert library.root_path == library_path
|
||||
assert library.path_template == "{author}/{title}"
|
||||
assert library.description == None
|
||||
assert library.read_only == True
|
||||
|
||||
async def test_create_library_read_only_nonexistent_path(
|
||||
self, library_service: LibraryService, tmp_path: Path
|
||||
) -> None:
|
||||
"""Test creating a read-only library with a nonexistent root path."""
|
||||
|
||||
library_path = f"{tmp_path}/books"
|
||||
library_data = LibraryCreate(
|
||||
name="Test Library",
|
||||
root_path=library_path,
|
||||
path_template="{author}/{title}",
|
||||
read_only=True,
|
||||
)
|
||||
|
||||
with pytest.raises(DirectoryDoesNotExist) as exc_info:
|
||||
await library_service.create(library_data)
|
||||
|
||||
assert "Root directory" in str(exc_info.value)
|
||||
assert "must exist for a read-only library" in str(exc_info.value)
|
||||
|
||||
# Check if directory was created
|
||||
assert not Path(library_path).exists()
|
||||
|
||||
async def test_get_library(
|
||||
self, session: AsyncSession, library_service: LibraryService
|
||||
) -> None:
|
||||
"""Test retrieving a library."""
|
||||
|
||||
# Add a library to the database
|
||||
library_data = m.Library(
|
||||
name="Testing Library",
|
||||
slug="testing-library",
|
||||
root_path="./books",
|
||||
path_template="{author}/{title}",
|
||||
description=None,
|
||||
read_only=False,
|
||||
)
|
||||
|
||||
session.add(library_data)
|
||||
await session.commit()
|
||||
await session.refresh(library_data)
|
||||
|
||||
library = await library_service.get(library_data.id)
|
||||
|
||||
assert library is not None
|
||||
assert library.id == library_data.id
|
||||
assert library.name == "Testing Library"
|
||||
assert library.root_path == "./books"
|
||||
assert library.path_template == "{author}/{title}"
|
||||
assert library.description is None
|
||||
assert library.read_only == False
|
||||
|
||||
# async def test_delete_library_keep_files(
|
||||
# self, session: AsyncSession, library_service: LibraryService
|
||||
# ) -> None:
|
||||
# """Test deletion of a library's metadata and associated entities."""
|
||||
# raise NotImplementedError()
|
||||
|
||||
# async def test_delete_library_delete_files(
|
||||
# self, session: AsyncSession, library_service: LibraryService
|
||||
# ) -> None:
|
||||
# """Test deletion of a library's metadata, associated enties, and files/directories."""
|
||||
# raise NotImplementedError()
|
||||
116
backend/tests/unit/test_services/test_user_service.py
Normal file
116
backend/tests/unit/test_services/test_user_service.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""Tests for UserService"""
|
||||
|
||||
import pytest
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from litestar.exceptions import PermissionDeniedException
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
|
||||
from chitai.services import UserService
|
||||
from chitai.database import models as m
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestUserServiceAuthentication:
|
||||
"""Test authentication functionality."""
|
||||
|
||||
async def test_authenticate_success(
|
||||
self, session: AsyncSession, user_service: UserService
|
||||
) -> None:
|
||||
"""Test successful user authentication."""
|
||||
|
||||
# Create a user with a known password
|
||||
password = "password123"
|
||||
user = m.User(email=f"test@example.com", password=password)
|
||||
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
|
||||
# Authenticate user
|
||||
authenticated_user = await user_service.authenticate(
|
||||
"test@example.com", password
|
||||
)
|
||||
|
||||
assert authenticated_user is not None
|
||||
assert authenticated_user.email == "test@example.com"
|
||||
assert authenticated_user.id == user.id
|
||||
|
||||
async def test_authenticate_user_not_found(
|
||||
self, session: AsyncSession, user_service: UserService
|
||||
) -> None:
|
||||
"""Test authentication with non-existent user."""
|
||||
|
||||
with pytest.raises(PermissionDeniedException) as exc_info:
|
||||
await user_service.authenticate("nonexistent@example.com", "password")
|
||||
|
||||
assert "User not found or password invalid" in str(exc_info.value)
|
||||
|
||||
async def test_authenticate_wrong_password(
|
||||
self, session: AsyncSession, user_service: UserService
|
||||
) -> None:
|
||||
"""Test authentication with wrong password."""
|
||||
|
||||
# Create user
|
||||
password = "password123"
|
||||
user = m.User(email=f"test@example.com", password=password)
|
||||
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
|
||||
# Attempt authentication
|
||||
with pytest.raises(PermissionDeniedException) as exc_info:
|
||||
await user_service.authenticate("test@example.com", "WrongPassword")
|
||||
|
||||
assert "User not found or password invalid" in str(exc_info.value)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
class TestUserServiceCRUD:
|
||||
"""Test basic CRUD operations."""
|
||||
|
||||
async def test_create_user_with_password_hashing(
|
||||
self, session: AsyncSession, user_service: UserService
|
||||
) -> None:
|
||||
user_data = {"email": "newuser@example.com", "password": "password123"}
|
||||
|
||||
user = await user_service.create(data=user_data)
|
||||
|
||||
assert user.email == "newuser@example.com"
|
||||
assert user.password is not None
|
||||
assert user.password != "password123" # Password should be hashed
|
||||
assert user.password.verify("password123")
|
||||
|
||||
async def test_get_user_by_email(
|
||||
self, session: AsyncSession, user_service: UserService
|
||||
) -> None:
|
||||
"""Test getting user by email."""
|
||||
|
||||
user = m.User(email=f"test@example.com", password="password123")
|
||||
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
|
||||
found_user = await user_service.get_one_or_none(email="test@example.com")
|
||||
|
||||
assert found_user is not None
|
||||
assert found_user.id == user.id
|
||||
assert found_user.email == user.email
|
||||
|
||||
async def test_create_user_with_duplicate_email(
|
||||
self, session: AsyncSession, user_service: UserService
|
||||
) -> None:
|
||||
"""Test creating a new user with a duplicate email."""
|
||||
|
||||
# Create first user
|
||||
user = m.User(email=f"test@example.com", password="password123")
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
|
||||
# Create second user
|
||||
user = m.User(email=f"test@example.com", password="password12345")
|
||||
|
||||
with pytest.raises(IntegrityError) as exc_info:
|
||||
session.add(user)
|
||||
await session.commit()
|
||||
|
||||
assert "violates unique constraint" in str(exc_info.value)
|
||||
Reference in New Issue
Block a user