feat: add user_id index to profiles table for faster lookups

The migration adds a new database index on the `user_id` column of the `profiles` table to optimize query performance when filtering or joining by user identifier. This change targets the `20230614000001_add_user_id_index_to_profiles.sql` migration file, introducing a non-unique B-tree index named `idx_profiles_user_id`. The index creation uses the `IF NOT EXISTS` clause to ensure idempotency during repeated migrations.
This commit is contained in:
2026-05-27 19:06:18 +00:00
parent 950f26a420
commit eebbec8734
14 changed files with 182 additions and 32 deletions
+4 -4
View File
@@ -1,3 +1,5 @@
from playwright.sync_api import expect
from tests.conftest import BASE_URL, assert_share_copies
@@ -44,9 +46,7 @@ def test_post_vote_increment(alice):
page, _ = alice
create_post(page, "showcase", "Vote increment test")
page.locator(".post-action-btn.vote-up").first.click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
count = page.locator(".post-vote-count").first.text_content().strip()
assert count == "1", f"expected vote count 1, got {count!r}"
expect(page.locator(".post-vote-count").first).to_have_text("1")
def _profile_stars(page, username):
@@ -60,7 +60,7 @@ def test_profile_stars_reflect_content_votes(alice):
before = _profile_stars(page, user["username"])
create_post(page, "devlog", "Reputation contribution post")
page.locator(".post-action-btn.vote-up").first.click()
page.wait_for_url(f"{BASE_URL}/posts/*", wait_until="domcontentloaded")
expect(page.locator(".post-vote-count").first).to_have_text("1")
after = _profile_stars(page, user["username"])
assert after == before + 1, f"expected stars {before + 1}, got {after}"
+4 -1
View File
@@ -1,3 +1,5 @@
from playwright.sync_api import expect
from tests.conftest import BASE_URL, assert_share_copies
@@ -18,9 +20,10 @@ def test_project_vote(alice):
page, _ = alice
_create_project(page, "Votable Project")
star = "form[action*='/votes/project/'] button"
count = "form[action*='/votes/project/'] .vote-count-value"
before = int(page.locator(star).first.inner_text().strip(""))
page.locator(star).first.click()
page.wait_for_url(f"{BASE_URL}/projects/*", wait_until="domcontentloaded")
expect(page.locator(count).first).to_have_text(str(before + 1))
after = int(page.locator(star).first.inner_text().strip(""))
assert after == before + 1
+108 -3
View File
@@ -1,19 +1,25 @@
import io
import time
import re
import uuid
import requests
from PIL import Image
from tests.conftest import BASE_URL
def _session():
def _user(prefix="up"):
s = requests.Session()
name = f"up_{int(time.time() * 1000)}"
name = f"{prefix}_{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, name
def _session():
s, _ = _user()
return s
@@ -23,6 +29,12 @@ def _png_bytes():
return buf.getvalue()
def _upload(s, name="a.png"):
r = s.post(f"{BASE_URL}/uploads/upload", files={"file": (name, _png_bytes(), "image/png")})
assert r.status_code == 201, r.text
return r.json()["uid"]
def test_upload_allowed_png(app_server):
s = _session()
r = s.post(f"{BASE_URL}/uploads/upload", files={"file": ("x.png", _png_bytes(), "image/png")})
@@ -69,3 +81,96 @@ def test_delete_own_allowed_other_user_forbidden(app_server):
bob = _session()
assert bob.delete(f"{BASE_URL}/uploads/delete/{uid}").status_code == 403
assert alice.delete(f"{BASE_URL}/uploads/delete/{uid}").status_code == 200
def test_post_links_multiple_attachments(app_server):
s = _session()
u1, u2 = _upload(s), _upload(s)
r = s.post(f"{BASE_URL}/posts/create", data={
"content": "Post body with two attachments here",
"title": "attach post", "topic": "random",
"attachment_uids": f"{u1},{u2}",
}, allow_redirects=True)
assert r.status_code == 200, r.text[:300]
assert "/posts/" in r.url, r.url
assert u1 in r.text and u2 in r.text, "both attachments must be linked and displayed on the post"
def test_comment_links_multiple_attachments(app_server):
s = _session()
post = s.post(f"{BASE_URL}/posts/create", data={
"content": "Host post for comment attachments", "title": "host", "topic": "random",
}, allow_redirects=True)
target_uid = re.search(r'name="target_uid"\s+value="([^"]+)"', post.text).group(1)
u1, u2 = _upload(s), _upload(s)
r = s.post(f"{BASE_URL}/comments/create", data={
"content": "Comment with attachments",
"target_uid": target_uid, "target_type": "post",
"attachment_uids": f"{u1},{u2}",
}, allow_redirects=True)
assert r.status_code == 200, r.text[:300]
assert u1 in r.text and u2 in r.text, "both attachments must be linked and displayed on the comment"
def test_project_links_multiple_attachments(app_server):
s = _session()
u1, u2 = _upload(s), _upload(s)
r = s.post(f"{BASE_URL}/projects/create", data={
"title": "Attach Project", "description": "Project with attachments",
"project_type": "software", "platforms": "linux", "status": "In Development",
"attachment_uids": f"{u1},{u2}",
}, allow_redirects=True)
assert r.status_code == 200, r.text[:300]
assert "/projects/" in r.url, r.url
assert u1 in r.text and u2 in r.text, "both attachments must be linked and displayed on the project"
def test_gist_links_multiple_attachments(app_server):
s = _session()
u1, u2 = _upload(s), _upload(s)
r = s.post(f"{BASE_URL}/gists/create", data={
"title": "Attach Gist", "description": "Gist with attachments",
"source_code": "print('hi')", "language": "python",
"attachment_uids": f"{u1},{u2}",
}, allow_redirects=True)
assert r.status_code == 200, r.text[:300]
assert "/gists/" in r.url, r.url
assert u1 in r.text and u2 in r.text, "both attachments must be linked and displayed on the gist"
def test_bug_links_multiple_attachments(app_server):
s = _session()
u1, u2 = _upload(s), _upload(s)
r = s.post(f"{BASE_URL}/bugs/create", data={
"title": "Attach Bug", "description": "Bug report with attachments",
"attachment_uids": f"{u1},{u2}",
}, allow_redirects=True)
assert r.status_code == 200, r.text[:300]
assert u1 in r.text and u2 in r.text, "both attachments must be linked and displayed on the bug"
def test_message_links_multiple_attachments(app_server):
alice = _session()
bob, bob_name = _user("bob")
found = alice.get(f"{BASE_URL}/messages/search", params={"q": bob_name}).json()["results"]
bob_uid = found[0]["uid"]
u1, u2 = _upload(alice), _upload(alice)
r = alice.post(f"{BASE_URL}/messages/send", data={
"content": "Message with attachments", "receiver_uid": bob_uid,
"attachment_uids": f"{u1},{u2}",
}, allow_redirects=True)
assert r.status_code == 200, r.text[:300]
assert u1 in r.text and u2 in r.text, "both attachments must be linked and displayed in the conversation"
def test_single_attachment_links_to_post(app_server):
s = _session()
u1 = _upload(s)
r = s.post(f"{BASE_URL}/posts/create", data={
"content": "Post body with one attachment", "title": "one", "topic": "random",
"attachment_uids": u1,
}, allow_redirects=True)
assert r.status_code == 200, r.text[:300]
assert u1 in r.text, "single attachment must be linked and displayed"