## Copyright © 2026 Olaf Kolkman ## SPDX-License-Identifier: GPL-3.0-or-later from fastapi.testclient import TestClient from backend.app.main import app from backend.app.services.otp_service import current_code client = TestClient(app) def test_user_config_api_and_profile_page(): login = client.post('/api/auth/login', json={ 'email': 'alice@example.com', 'password': 'secret123', }).json() headers = {'Authorization': f"Bearer {login['access_token']}"} profile_response = client.get('/api/user/me', headers=headers) assert profile_response.status_code == 200 payload = profile_response.json() assert payload['username'] == 'alice' update_response = client.put('/api/user/me', json={ 'email': 'alice@example.com', 'bio': 'Updated bio', }, headers=headers) assert update_response.status_code == 200 assert client.get('/api/user/me', headers=headers).json()['username'] == 'alice' page_response = client.get('/profile') assert page_response.status_code == 200 assert 'Profile' in page_response.text assert 'Loading...' in page_response.text assert 'name="username"' not in page_response.text assert '' in page_response.text assert 'name="avatar" type="file"' in page_response.text assert 'name="avatar_url"' not in page_response.text assert 'value="mastodon.social"' in page_response.text assert 'value="From my #LinkLog: "' in page_response.text assert 'id="auth-menu" class="auth-menu hidden"' in page_response.text assert 'id="auth-home-link" href="/">Home' in page_response.text assert 'id="auth-profile-link" class="hidden"' in page_response.text assert '' in page_response.text assert 'id="auth-avatar"' not in page_response.text assert 'name="new_password_confirmation"' in page_response.text bob_login = client.post('/api/auth/login', json={ 'email': 'bob@example.com', 'password': 'secret123', }).json() bob_headers = {'Authorization': f"Bearer {bob_login['access_token']}"} password_response = client.put('/api/user/password', json={ 'current_password': 'secret123', 'new_password': 'new-secret-123', }, headers=bob_headers) assert password_response.status_code == 200 assert client.post('/api/auth/login', json={ 'email': 'bob@example.com', 'password': 'new-secret-123', }).status_code == 200 assert client.put('/api/user/password', json={ 'current_password': 'new-secret-123', 'new_password': 'secret123', }, headers=bob_headers).status_code == 200 upload_response = client.post( '/api/user/avatar', headers=headers, files={'avatar': ('avatar.png', b'fake-png-data', 'image/png')}, ) assert upload_response.status_code == 200 avatar_url = upload_response.json()['avatar_url'] assert avatar_url.startswith('/media/user-1.png') assert client.get(avatar_url).content == b'fake-png-data' updated_profile = client.get('/api/user/me', headers=headers).json() assert updated_profile['avatar_url'] == avatar_url def test_user_can_enable_and_use_otp(): login = client.post('/api/auth/login', json={'email': 'alice@example.com', 'password': 'secret123'}).json() headers = {'Authorization': f"Bearer {login['access_token']}"} setup = client.post('/api/user/otp/setup', headers=headers) assert setup.status_code == 200 secret = setup.json()['secret'] assert setup.json()['otpauth_url'].startswith('otpauth://totp/') enabled = client.post('/api/user/otp', headers=headers, json={ 'action': 'enable', 'code': current_code(secret), }) assert enabled.status_code == 200 assert enabled.json()['enabled'] is True assert client.post('/api/auth/login', json={'email': 'alice@example.com', 'password': 'secret123'}).status_code == 401 otp_login = client.post('/api/auth/login', json={ 'email': 'alice@example.com', 'password': 'secret123', 'otp': current_code(secret), }) assert otp_login.status_code == 200 disabled = client.post('/api/user/otp', headers=headers, json={ 'action': 'disable', 'code': current_code(secret), }) assert disabled.status_code == 200 assert disabled.json()['enabled'] is False