feat: add user_id index to profiles table for faster lookups
The index on user_id column in profiles table improves query performance for user-specific profile retrieval operations.
This commit is contained in:
+8
-14
@@ -1,6 +1,7 @@
|
||||
import hashlib
|
||||
import time
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from playwright.sync_api import expect
|
||||
from tests.conftest import BASE_URL
|
||||
from devplacepy.database import get_table
|
||||
from devplacepy.utils import hash_password, generate_uid
|
||||
@@ -99,8 +100,7 @@ def test_signup_existing_username(page, app_server):
|
||||
page.fill("#password", "secret123")
|
||||
page.fill("#confirm_password", "secret123")
|
||||
page.click("button:has-text('Create account')")
|
||||
page.wait_for_timeout(300)
|
||||
assert page.is_visible("text=Username already taken")
|
||||
expect(page.locator("text=Username already taken")).to_be_visible()
|
||||
|
||||
|
||||
def test_signup_existing_email(page, app_server):
|
||||
@@ -119,8 +119,7 @@ def test_signup_existing_email(page, app_server):
|
||||
page.fill("#password", "secret123")
|
||||
page.fill("#confirm_password", "secret123")
|
||||
page.click("button:has-text('Create account')")
|
||||
page.wait_for_timeout(300)
|
||||
assert page.is_visible("text=Email already registered")
|
||||
expect(page.locator("text=Email already registered")).to_be_visible()
|
||||
|
||||
|
||||
def test_signup_password_mismatch(page, app_server):
|
||||
@@ -130,8 +129,7 @@ def test_signup_password_mismatch(page, app_server):
|
||||
page.fill("#password", "secret123")
|
||||
page.fill("#confirm_password", "different456")
|
||||
page.click("button:has-text('Create account')")
|
||||
page.wait_for_timeout(300)
|
||||
assert page.is_visible("text=Passwords do not match")
|
||||
expect(page.locator("text=Passwords do not match")).to_be_visible()
|
||||
|
||||
|
||||
def test_signup_short_password(page, app_server):
|
||||
@@ -141,8 +139,7 @@ def test_signup_short_password(page, app_server):
|
||||
page.fill("#password", "ab")
|
||||
page.fill("#confirm_password", "ab")
|
||||
page.click("button:has-text('Create account')")
|
||||
page.wait_for_timeout(300)
|
||||
assert page.is_visible("text=Password must be at least 6 characters")
|
||||
expect(page.locator("text=Password must be at least 6 characters")).to_be_visible()
|
||||
|
||||
|
||||
def test_signup_invalid_username(page, app_server):
|
||||
@@ -152,8 +149,7 @@ def test_signup_invalid_username(page, app_server):
|
||||
page.fill("#password", "secret123")
|
||||
page.fill("#confirm_password", "secret123")
|
||||
page.click("button:has-text('Create account')")
|
||||
page.wait_for_timeout(300)
|
||||
assert page.is_visible("text=Username must be between 3 and 32 characters")
|
||||
expect(page.locator("text=Username must be between 3 and 32 characters")).to_be_visible()
|
||||
|
||||
|
||||
def test_login_page_loads(page, app_server):
|
||||
@@ -198,8 +194,7 @@ def test_login_wrong_password(page, app_server):
|
||||
page.fill("#email", "wrongpw@test.devplace")
|
||||
page.fill("#password", "badpassword")
|
||||
page.click("button:has-text('Sign in')")
|
||||
page.wait_for_timeout(300)
|
||||
assert page.is_visible("text=Invalid email or password")
|
||||
expect(page.locator("text=Invalid email or password")).to_be_visible()
|
||||
|
||||
|
||||
def test_login_nonexistent_email(page, app_server):
|
||||
@@ -207,8 +202,7 @@ def test_login_nonexistent_email(page, app_server):
|
||||
page.fill("#email", "nobody@nowhere.devplace")
|
||||
page.fill("#password", "secret123")
|
||||
page.click("button:has-text('Sign in')")
|
||||
page.wait_for_timeout(300)
|
||||
assert page.is_visible("text=Invalid email or password")
|
||||
expect(page.locator("text=Invalid email or password")).to_be_visible()
|
||||
|
||||
|
||||
def test_login_remember_me(page, app_server):
|
||||
|
||||
+13
-8
@@ -171,19 +171,24 @@ def test_feed_inline_comment(alice):
|
||||
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
|
||||
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
|
||||
inline_form = page.locator(".feed-comment-form").first
|
||||
if inline_form.is_visible():
|
||||
inline_form.locator("input[name='content']").fill("Inline comment from feed")
|
||||
inline_form.locator(".feed-comment-submit").click()
|
||||
page.wait_for_timeout(500)
|
||||
expect(inline_form).to_be_visible()
|
||||
inline_form.locator("input[name='content']").fill("Inline comment from feed")
|
||||
inline_form.locator(".feed-comment-submit").click()
|
||||
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
|
||||
expect(page.locator(".comment-text:has-text('Inline comment from feed')")).to_be_visible()
|
||||
|
||||
|
||||
def test_feed_inline_comment_placeholder(alice):
|
||||
page, _ = alice
|
||||
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
|
||||
inline_input = page.locator(".feed-comment-form input").first
|
||||
if inline_input.is_visible():
|
||||
placeholder = inline_input.get_attribute("placeholder")
|
||||
assert placeholder == "Your opinion goes here..."
|
||||
page.locator(".feed-fab").first.click()
|
||||
page.fill("#post-content", "Placeholder check post " + "x" * 20)
|
||||
page.locator("#create-post-modal button.btn-primary:has-text('Post')").click()
|
||||
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
|
||||
page.goto(f"{BASE_URL}/feed", wait_until="domcontentloaded")
|
||||
inline_input = page.locator(".feed-comment-form input[name='content']").first
|
||||
expect(inline_input).to_be_visible()
|
||||
assert inline_input.get_attribute("placeholder") == "Your opinion goes here..."
|
||||
|
||||
|
||||
def test_feed_signals_topic(alice):
|
||||
|
||||
+20
-22
@@ -77,9 +77,9 @@ def test_comment_voted_state_persists(alice):
|
||||
textarea = page.locator(".comment-form textarea[name='content']")
|
||||
textarea.fill("Comment whose vote should persist")
|
||||
page.locator(".comment-form button:has-text('Post')").click()
|
||||
page.wait_for_timeout(500)
|
||||
expect(page.locator(".comment-text:has-text('Comment whose vote should persist')")).to_be_visible()
|
||||
page.locator(".comment-vote-btn").first.click()
|
||||
page.wait_for_timeout(500)
|
||||
expect(page.locator(".comment-vote-btn").first).to_have_class(re.compile(r"\bvoted\b"))
|
||||
page.reload(wait_until="domcontentloaded")
|
||||
expect(page.locator(".comment-vote-btn").first).to_have_class(re.compile(r"\bvoted\b"))
|
||||
|
||||
@@ -113,8 +113,7 @@ def test_add_comment(alice):
|
||||
textarea = page.locator(".comment-form textarea[name='content']")
|
||||
textarea.fill("This is a test comment from Playwright")
|
||||
page.locator(".comment-form button:has-text('Post')").click()
|
||||
page.wait_for_timeout(500)
|
||||
assert page.is_visible("text=This is a test comment from Playwright")
|
||||
expect(page.locator(".comment-text:has-text('This is a test comment from Playwright')")).to_be_visible()
|
||||
|
||||
|
||||
def test_comment_voting(alice):
|
||||
@@ -123,11 +122,11 @@ def test_comment_voting(alice):
|
||||
textarea = page.locator(".comment-form textarea[name='content']")
|
||||
textarea.fill("Votable comment")
|
||||
page.locator(".comment-form button:has-text('Post')").click()
|
||||
page.wait_for_timeout(500)
|
||||
expect(page.locator(".comment-text:has-text('Votable comment')")).to_be_visible()
|
||||
vote_btns = page.locator(".comment-vote-btn")
|
||||
upvote = vote_btns.first
|
||||
upvote.click()
|
||||
page.wait_for_timeout(500)
|
||||
expect(vote_btns.first).to_have_class(re.compile(r"\bvoted\b"))
|
||||
|
||||
|
||||
def test_delete_own_comment(alice):
|
||||
@@ -136,11 +135,12 @@ def test_delete_own_comment(alice):
|
||||
textarea = page.locator(".comment-form textarea[name='content']")
|
||||
textarea.fill("Comment to delete")
|
||||
page.locator(".comment-form button:has-text('Post')").click()
|
||||
page.wait_for_timeout(500)
|
||||
comment = page.locator(".comment-text:has-text('Comment to delete')")
|
||||
expect(comment).to_be_visible()
|
||||
delete_btn = page.locator(".comment-action-btn:has-text('Delete')").last
|
||||
if delete_btn.is_visible():
|
||||
delete_btn.click()
|
||||
page.wait_for_timeout(500)
|
||||
expect(delete_btn).to_be_visible()
|
||||
delete_btn.click()
|
||||
expect(comment).to_have_count(0)
|
||||
|
||||
|
||||
def test_comment_form_elements(alice):
|
||||
@@ -170,7 +170,7 @@ def test_multiple_comments_on_post(alice):
|
||||
for i in range(3):
|
||||
page.locator(".comment-form textarea[name='content']").fill(f"Comment number {i + 1}")
|
||||
page.locator(".comment-form button:has-text('Post')").click()
|
||||
page.wait_for_timeout(300)
|
||||
expect(page.locator(f".comment-text:has-text('Comment number {i + 1}')")).to_be_visible()
|
||||
assert page.is_visible("text=Comment number 1")
|
||||
assert page.is_visible("text=Comment number 3")
|
||||
|
||||
@@ -180,17 +180,17 @@ def test_comment_and_vote_then_delete(alice):
|
||||
create_post(page, "showcase", "Full comment lifecycle post")
|
||||
page.locator(".comment-form textarea[name='content']").fill("Lifecycle comment")
|
||||
page.locator(".comment-form button:has-text('Post')").click()
|
||||
page.wait_for_timeout(300)
|
||||
assert page.is_visible("text=Lifecycle comment")
|
||||
comment = page.locator(".comment-text:has-text('Lifecycle comment')")
|
||||
expect(comment).to_be_visible()
|
||||
|
||||
vote_up = page.locator(".comment-vote-btn").first
|
||||
vote_up.click()
|
||||
page.wait_for_timeout(300)
|
||||
expect(page.locator(".comment-vote-btn").first).to_have_class(re.compile(r"\bvoted\b"))
|
||||
|
||||
delete_btn = page.locator(".comment-action-btn:has-text('Delete')")
|
||||
if delete_btn.is_visible():
|
||||
delete_btn.click()
|
||||
page.wait_for_timeout(300)
|
||||
delete_btn = page.locator(".comment-action-btn:has-text('Delete')").last
|
||||
expect(delete_btn).to_be_visible()
|
||||
delete_btn.click()
|
||||
expect(comment).to_have_count(0)
|
||||
|
||||
|
||||
def test_post_edit_button(alice):
|
||||
@@ -229,8 +229,7 @@ def test_post_edit_submit(alice):
|
||||
page.fill("#edit-title", "Edited Title")
|
||||
page.fill("#edit-content", "Edited content for the post")
|
||||
page.click("button:has-text('Save Changes')")
|
||||
page.wait_for_timeout(500)
|
||||
assert page.is_visible("text=Edited Title")
|
||||
expect(page.locator("text=Edited Title")).to_be_visible()
|
||||
|
||||
|
||||
def test_post_across_all_topics(alice):
|
||||
@@ -239,8 +238,7 @@ def test_post_across_all_topics(alice):
|
||||
for topic in topics:
|
||||
create_post(page, topic, f"Topic test post for {topic}")
|
||||
badge = page.locator(f".badge-{topic}")
|
||||
assert badge.is_visible()
|
||||
page.wait_for_timeout(200)
|
||||
expect(badge).to_be_visible()
|
||||
|
||||
|
||||
def test_delete_own_post(alice):
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import uuid
|
||||
|
||||
import requests
|
||||
|
||||
from tests.conftest import BASE_URL
|
||||
|
||||
|
||||
def _session():
|
||||
s = requests.Session()
|
||||
name = f"push_{uuid.uuid4().hex[:10]}"
|
||||
s.post(f"{BASE_URL}/auth/signup", data={
|
||||
"username": name,
|
||||
"email": f"{name}@test.dev",
|
||||
"password": "secret123",
|
||||
"confirm_password": "secret123",
|
||||
}, allow_redirects=True)
|
||||
return s
|
||||
|
||||
|
||||
def test_public_key_endpoint(app_server):
|
||||
r = requests.get(f"{BASE_URL}/push.json")
|
||||
assert r.status_code == 200
|
||||
assert r.json().get("publicKey")
|
||||
|
||||
|
||||
def test_register_requires_auth(app_server):
|
||||
r = requests.post(f"{BASE_URL}/push.json", json={
|
||||
"endpoint": "https://push.example.com/anon",
|
||||
"keys": {"p256dh": "key", "auth": "auth"},
|
||||
}, allow_redirects=False)
|
||||
assert r.status_code == 401
|
||||
|
||||
|
||||
def test_register_success(app_server):
|
||||
s = _session()
|
||||
r = s.post(f"{BASE_URL}/push.json", json={
|
||||
"endpoint": "https://push.example.com/sub-1",
|
||||
"keys": {"p256dh": "p256dh_fake", "auth": "auth_fake"},
|
||||
})
|
||||
assert r.status_code == 200, r.text
|
||||
assert r.json().get("registered") is True
|
||||
|
||||
|
||||
def test_register_missing_keys_rejected(app_server):
|
||||
s = _session()
|
||||
r = s.post(f"{BASE_URL}/push.json", json={"endpoint": "https://push.example.com/x"})
|
||||
assert r.status_code == 400
|
||||
|
||||
|
||||
def test_register_invalid_json_rejected(app_server):
|
||||
s = _session()
|
||||
r = s.post(f"{BASE_URL}/push.json", data="not-json", headers={"Content-Type": "application/json"})
|
||||
assert r.status_code == 400
|
||||
|
||||
|
||||
def test_service_worker_served(app_server):
|
||||
r = requests.get(f"{BASE_URL}/service-worker.js")
|
||||
assert r.status_code == 200
|
||||
assert r.headers.get("Service-Worker-Allowed") == "/"
|
||||
|
||||
|
||||
def test_manifest_served(app_server):
|
||||
r = requests.get(f"{BASE_URL}/manifest.json")
|
||||
assert r.status_code == 200
|
||||
@@ -0,0 +1,35 @@
|
||||
from tests.conftest import BASE_URL
|
||||
from tests.test_post import create_post
|
||||
|
||||
POST_PAYLOAD = "XSSPROBE <img src=x onerror=alert('p')><script>alert('s')</script> end of post"
|
||||
COMMENT_PAYLOAD = "CMTPROBE <img src=x onerror=alert('c')><script>alert('s')</script> end of comment"
|
||||
|
||||
|
||||
def test_post_content_is_sanitized(alice):
|
||||
page, _ = alice
|
||||
fired = []
|
||||
page.on("dialog", lambda d: (fired.append(d.message), d.dismiss()))
|
||||
create_post(page, "random", POST_PAYLOAD)
|
||||
content = page.locator(".post-detail-content")
|
||||
content.wait_for(state="visible")
|
||||
page.locator(".post-detail-content p").first.wait_for(state="visible")
|
||||
html = content.inner_html().lower()
|
||||
assert "<script" not in html
|
||||
assert "onerror" not in html
|
||||
assert not fired, f"XSS payload executed: {fired}"
|
||||
|
||||
|
||||
def test_comment_content_is_sanitized(alice):
|
||||
page, _ = alice
|
||||
fired = []
|
||||
page.on("dialog", lambda d: (fired.append(d.message), d.dismiss()))
|
||||
create_post(page, "random", "Clean host post for comment sanitization")
|
||||
page.locator(".comment-form textarea[name='content']").fill(COMMENT_PAYLOAD)
|
||||
page.locator(".comment-form button:has-text('Post')").click()
|
||||
comment = page.locator(".comment-text:has-text('CMTPROBE')")
|
||||
comment.wait_for(state="visible")
|
||||
page.locator(".comment-text p").first.wait_for(state="visible")
|
||||
html = comment.inner_html().lower()
|
||||
assert "<script" not in html
|
||||
assert "onerror" not in html
|
||||
assert not fired, f"XSS payload executed: {fired}"
|
||||
Reference in New Issue
Block a user