porchlight/tests/test_manage_profile.py

132 lines
4.3 KiB
Python

from datetime import UTC, datetime
from argon2 import PasswordHasher
from httpx import AsyncClient
from porchlight.authn.password import PasswordService
from porchlight.models import PasswordCredential, User
from tests.conftest import get_csrf_token
async def _login(client: AsyncClient, username: str = "alice", userid: str = "user-01") -> None:
app = client._transport.app # type: ignore[union-attr]
user_repo = app.state.user_repo
cred_repo = app.state.credential_repo
user = await user_repo.get_by_username(username)
if user is None:
user = User(
userid=userid,
username=username,
created_at=datetime.now(UTC),
updated_at=datetime.now(UTC),
)
await user_repo.create(user)
svc = PasswordService(hasher=PasswordHasher(time_cost=1, memory_cost=8192))
existing = await cred_repo.get_password_by_user(user.userid)
if existing is None:
await cred_repo.create_password(PasswordCredential(user_id=user.userid, password_hash=svc.hash("password")))
token = await get_csrf_token(client)
await client.post(
"/login/password",
data={"username": username, "password": "password"},
headers={"HX-Request": "true", "X-CSRF-Token": token},
)
async def test_update_profile_success(client: AsyncClient) -> None:
await _login(client)
token = await get_csrf_token(client)
response = await client.post(
"/manage/profile",
data={
"given_name": "Alice",
"family_name": "Smith",
"preferred_username": "ally",
"email": "alice@example.com",
"phone_number": "+46701234567",
"picture": "https://example.com/alice.jpg",
"locale": "en",
},
headers={"X-CSRF-Token": token},
)
assert response.status_code == 200
assert "Profile updated" in response.text
app = client._transport.app # type: ignore[union-attr]
user = await app.state.user_repo.get_by_userid("user-01")
assert user is not None
assert user.given_name == "Alice"
assert user.email == "alice@example.com"
assert user.phone_number == "+46701234567"
async def test_update_profile_invalid_email(client: AsyncClient) -> None:
await _login(client)
token = await get_csrf_token(client)
response = await client.post(
"/manage/profile",
data={"email": "not-an-email"},
headers={"X-CSRF-Token": token},
)
assert response.status_code == 200
assert "alert" in response.text
assert "email" in response.text.lower()
async def test_update_profile_invalid_phone(client: AsyncClient) -> None:
await _login(client)
token = await get_csrf_token(client)
response = await client.post(
"/manage/profile",
data={"phone_number": "not-a-phone"},
headers={"X-CSRF-Token": token},
)
assert response.status_code == 200
assert "alert" in response.text
assert "phone" in response.text.lower()
async def test_update_profile_phone_normalized(client: AsyncClient) -> None:
await _login(client)
token = await get_csrf_token(client)
response = await client.post(
"/manage/profile",
data={"phone_number": "+46 70 123 45 67"},
headers={"X-CSRF-Token": token},
)
assert response.status_code == 200
assert "Profile updated" in response.text
app = client._transport.app # type: ignore[union-attr]
user = await app.state.user_repo.get_by_userid("user-01")
assert user is not None
assert user.phone_number == "+46701234567"
async def test_update_profile_invalid_picture(client: AsyncClient) -> None:
await _login(client)
token = await get_csrf_token(client)
response = await client.post(
"/manage/profile",
data={"picture": "not-a-url"},
headers={"X-CSRF-Token": token},
)
assert response.status_code == 200
assert "alert" in response.text
assert "Picture URL" in response.text
async def test_update_profile_field_too_long(client: AsyncClient) -> None:
await _login(client)
token = await get_csrf_token(client)
response = await client.post(
"/manage/profile",
data={"given_name": "x" * 256},
headers={"X-CSRF-Token": token},
)
assert response.status_code == 200
assert "alert" in response.text
assert "Given name" in response.text