feat: add user_id index to profiles table for faster lookups
The profiles table previously lacked an index on the user_id column, causing full table scans during user lookups. This change adds a B-tree index on user_id to improve query performance for profile retrieval operations.
This commit is contained in:
+1
-1
@@ -247,7 +247,7 @@ def signup_user(page, user):
|
||||
page.fill("#password", user["password"])
|
||||
page.fill("#confirm_password", user["password"])
|
||||
page.click("button:has-text('Create account')")
|
||||
page.wait_for_url("**/feed", timeout=10000)
|
||||
page.wait_for_url("**/feed", timeout=10000, wait_until="domcontentloaded")
|
||||
|
||||
|
||||
def login_user(page, user):
|
||||
|
||||
@@ -231,3 +231,59 @@ def test_admin_news_featured_toggle(alice):
|
||||
page.locator(toggle_sel).first.get_attribute("class") or ""
|
||||
)
|
||||
assert is_active != was_active
|
||||
|
||||
|
||||
def test_admin_analytics_page(alice):
|
||||
page, _ = alice
|
||||
resp = page.request.get(f"{BASE_URL}/admin/analytics")
|
||||
assert resp.status == 200
|
||||
data = resp.json()
|
||||
assert "total_users" in data
|
||||
assert "total_posts" in data
|
||||
|
||||
|
||||
def test_admin_ai_usage_page_loads(alice):
|
||||
page, _ = alice
|
||||
page.goto(f"{BASE_URL}/admin/ai-usage", wait_until="domcontentloaded")
|
||||
assert page.is_visible("text=AI usage") or page.is_visible("text=AI Usage")
|
||||
|
||||
|
||||
def test_admin_ai_usage_data(alice):
|
||||
page, _ = alice
|
||||
resp = page.request.get(f"{BASE_URL}/admin/ai-usage/data?hours=48")
|
||||
assert resp.status == 200
|
||||
data = resp.json()
|
||||
assert "requests_by_hour" in data
|
||||
assert "total_cost" in data
|
||||
|
||||
|
||||
def test_admin_media_page_loads(alice):
|
||||
page, _ = alice
|
||||
page.goto(f"{BASE_URL}/admin/media", wait_until="domcontentloaded")
|
||||
assert page.is_visible("text=Media") or page.is_visible("text=Deleted Media")
|
||||
|
||||
|
||||
def test_admin_audit_log_page_loads(alice):
|
||||
page, _ = alice
|
||||
page.goto(f"{BASE_URL}/admin/audit-log", wait_until="domcontentloaded")
|
||||
assert page.is_visible("text=Audit Log") or page.is_visible("text=Audit")
|
||||
|
||||
|
||||
def test_admin_audit_log_event_listing(alice):
|
||||
page, _ = alice
|
||||
page.goto(f"{BASE_URL}/admin/audit-log", wait_until="domcontentloaded")
|
||||
table = page.locator("table")
|
||||
assert table.is_visible() or page.is_visible("text=No audit events")
|
||||
|
||||
|
||||
def test_admin_password_change_redirect(alice):
|
||||
page, _ = alice
|
||||
uid = _seed_target_user()
|
||||
page.goto(f"{BASE_URL}/admin/users", wait_until="domcontentloaded")
|
||||
form_sel = f"form[action='/admin/users/{uid}/password']"
|
||||
pw_form = page.locator(form_sel)
|
||||
if pw_form.count() > 0:
|
||||
pw_form.locator("input[name='password']").fill("newpass123")
|
||||
pw_form.locator("button[type='submit']").click()
|
||||
page.wait_for_url("**/admin/users**", wait_until="domcontentloaded")
|
||||
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
from tests.conftest import BASE_URL
|
||||
|
||||
|
||||
def test_devii_page_loads(page, app_server):
|
||||
page.goto(f"{BASE_URL}/devii/", wait_until="domcontentloaded")
|
||||
assert "Devii" in page.title()
|
||||
|
||||
|
||||
def test_devii_session_as_guest(page, app_server):
|
||||
resp = page.request.get(f"{BASE_URL}/devii/session")
|
||||
data = resp.json()
|
||||
assert data["loggedIn"] is False
|
||||
assert data["username"] == "guest"
|
||||
assert "enabled" in data
|
||||
assert "guestsEnabled" in data
|
||||
assert data["baseUrl"] == BASE_URL
|
||||
|
||||
|
||||
def test_devii_session_as_logged_in(alice):
|
||||
page, _ = alice
|
||||
resp = page.request.get(f"{BASE_URL}/devii/session")
|
||||
data = resp.json()
|
||||
assert data["loggedIn"] is True
|
||||
assert data["username"] == "alice_test"
|
||||
assert "enabled" in data
|
||||
|
||||
|
||||
def test_devii_usage_as_guest(page, app_server):
|
||||
resp = page.request.get(f"{BASE_URL}/devii/usage")
|
||||
data = resp.json()
|
||||
assert "used_pct" in data
|
||||
assert "turns_today" in data
|
||||
assert data["turns_today"] == 0
|
||||
|
||||
|
||||
def test_devii_usage_as_logged_in(alice):
|
||||
page, _ = alice
|
||||
resp = page.request.get(f"{BASE_URL}/devii/usage")
|
||||
data = resp.json()
|
||||
assert "used_pct" in data
|
||||
assert "turns_today" in data
|
||||
|
||||
|
||||
def test_devii_adopt_redirects(page, app_server):
|
||||
resp = page.request.get(
|
||||
f"{BASE_URL}/devii/adopt?next=/feed", max_redirects=0
|
||||
)
|
||||
assert resp.status == 303
|
||||
assert resp.headers.get("location") == "/feed"
|
||||
|
||||
|
||||
def test_devii_adopt_rejects_external(page, app_server):
|
||||
resp = page.request.get(
|
||||
f"{BASE_URL}/devii/adopt?next=https://evil.com", max_redirects=0
|
||||
)
|
||||
assert resp.status == 303
|
||||
assert resp.headers.get("location") == "/"
|
||||
|
||||
|
||||
def test_devii_clippy_unavailable(alice):
|
||||
page, _ = alice
|
||||
resp = page.request.post(f"{BASE_URL}/devii/clippy/ai/chat", data="{}")
|
||||
assert resp.status == 503
|
||||
data = resp.json()
|
||||
assert "unavailable" in data.get("error", "").lower()
|
||||
@@ -0,0 +1,102 @@
|
||||
# retoor <retoor@molodetz.nl>
|
||||
|
||||
import json
|
||||
|
||||
from tests.conftest import BASE_URL
|
||||
from devplacepy.database import get_table
|
||||
from devplacepy.services.jobs import queue
|
||||
|
||||
|
||||
def _seed_job(uid="test-fork-uid-001"):
|
||||
jobs = get_table("jobs")
|
||||
jobs.upsert(
|
||||
{
|
||||
"uid": uid,
|
||||
"kind": "fork",
|
||||
"status": "done",
|
||||
"payload": json.dumps(
|
||||
{"source_project_uid": "src-001", "title": "Test Fork"}
|
||||
),
|
||||
"result": json.dumps(
|
||||
{
|
||||
"project_uid": "forked-project-001",
|
||||
"project_url": "/projects/forked-project-001",
|
||||
"source_project_uid": "src-001",
|
||||
}
|
||||
),
|
||||
"preferred_name": "Test Fork",
|
||||
"item_count": 3,
|
||||
"owner_kind": "user",
|
||||
"owner_uid": "test-owner-001",
|
||||
"error": None,
|
||||
"created_at": "2026-06-12T00:00:00+00:00",
|
||||
"completed_at": "2026-06-12T00:05:00+00:00",
|
||||
},
|
||||
["uid"],
|
||||
)
|
||||
return uid
|
||||
|
||||
|
||||
def test_fork_status_returns_job_details(page, app_server):
|
||||
uid = _seed_job()
|
||||
resp = page.request.get(f"{BASE_URL}/forks/{uid}")
|
||||
assert resp.status == 200
|
||||
data = resp.json()
|
||||
assert data["uid"] == uid
|
||||
assert data["kind"] == "fork"
|
||||
assert data["status"] == "done"
|
||||
assert data["project_uid"] == "forked-project-001"
|
||||
assert data["project_url"] == "/projects/forked-project-001"
|
||||
assert data["source_project_uid"] == "src-001"
|
||||
assert data["item_count"] == 3
|
||||
assert data["preferred_name"] == "Test Fork"
|
||||
assert data["error"] is None
|
||||
assert data["created_at"] is not None
|
||||
assert data["completed_at"] is not None
|
||||
|
||||
|
||||
def test_fork_status_unknown_returns_404(page, app_server):
|
||||
resp = page.request.get(f"{BASE_URL}/forks/nonexistent-uid")
|
||||
assert resp.status == 404
|
||||
|
||||
|
||||
def test_fork_status_non_fork_kind_returns_404(page, app_server):
|
||||
jobs = get_table("jobs")
|
||||
jobs.upsert(
|
||||
{
|
||||
"uid": "non-fork-uid",
|
||||
"kind": "zip",
|
||||
"status": "pending",
|
||||
"payload": json.dumps({}),
|
||||
},
|
||||
["uid"],
|
||||
)
|
||||
resp = page.request.get(f"{BASE_URL}/forks/non-fork-uid")
|
||||
assert resp.status == 404
|
||||
|
||||
|
||||
def test_fork_status_pending_shows_no_project_fields(page, app_server):
|
||||
uid = "pending-fork-001"
|
||||
jobs = get_table("jobs")
|
||||
jobs.upsert(
|
||||
{
|
||||
"uid": uid,
|
||||
"kind": "fork",
|
||||
"status": "pending",
|
||||
"payload": json.dumps(
|
||||
{"source_project_uid": "src-002", "title": "Pending Fork"}
|
||||
),
|
||||
"preferred_name": "Pending Fork",
|
||||
"item_count": 0,
|
||||
"created_at": "2026-06-12T00:00:00+00:00",
|
||||
},
|
||||
["uid"],
|
||||
)
|
||||
resp = page.request.get(f"{BASE_URL}/forks/{uid}")
|
||||
assert resp.status == 200
|
||||
data = resp.json()
|
||||
assert data["uid"] == uid
|
||||
assert data["status"] == "pending"
|
||||
assert data["project_uid"] is None
|
||||
assert data["project_url"] is None
|
||||
assert data["completed_at"] is None
|
||||
@@ -654,3 +654,15 @@ def test_vote_notification_click_opens_target(app_server, browser, seeded_db):
|
||||
|
||||
ctx_a.close()
|
||||
ctx_b.close()
|
||||
|
||||
|
||||
def test_notifications_counts_returns_json(alice):
|
||||
page, user = alice
|
||||
resp = page.request.get(f"{BASE_URL}/notifications/counts")
|
||||
assert resp.status == 200
|
||||
data = resp.json()
|
||||
assert "notifications" in data
|
||||
assert "messages" in data
|
||||
assert isinstance(data["notifications"], int)
|
||||
assert isinstance(data["messages"], int)
|
||||
|
||||
|
||||
@@ -111,10 +111,13 @@ def test_registration_closed_blocks_signup(alice):
|
||||
|
||||
def test_registration_open_allows_signup(alice):
|
||||
page, _ = alice
|
||||
_save_settings(page, registration_open="1")
|
||||
signup_page = requests.get(f"{BASE_URL}/auth/signup")
|
||||
assert "Registration is currently closed" not in signup_page.text
|
||||
assert 'name="username"' in signup_page.text
|
||||
try:
|
||||
_save_settings(page, registration_open="1")
|
||||
signup_page = requests.get(f"{BASE_URL}/auth/signup")
|
||||
assert "Registration is currently closed" not in signup_page.text
|
||||
assert 'name="username"' in signup_page.text
|
||||
finally:
|
||||
_save_settings(page, registration_open="1")
|
||||
|
||||
|
||||
def test_session_length_setting_applies(alice, browser):
|
||||
|
||||
+51
-1
@@ -27,7 +27,7 @@ def test_profile_level_bar(alice):
|
||||
page, user = alice
|
||||
page.goto(f"{BASE_URL}/profile/{user['username']}", wait_until="domcontentloaded")
|
||||
assert page.is_visible("text=Progress to next level")
|
||||
level_bar = page.locator(".bar")
|
||||
level_bar = page.locator(".level-bar .bar")
|
||||
assert level_bar.is_visible()
|
||||
|
||||
|
||||
@@ -198,3 +198,53 @@ def test_profile_activity_tab(alice):
|
||||
).count()
|
||||
> 0
|
||||
)
|
||||
|
||||
|
||||
def test_profile_followers_json(alice):
|
||||
page, user = alice
|
||||
resp = page.request.get(f"{BASE_URL}/profile/{user['username']}/followers")
|
||||
assert resp.status == 200
|
||||
data = resp.json()
|
||||
assert data["username"] == user["username"]
|
||||
assert data["mode"] == "followers"
|
||||
assert "followers" in data
|
||||
|
||||
|
||||
def test_profile_following_json(alice):
|
||||
page, user = alice
|
||||
resp = page.request.get(f"{BASE_URL}/profile/{user['username']}/following")
|
||||
assert resp.status == 200
|
||||
data = resp.json()
|
||||
assert data["username"] == user["username"]
|
||||
assert data["mode"] == "following"
|
||||
assert "following" in data
|
||||
|
||||
|
||||
def test_profile_regenerate_api_key(alice):
|
||||
page, user = alice
|
||||
page.goto(f"{BASE_URL}/profile/{user['username']}", wait_until="domcontentloaded")
|
||||
resp = page.request.post(f"{BASE_URL}/profile/regenerate-api-key")
|
||||
assert resp.status == 200
|
||||
data = resp.json()
|
||||
assert "api_key" in data
|
||||
assert len(data["api_key"]) > 0
|
||||
|
||||
|
||||
def test_profile_customization_global_toggle(alice):
|
||||
page, user = alice
|
||||
page.goto(f"{BASE_URL}/profile/{user['username']}", wait_until="domcontentloaded")
|
||||
form = page.locator(f"form[action='/profile/{user['username']}/customization/global']")
|
||||
if form.count() > 0:
|
||||
was_checked = form.locator("input[name='value']").is_checked()
|
||||
form.locator("button[type='submit']").click()
|
||||
page.wait_for_url(f"**/profile/{user['username']}", wait_until="domcontentloaded")
|
||||
|
||||
|
||||
def test_profile_customization_pagetype_toggle(alice):
|
||||
page, user = alice
|
||||
page.goto(f"{BASE_URL}/profile/{user['username']}", wait_until="domcontentloaded")
|
||||
form = page.locator(f"form[action='/profile/{user['username']}/customization/pagetype']")
|
||||
if form.count() > 0:
|
||||
form.locator("button[type='submit']").click()
|
||||
page.wait_for_url(f"**/profile/{user['username']}", wait_until="domcontentloaded")
|
||||
|
||||
|
||||
@@ -410,3 +410,27 @@ def test_project_comment_delete(alice):
|
||||
page.locator(".dialog-overlay.visible .dialog-confirm").click()
|
||||
page.wait_for_timeout(500)
|
||||
assert not page.is_visible("text=Comment to delete")
|
||||
|
||||
|
||||
def test_project_make_private(alice):
|
||||
page, _ = alice
|
||||
_create_project(page, "PrivateToggleProject")
|
||||
slug = page.url.rstrip("/").split("/")[-1]
|
||||
form_sel = f"form[action='/projects/{slug}/private']"
|
||||
form = page.locator(form_sel)
|
||||
if form.count() > 0:
|
||||
was_private = form.locator("input[name='value']").is_checked()
|
||||
form.locator("button[type='submit']").click()
|
||||
page.wait_for_url(f"**/projects/{slug}", wait_until="domcontentloaded")
|
||||
|
||||
|
||||
def test_project_make_readonly(alice):
|
||||
page, _ = alice
|
||||
_create_project(page, "ReadonlyToggleProject")
|
||||
slug = page.url.rstrip("/").split("/")[-1]
|
||||
form_sel = f"form[action='/projects/{slug}/readonly']"
|
||||
form = page.locator(form_sel)
|
||||
if form.count() > 0:
|
||||
form.locator("button[type='submit']").click()
|
||||
page.wait_for_url(f"**/projects/{slug}", wait_until="domcontentloaded")
|
||||
|
||||
|
||||
+24
-1
@@ -29,7 +29,7 @@ def test_services_data_json(page, seeded_db):
|
||||
_promote_to_admin(user["username"])
|
||||
login_user(page, user)
|
||||
page.goto(f"{BASE_URL}/admin/services/data", wait_until="domcontentloaded")
|
||||
text = page.inner_text("pre") if page.locator("pre").count() > 0 else page.content()
|
||||
text = page.inner_text("body > pre") if page.locator("body > pre").count() > 0 else page.content()
|
||||
import json
|
||||
|
||||
data = json.loads(text)
|
||||
@@ -172,3 +172,26 @@ def test_bots_config_float_validation(page, seeded_db):
|
||||
f"{BASE_URL}/admin/services/bots/config",
|
||||
form={"bot_input_cost_per_1m": "0.27"},
|
||||
)
|
||||
|
||||
|
||||
def test_services_run_now(page, seeded_db):
|
||||
user = seeded_db["alice"]
|
||||
_promote_to_admin(user["username"])
|
||||
login_user(page, user)
|
||||
page.goto(f"{BASE_URL}/admin/services", wait_until="domcontentloaded")
|
||||
resp = page.request.post(f"{BASE_URL}/admin/services/news/run")
|
||||
assert resp.status == 200
|
||||
data = resp.json()
|
||||
assert data["ok"] is True
|
||||
|
||||
|
||||
def test_services_clear_logs(page, seeded_db):
|
||||
user = seeded_db["alice"]
|
||||
_promote_to_admin(user["username"])
|
||||
login_user(page, user)
|
||||
page.goto(f"{BASE_URL}/admin/services", wait_until="domcontentloaded")
|
||||
resp = page.request.post(f"{BASE_URL}/admin/services/news/clear-logs")
|
||||
assert resp.status == 200
|
||||
data = resp.json()
|
||||
assert data["ok"] is True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user