130 lines
4.0 KiB
Python
130 lines
4.0 KiB
Python
|
|
"""
|
||
|
|
Tests for the /api/v1/pages/* endpoints.
|
||
|
|
"""
|
||
|
|
import pytest
|
||
|
|
from httpx import AsyncClient
|
||
|
|
|
||
|
|
pytestmark = pytest.mark.asyncio
|
||
|
|
|
||
|
|
|
||
|
|
async def test_list_pages_empty(client: AsyncClient):
|
||
|
|
"""GET /pages returns an empty list when no pages exist."""
|
||
|
|
response = await client.get("/api/v1/pages")
|
||
|
|
assert response.status_code == 200
|
||
|
|
assert response.json() == []
|
||
|
|
|
||
|
|
|
||
|
|
async def test_create_page_without_auth(client: AsyncClient):
|
||
|
|
"""POST /pages without an auth token returns 401."""
|
||
|
|
response = await client.post(
|
||
|
|
"/api/v1/pages",
|
||
|
|
json={"title": "Test", "slug": "test", "body": "<p>Hello</p>", "published": True},
|
||
|
|
)
|
||
|
|
assert response.status_code == 401
|
||
|
|
|
||
|
|
|
||
|
|
async def test_create_page_with_auth(client: AsyncClient, admin_token: str):
|
||
|
|
"""POST /pages with a valid token creates the page and returns 201."""
|
||
|
|
response = await client.post(
|
||
|
|
"/api/v1/pages",
|
||
|
|
json={
|
||
|
|
"title": "About Us",
|
||
|
|
"slug": "about",
|
||
|
|
"body": "<p>We walk dogs.</p>",
|
||
|
|
"published": True,
|
||
|
|
},
|
||
|
|
headers={"Authorization": f"Bearer {admin_token}"},
|
||
|
|
)
|
||
|
|
assert response.status_code == 201
|
||
|
|
data = response.json()
|
||
|
|
assert data["slug"] == "about"
|
||
|
|
assert data["title"] == "About Us"
|
||
|
|
assert data["published"] is True
|
||
|
|
assert "id" in data
|
||
|
|
assert "created_at" in data
|
||
|
|
|
||
|
|
|
||
|
|
async def test_get_page_by_slug(client: AsyncClient, admin_token: str):
|
||
|
|
"""GET /pages/{slug} returns the created page."""
|
||
|
|
# Create the page first
|
||
|
|
await client.post(
|
||
|
|
"/api/v1/pages",
|
||
|
|
json={
|
||
|
|
"title": "Contact",
|
||
|
|
"slug": "contact",
|
||
|
|
"body": "<p>Contact us here.</p>",
|
||
|
|
"published": True,
|
||
|
|
},
|
||
|
|
headers={"Authorization": f"Bearer {admin_token}"},
|
||
|
|
)
|
||
|
|
|
||
|
|
response = await client.get("/api/v1/pages/contact")
|
||
|
|
assert response.status_code == 200
|
||
|
|
data = response.json()
|
||
|
|
assert data["slug"] == "contact"
|
||
|
|
assert data["title"] == "Contact"
|
||
|
|
|
||
|
|
|
||
|
|
async def test_get_page_not_found(client: AsyncClient):
|
||
|
|
"""GET /pages/{slug} for a non-existent slug returns 404."""
|
||
|
|
response = await client.get("/api/v1/pages/does-not-exist")
|
||
|
|
assert response.status_code == 404
|
||
|
|
|
||
|
|
|
||
|
|
async def test_unpublished_page_not_visible_publicly(client: AsyncClient, admin_token: str):
|
||
|
|
"""Unpublished pages are not returned by the public GET endpoints."""
|
||
|
|
await client.post(
|
||
|
|
"/api/v1/pages",
|
||
|
|
json={
|
||
|
|
"title": "Draft",
|
||
|
|
"slug": "draft",
|
||
|
|
"body": "<p>Work in progress.</p>",
|
||
|
|
"published": False,
|
||
|
|
},
|
||
|
|
headers={"Authorization": f"Bearer {admin_token}"},
|
||
|
|
)
|
||
|
|
|
||
|
|
# Should not appear in list
|
||
|
|
list_resp = await client.get("/api/v1/pages")
|
||
|
|
slugs = [p["slug"] for p in list_resp.json()]
|
||
|
|
assert "draft" not in slugs
|
||
|
|
|
||
|
|
# Should not be accessible by slug
|
||
|
|
get_resp = await client.get("/api/v1/pages/draft")
|
||
|
|
assert get_resp.status_code == 404
|
||
|
|
|
||
|
|
|
||
|
|
async def test_update_page(client: AsyncClient, admin_token: str):
|
||
|
|
"""PUT /pages/{slug} updates the page."""
|
||
|
|
await client.post(
|
||
|
|
"/api/v1/pages",
|
||
|
|
json={"title": "Old Title", "slug": "updatable", "body": "<p>Old</p>", "published": True},
|
||
|
|
headers={"Authorization": f"Bearer {admin_token}"},
|
||
|
|
)
|
||
|
|
|
||
|
|
response = await client.put(
|
||
|
|
"/api/v1/pages/updatable",
|
||
|
|
json={"title": "New Title"},
|
||
|
|
headers={"Authorization": f"Bearer {admin_token}"},
|
||
|
|
)
|
||
|
|
assert response.status_code == 200
|
||
|
|
assert response.json()["title"] == "New Title"
|
||
|
|
|
||
|
|
|
||
|
|
async def test_delete_page(client: AsyncClient, admin_token: str):
|
||
|
|
"""DELETE /pages/{slug} removes the page."""
|
||
|
|
await client.post(
|
||
|
|
"/api/v1/pages",
|
||
|
|
json={"title": "Bye", "slug": "bye", "body": "<p>bye</p>", "published": True},
|
||
|
|
headers={"Authorization": f"Bearer {admin_token}"},
|
||
|
|
)
|
||
|
|
|
||
|
|
del_resp = await client.delete(
|
||
|
|
"/api/v1/pages/bye",
|
||
|
|
headers={"Authorization": f"Bearer {admin_token}"},
|
||
|
|
)
|
||
|
|
assert del_resp.status_code == 204
|
||
|
|
|
||
|
|
get_resp = await client.get("/api/v1/pages/bye")
|
||
|
|
assert get_resp.status_code == 404
|