287 lines
9.3 KiB
Python
287 lines
9.3 KiB
Python
import pytest
|
|
from httpx import AsyncClient
|
|
|
|
|
|
async def test_get_shelves_without_auth(client: AsyncClient) -> None:
|
|
response = await client.get("/shelves")
|
|
assert response.status_code == 401
|
|
|
|
|
|
async def test_get_shelves_with_auth(authenticated_client: AsyncClient) -> None:
|
|
response = await authenticated_client.get("/shelves")
|
|
assert response.status_code == 200
|
|
|
|
result = response.json()
|
|
assert len(result["items"]) == 0
|
|
|
|
|
|
async def test_get_existing_shelves(
|
|
populated_authenticated_client: AsyncClient,
|
|
) -> None:
|
|
response = await populated_authenticated_client.get("/shelves")
|
|
assert response.status_code == 200
|
|
|
|
result = response.json()
|
|
assert len(result["items"]) == 1
|
|
|
|
|
|
async def test_create_shelf(authenticated_client: AsyncClient) -> None:
|
|
shelf_data = {"title": "Favourites"}
|
|
|
|
response = await authenticated_client.post("/shelves", json=shelf_data)
|
|
|
|
assert response.status_code == 201
|
|
|
|
result = response.json()
|
|
|
|
assert result["title"] == "Favourites"
|
|
assert result["library_id"] is None
|
|
|
|
|
|
async def test_create_shelf_in_nonexistent_library(
|
|
authenticated_client: AsyncClient,
|
|
) -> None:
|
|
shelf_data = {"title": "Favourites", "library_id": 5}
|
|
|
|
response = await authenticated_client.post("/shelves", json=shelf_data)
|
|
|
|
assert response.status_code == 400
|
|
assert f"Library with ID {shelf_data['library_id']} does not exist" in response.text
|
|
|
|
|
|
async def test_create_shelf_in_existing_library(
|
|
authenticated_client: AsyncClient,
|
|
) -> None:
|
|
shelf_data = {"title": "Favourites", "library_id": 1}
|
|
|
|
response = await authenticated_client.post("/shelves", json=shelf_data)
|
|
|
|
assert response.status_code == 201
|
|
|
|
result = response.json()
|
|
|
|
assert result["title"] == "Favourites"
|
|
assert result["library_id"] == 1
|
|
|
|
|
|
async def test_delete_shelf_without_auth(client: AsyncClient) -> None:
|
|
response = await client.delete("/shelves/1")
|
|
assert response.status_code == 401
|
|
|
|
|
|
async def test_delete_shelf_unauthorized(
|
|
authenticated_client: AsyncClient, other_authenticated_client: AsyncClient
|
|
) -> None:
|
|
"""Verify users can't delete shelves they don't own."""
|
|
# Create a shelf as authenticated_client
|
|
shelf_data = {"title": "My Shelf"}
|
|
shelf_response = await authenticated_client.post("/shelves", json=shelf_data)
|
|
shelf_id = shelf_response.json()["id"]
|
|
|
|
# Try to delete as other_authenticated_client
|
|
response = await other_authenticated_client.delete(f"/shelves/{shelf_id}")
|
|
assert response.status_code == 403
|
|
assert "do not have permission" in response.text
|
|
|
|
|
|
async def test_add_books_unauthorized(
|
|
authenticated_client: AsyncClient, other_authenticated_client: AsyncClient
|
|
) -> None:
|
|
"""Verify users can't add books to shelves they don't own."""
|
|
# Create a shelf as authenticated_client
|
|
shelf_data = {"title": "Other User's Shelf"}
|
|
shelf_response = await authenticated_client.post("/shelves", json=shelf_data)
|
|
shelf_id = shelf_response.json()["id"]
|
|
|
|
# Try to add books as other_authenticated_client
|
|
response = await other_authenticated_client.post(
|
|
f"/shelves/{shelf_id}/books", params={"book_ids": [1, 2]}
|
|
)
|
|
assert response.status_code == 403
|
|
assert "do not have permission" in response.text
|
|
|
|
|
|
async def test_remove_books_unauthorized(
|
|
authenticated_client: AsyncClient, other_authenticated_client: AsyncClient
|
|
) -> None:
|
|
"""Verify users can't remove books from shelves they don't own."""
|
|
# Create a shelf and add books as authenticated_client
|
|
shelf_data = {"title": "Other User's Shelf"}
|
|
shelf_response = await authenticated_client.post("/shelves", json=shelf_data)
|
|
shelf_id = shelf_response.json()["id"]
|
|
|
|
await authenticated_client.post(
|
|
f"/shelves/{shelf_id}/books", params={"book_ids": [1, 2]}
|
|
)
|
|
|
|
# Try to remove books as other_authenticated_client
|
|
response = await other_authenticated_client.delete(
|
|
f"/shelves/{shelf_id}/books", params={"book_ids": [1]}
|
|
)
|
|
assert response.status_code == 403
|
|
assert "do not have permission" in response.text
|
|
|
|
|
|
async def test_delete_nonexistent_shelf(authenticated_client: AsyncClient) -> None:
|
|
"""Verify 404 when deleting a shelf that doesn't exist."""
|
|
response = await authenticated_client.delete("/shelves/99999")
|
|
assert response.status_code == 404
|
|
|
|
|
|
async def test_add_books_to_shelf(populated_authenticated_client: AsyncClient) -> None:
|
|
"""Successfully add books to a shelf."""
|
|
shelf_data = {"title": "Test Shelf"}
|
|
shelf_response = await populated_authenticated_client.post(
|
|
"/shelves", json=shelf_data
|
|
)
|
|
shelf_id = shelf_response.json()["id"]
|
|
|
|
book_ids = [1, 2]
|
|
response = await populated_authenticated_client.post(
|
|
f"/shelves/{shelf_id}/books", params={"book_ids": book_ids}
|
|
)
|
|
assert response.status_code == 201
|
|
|
|
# Verify by listing books filtered by shelf
|
|
books_response = await populated_authenticated_client.get(
|
|
"/books", params={"shelves": shelf_id}
|
|
)
|
|
assert books_response.status_code == 200
|
|
assert len(books_response.json()["items"]) == 2
|
|
|
|
|
|
async def test_add_books_to_nonexistent_shelf(
|
|
authenticated_client: AsyncClient,
|
|
) -> None:
|
|
"""Verify 404 when adding to nonexistent shelf."""
|
|
response = await authenticated_client.post(
|
|
"/shelves/99999/books", params={"book_ids": [1, 2]}
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
|
|
async def test_add_nonexistent_books(authenticated_client: AsyncClient) -> None:
|
|
"""Verify appropriate error when book IDs don't exist."""
|
|
shelf_data = {"title": "Test Shelf"}
|
|
shelf_response = await authenticated_client.post("/shelves", json=shelf_data)
|
|
shelf_id = shelf_response.json()["id"]
|
|
|
|
response = await authenticated_client.post(
|
|
f"/shelves/{shelf_id}/books", params={"book_ids": [99999, 99998]}
|
|
)
|
|
|
|
assert response.status_code == 400
|
|
|
|
|
|
async def test_remove_books_from_shelf(
|
|
populated_authenticated_client: AsyncClient,
|
|
) -> None:
|
|
"""Successfully remove books from a shelf."""
|
|
shelf_data = {"title": "Test Shelf"}
|
|
shelf_response = await populated_authenticated_client.post(
|
|
"/shelves", json=shelf_data
|
|
)
|
|
shelf_id = shelf_response.json()["id"]
|
|
|
|
# Add books first
|
|
book_ids = [1, 2, 3]
|
|
await populated_authenticated_client.post(
|
|
f"/shelves/{shelf_id}/books", params={"book_ids": book_ids}
|
|
)
|
|
|
|
# Remove one book
|
|
response = await populated_authenticated_client.delete(
|
|
f"/shelves/{shelf_id}/books", params={"book_ids": [1]}
|
|
)
|
|
assert response.status_code == 200
|
|
|
|
# Verify by listing books
|
|
books_response = await populated_authenticated_client.get(
|
|
"/books", params={"shelves": shelf_id}
|
|
)
|
|
|
|
|
|
assert books_response.status_code == 200
|
|
assert books_response.json()["total"] == 2
|
|
|
|
|
|
async def test_remove_books_from_nonexistent_shelf(
|
|
authenticated_client: AsyncClient,
|
|
) -> None:
|
|
"""Verify 404 when removing from nonexistent shelf."""
|
|
response = await authenticated_client.delete(
|
|
"/shelves/99999/books", params={"book_ids": [1, 2]}
|
|
)
|
|
assert response.status_code == 404
|
|
|
|
|
|
async def test_remove_nonexistent_books(
|
|
populated_authenticated_client: AsyncClient,
|
|
) -> None:
|
|
"""Verify appropriate error handling when removing nonexistent books."""
|
|
shelf_data = {"title": "Test Shelf"}
|
|
shelf_response = await populated_authenticated_client.post(
|
|
"/shelves", json=shelf_data
|
|
)
|
|
shelf_id = shelf_response.json()["id"]
|
|
|
|
# Add a book first
|
|
await populated_authenticated_client.post(
|
|
f"/shelves/{shelf_id}/books", params={"book_ids": [1]}
|
|
)
|
|
|
|
# Try to remove books that don't exist on shelf
|
|
response = await populated_authenticated_client.delete(
|
|
f"/shelves/{shelf_id}/books", params={"book_ids": [99999]}
|
|
)
|
|
|
|
# Idempotent behaviour
|
|
assert response.status_code == 200
|
|
|
|
|
|
async def test_add_duplicate_books(populated_authenticated_client: AsyncClient) -> None:
|
|
"""Verify behavior when adding books already on shelf."""
|
|
shelf_data = {"title": "Test Shelf"}
|
|
shelf_response = await populated_authenticated_client.post(
|
|
"/shelves", json=shelf_data
|
|
)
|
|
shelf_id = shelf_response.json()["id"]
|
|
|
|
# Add books
|
|
book_ids = [1, 2]
|
|
await populated_authenticated_client.post(
|
|
f"/shelves/{shelf_id}/books", params={"book_ids": book_ids}
|
|
)
|
|
|
|
# Try to add some of the same books again
|
|
response = await populated_authenticated_client.post(
|
|
f"/shelves/{shelf_id}/books", params={"book_ids": [1, 2, 3]}
|
|
)
|
|
|
|
assert response.status_code == 201
|
|
|
|
# Verify final state
|
|
books_response = await populated_authenticated_client.get(
|
|
"/books", params={"shelves": shelf_id}
|
|
)
|
|
result = books_response.json()["items"]
|
|
|
|
assert len(result) == 3
|
|
|
|
|
|
async def test_create_shelf_with_empty_title(authenticated_client: AsyncClient) -> None:
|
|
"""Verify validation rejects empty shelf titles."""
|
|
shelf_data = {"title": ""}
|
|
response = await authenticated_client.post("/shelves", json=shelf_data)
|
|
assert response.status_code == 400
|
|
assert "title" in response.text.lower()
|
|
|
|
|
|
async def test_create_shelf_with_whitespace_only_title(
|
|
authenticated_client: AsyncClient,
|
|
) -> None:
|
|
"""Verify validation rejects whitespace-only titles."""
|
|
shelf_data = {"title": " "}
|
|
response = await authenticated_client.post("/shelves", json=shelf_data)
|
|
assert response.status_code == 400
|