Initial commit: Pole Dance Championships App
Full-stack mobile app for pole dance championship management. Backend: FastAPI + SQLAlchemy 2 (async) + SQLite (dev) / PostgreSQL (prod) - JWT auth with refresh token rotation - Championship CRUD with Instagram Graph API sync (APScheduler) - Registration flow with status management - Participant list publish with Expo push notifications - Alembic migrations, pytest test suite Mobile: React Native + Expo (TypeScript) - Auth gate: pending approval screen for new members - Championships list & detail screens - Registration form with status tracking - React Query + Zustand + React Navigation v6 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
89
backend/tests/test_auth.py
Normal file
89
backend/tests/test_auth.py
Normal file
@@ -0,0 +1,89 @@
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_register_and_login(client):
|
||||
# Register
|
||||
res = await client.post(
|
||||
"/api/v1/auth/register",
|
||||
json={
|
||||
"email": "test@example.com",
|
||||
"password": "secret123",
|
||||
"full_name": "Test User",
|
||||
},
|
||||
)
|
||||
assert res.status_code == 201
|
||||
tokens = res.json()
|
||||
assert "access_token" in tokens
|
||||
assert "refresh_token" in tokens
|
||||
|
||||
# Duplicate registration should fail
|
||||
res2 = await client.post(
|
||||
"/api/v1/auth/register",
|
||||
json={
|
||||
"email": "test@example.com",
|
||||
"password": "secret123",
|
||||
"full_name": "Test User",
|
||||
},
|
||||
)
|
||||
assert res2.status_code == 409
|
||||
|
||||
# Login with correct credentials
|
||||
res3 = await client.post(
|
||||
"/api/v1/auth/login",
|
||||
json={"email": "test@example.com", "password": "secret123"},
|
||||
)
|
||||
assert res3.status_code == 200
|
||||
|
||||
# Login with wrong password
|
||||
res4 = await client.post(
|
||||
"/api/v1/auth/login",
|
||||
json={"email": "test@example.com", "password": "wrong"},
|
||||
)
|
||||
assert res4.status_code == 401
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_me_requires_auth(client):
|
||||
res = await client.get("/api/v1/auth/me")
|
||||
assert res.status_code in (401, 403) # missing Authorization header
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_pending_user_cannot_access_championships(client):
|
||||
await client.post(
|
||||
"/api/v1/auth/register",
|
||||
json={"email": "pending@example.com", "password": "pw", "full_name": "Pending"},
|
||||
)
|
||||
login = await client.post(
|
||||
"/api/v1/auth/login",
|
||||
json={"email": "pending@example.com", "password": "pw"},
|
||||
)
|
||||
token = login.json()["access_token"]
|
||||
res = await client.get(
|
||||
"/api/v1/championships",
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
)
|
||||
assert res.status_code == 403
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_token_refresh(client):
|
||||
await client.post(
|
||||
"/api/v1/auth/register",
|
||||
json={"email": "refresh@example.com", "password": "pw", "full_name": "Refresh"},
|
||||
)
|
||||
login = await client.post(
|
||||
"/api/v1/auth/login",
|
||||
json={"email": "refresh@example.com", "password": "pw"},
|
||||
)
|
||||
refresh_token = login.json()["refresh_token"]
|
||||
|
||||
res = await client.post("/api/v1/auth/refresh", json={"refresh_token": refresh_token})
|
||||
assert res.status_code == 200
|
||||
new_tokens = res.json()
|
||||
assert "access_token" in new_tokens
|
||||
|
||||
# Old refresh token should now be revoked
|
||||
res2 = await client.post("/api/v1/auth/refresh", json={"refresh_token": refresh_token})
|
||||
assert res2.status_code == 401
|
||||
Reference in New Issue
Block a user