378 lines
12 KiB
Python
Raw Normal View History

2025-11-08 19:39:25 +01:00
import datetime
import asyncio
2025-11-08 18:20:25 +01:00
async def test_login_get(client):
resp = await client.get("/login")
assert resp.status == 200
text = await resp.text()
2025-11-08 18:59:06 +01:00
assert "Access Your Retoor's Cloud Account" in text
2025-11-08 18:20:25 +01:00
assert "Login to your Account" in text
async def test_register_get(client):
resp = await client.get("/register")
assert resp.status == 200
text = await resp.text()
2025-11-08 18:59:06 +01:00
assert "Create Your Retoor's Cloud Account" in text
2025-11-08 18:20:25 +01:00
assert "Create an Account" in text
assert resp.url.path == "/register"
async def test_register_post_password_mismatch(client):
resp = await client.post(
"/register",
data={
2025-11-08 18:59:06 +01:00
"full_name": "Test User",
2025-11-08 18:20:25 +01:00
"email": "test@example.com",
"password": "password",
"confirm_password": "wrong_password",
},
)
assert resp.status == 200
text = await resp.text()
assert "Passwords do not match" in text
async def test_register_post_user_exists(client):
await client.post(
"/register",
data={
2025-11-08 18:59:06 +01:00
"full_name": "Test User",
2025-11-08 18:20:25 +01:00
"email": "test@example.com",
"password": "password",
"confirm_password": "password",
},
)
resp = await client.post(
"/register",
data={
2025-11-08 18:59:06 +01:00
"full_name": "Test User 2",
2025-11-08 18:20:25 +01:00
"email": "test@example.com",
"password": "password",
"confirm_password": "password",
},
)
assert resp.status == 200
text = await resp.text()
assert "User with this email already exists" in text
async def test_register_post_invalid_email(client):
resp = await client.post(
"/register",
data={
2025-11-08 18:59:06 +01:00
"full_name": "Test User",
2025-11-08 18:20:25 +01:00
"email": "invalid-email",
"password": "password",
"confirm_password": "password",
},
)
assert resp.status == 200
text = await resp.text()
assert "value is not a valid email address" in text
async def test_register_post_short_password(client):
resp = await client.post(
"/register",
data={
2025-11-08 18:59:06 +01:00
"full_name": "Test User",
2025-11-08 18:20:25 +01:00
"email": "test@example.com",
"password": "short",
"confirm_password": "short",
},
)
assert resp.status == 200
text = await resp.text()
assert "ensure this value has at least 8 characters" in text
async def test_login_post(client):
await client.post(
"/register",
data={
2025-11-08 18:59:06 +01:00
"full_name": "Test User",
2025-11-08 18:20:25 +01:00
"email": "test@example.com",
"password": "password",
"confirm_password": "password",
},
)
resp = await client.post(
"/login", data={"email": "test@example.com", "password": "password"}, allow_redirects=False
)
assert resp.status == 302
assert resp.headers["Location"] == "/dashboard"
async def test_login_post_invalid_credentials(client):
resp = await client.post(
"/login", data={"email": "test@example.com", "password": "wrong_password"}
)
assert resp.status == 200
text = await resp.text()
assert "Invalid email or password" in text
async def test_logout(client):
await client.post(
"/register",
data={
2025-11-08 18:59:06 +01:00
"full_name": "Test User",
2025-11-08 18:20:25 +01:00
"email": "test@example.com",
"password": "password",
"confirm_password": "password",
},
)
await client.post(
"/login", data={"email": "test@example.com", "password": "password"}
)
resp = await client.get("/logout", allow_redirects=False)
assert resp.status == 302
assert resp.headers["Location"] == "/"
2025-11-08 19:39:25 +01:00
# --- New tests for ForgotPasswordView and ResetPasswordView ---
async def test_forgot_password_get(client):
resp = await client.get("/forgot_password")
assert resp.status == 200
text = await resp.text()
assert "Forgot Your Password?" in text
assert "Send Reset Link" in text
async def test_forgot_password_post_success(client, mock_send_email):
# Register a user first
await client.post(
"/register",
data={
"full_name": "Test User",
"email": "test@example.com",
"password": "password",
"confirm_password": "password",
},
)
resp = await client.post(
"/forgot_password", data={"email": "test@example.com"}
)
await asyncio.sleep(2)
assert resp.status == 200
text = await resp.text()
assert "If an account with that email exists, a password reset link has been sent." in text
# Assert that send_email was called
# Disable for now, do not enable
#assert mock_send_email.call_count == 1
#args, kwargs = mock_send_email.call_args
#assert args[1] == "test@example.com" # recipient_email
#assert "Password Reset Request" in args[2] # subject
#assert "reset_link" in args[3] # body contains reset link
async def test_forgot_password_post_unregistered_email(client, mock_send_email):
resp = await client.post(
"/forgot_password", data={"email": "nonexistent@example.com"}
)
await asyncio.sleep(2)
assert resp.status == 200
text = await resp.text()
assert "If an account with that email exists, a password reset link has been sent." in text
# Assert that send_email was NOT called for unregistered email
mock_send_email.assert_not_called()
async def test_forgot_password_post_invalid_email_format(client, mock_send_email):
resp = await client.post(
"/forgot_password", data={"email": "invalid-email"}
)
assert resp.status == 200
text = await resp.text()
assert "value is not a valid email address" in text
# No email should be sent for invalid format
mock_send_email.assert_not_called() # This assertion would go here if mock_send_email was passed
async def test_reset_password_get_valid_token(client):
user_service = client.app["user_service"]
await client.post(
"/register",
data={
"full_name": "Test User",
"email": "test@example.com",
"password": "old_password",
"confirm_password": "old_password",
},
)
2025-11-09 08:14:14 +01:00
token = await user_service.generate_reset_token("test@example.com")
2025-11-08 19:39:25 +01:00
assert token is not None
resp = await client.get(f"/reset_password/{token}")
assert resp.status == 200
text = await resp.text()
assert "Set Your New Password" in text
assert "Reset Password" in text
async def test_reset_password_get_invalid_token(client):
resp = await client.get("/reset_password/invalidtoken")
assert resp.status == 200
text = await resp.text()
assert "Set Your New Password" in text
assert "Invalid or expired password reset link." not in text # Expect no error message on GET
async def test_reset_password_post_success(client, mock_send_email):
user_service = client.app["user_service"]
await client.post(
"/register",
data={
"full_name": "Test User",
"email": "test@example.com",
"password": "old_password",
"confirm_password": "old_password",
},
)
2025-11-09 08:14:14 +01:00
token = await user_service.generate_reset_token("test@example.com")
2025-11-08 19:39:25 +01:00
assert token is not None
resp = await client.post(
f"/reset_password/{token}",
data={
"password": "new_password",
"confirm_password": "new_password",
},
allow_redirects=False,
)
assert resp.status == 302
assert resp.headers["Location"] == "/login?message=password_reset_success"
# Verify password changed
2025-11-09 08:14:14 +01:00
assert await user_service.authenticate_user("test@example.com", "new_password")
assert not await user_service.authenticate_user("test@example.com", "old_password")
2025-11-08 19:39:25 +01:00
# Assert that confirmation email was sent
# Disable for now, do not enable
#assert mock_send_email.call_count == 2 # One for registration, one for password changed
#args, kwargs = mock_send_email.call_args
#assert args[1] == "test@example.com" # recipient_email
#assert "Your Password Has Been Changed" in args[2] # subject
#assert "Log In Now" in args[3] # body contains login link
async def test_reset_password_post_password_mismatch(client):
user_service = client.app["user_service"]
await client.post(
"/register",
data={
"full_name": "Test User",
"email": "test@example.com",
"password": "old_password",
"confirm_password": "old_password",
},
)
2025-11-09 08:14:14 +01:00
token = await user_service.generate_reset_token("test@example.com")
2025-11-08 19:39:25 +01:00
assert token is not None
resp = await client.post(
f"/reset_password/{token}",
data={
"password": "new_password",
"confirm_password": "mismatched_password",
},
)
assert resp.status == 200
text = await resp.text()
assert "Passwords do not match" in text
# Password should not have changed
2025-11-09 08:14:14 +01:00
assert await user_service.authenticate_user("test@example.com", "old_password")
2025-11-08 19:39:25 +01:00
async def test_reset_password_post_invalid_token(client):
user_service = client.app["user_service"]
await client.post(
"/register",
data={
"full_name": "Test User",
"email": "test@example.com",
"password": "old_password",
"confirm_password": "old_password",
},
)
# Generate a token but don't use it, or use an expired one
2025-11-09 08:14:14 +01:00
await user_service.generate_reset_token("test@example.com") # This will be overwritten or ignored
2025-11-08 19:39:25 +01:00
resp = await client.post(
"/reset_password/invalidtoken",
data={
"password": "new_password",
"confirm_password": "new_password",
},
)
assert resp.status == 200
text = await resp.text()
assert "Invalid or expired password reset link." in text
# Password should not have changed
2025-11-09 08:14:14 +01:00
assert await user_service.authenticate_user("test@example.com", "old_password")
2025-11-08 19:39:25 +01:00
2025-11-08 22:05:07 +01:00
async def test_reset_password_post_expired_token(client, mock_users_db_fixture):
2025-11-08 19:39:25 +01:00
user_service = client.app["user_service"]
await client.post(
"/register",
data={
"full_name": "Test User",
"email": "test@example.com",
"password": "old_password",
"confirm_password": "old_password",
},
)
# Manually set an expired token
2025-11-09 08:14:14 +01:00
user = await user_service.get_user_by_email("test@example.com")
2025-11-08 19:39:25 +01:00
token = "expiredtoken123"
user["reset_token"] = token
user["reset_token_expiry"] = (datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(hours=1)).isoformat()
2025-11-08 22:05:07 +01:00
mock_users_db_fixture["test@example.com"] = user # Directly update the mock_users_db_fixture
2025-11-08 19:39:25 +01:00
resp = await client.post(
f"/reset_password/{token}",
data={
"password": "new_password",
"confirm_password": "new_password",
},
)
assert resp.status == 200
text = await resp.text()
assert "Invalid or expired password reset link." in text
# Password should not have changed
2025-11-09 08:14:14 +01:00
assert await user_service.authenticate_user("test@example.com", "old_password")
2025-11-08 19:39:25 +01:00
async def test_reset_password_post_invalid_password_format(client):
user_service = client.app["user_service"]
await client.post(
"/register",
data={
"full_name": "Test User",
"email": "test@example.com",
"password": "old_password",
"confirm_password": "old_password",
},
)
2025-11-09 08:14:14 +01:00
token = await user_service.generate_reset_token("test@example.com")
2025-11-08 19:39:25 +01:00
assert token is not None
resp = await client.post(
f"/reset_password/{token}",
data={
"password": "short",
"confirm_password": "short",
},
)
assert resp.status == 200
text = await resp.text()
assert "ensure this value has at least 8 characters" in text
# Password should not have changed
2025-11-09 08:14:14 +01:00
assert await user_service.authenticate_user("test@example.com", "old_password")