تم الانتهاء من آخر دفعة تحسينات على المشروع، وتشمل:
تحويل لوحة الإدارة الداخلية من secret header إلى session auth حقيقي مع صلاحيات admin. إضافة دعم إدارة الأدوار من داخل لوحة الإدارة نفسها، مع حماية الحسابات المعتمدة عبر INTERNAL_ADMIN_EMAILS. تحسين بيانات المستخدم في الواجهة والباكند لتشمل role وis_allowlisted_admin. إضافة اختبار frontend مخصص لصفحة /internal/admin بدل الاعتماد فقط على build واختبار routes. تحسين إضافي في الأداء عبر إزالة الاعتماد على pdfjs-dist/pdf.worker في عدّ صفحات PDF واستبداله بمسار أخف باستخدام pdf-lib. تحسين تقسيم الـ chunks في build لتقليل أثر الحزم الكبيرة وفصل أجزاء مثل network, icons, pdf-core, وeditor. التحقق الذي تم: نجاح build للواجهة. نجاح اختبار صفحة الإدارة الداخلية في frontend. نجاح اختبارات auth/admin في backend. نجاح full backend suite مسبقًا مع EXIT:0. ولو تريد نسخة أقصر جدًا، استخدم هذه: آخر التحديثات: تم تحسين نظام الإدارة الداخلية ليعتمد على صلاحيات وجلسات حقيقية بدل secret header، مع إضافة إدارة أدوار من لوحة admin نفسها، وإضافة اختبارات frontend مخصصة للوحة، وتحسين أداء الواجهة عبر إزالة pdf.worker وتحسين تقسيم الـ chunks في build. جميع الاختبارات والتحققات الأساسية المطلوبة نجح
This commit is contained in:
175
backend/tests/test_admin.py
Normal file
175
backend/tests/test_admin.py
Normal file
@@ -0,0 +1,175 @@
|
||||
"""Tests for internal admin dashboard endpoints."""
|
||||
|
||||
from app.services.account_service import create_user, record_file_history, set_user_role, update_user_plan
|
||||
from app.services.contact_service import save_message
|
||||
from app.services.rating_service import submit_rating
|
||||
|
||||
|
||||
class TestInternalAdminRoutes:
|
||||
def test_overview_requires_authenticated_admin(self, client):
|
||||
response = client.get("/api/internal/admin/overview")
|
||||
|
||||
assert response.status_code == 401
|
||||
|
||||
def test_overview_rejects_non_admin_user(self, app, client):
|
||||
with app.app_context():
|
||||
create_user("member@example.com", "testpass123")
|
||||
|
||||
login_response = client.post(
|
||||
"/api/auth/login",
|
||||
json={"email": "member@example.com", "password": "testpass123"},
|
||||
)
|
||||
assert login_response.status_code == 200
|
||||
|
||||
response = client.get("/api/internal/admin/overview")
|
||||
assert response.status_code == 403
|
||||
|
||||
def test_overview_returns_operational_summary(self, app, client):
|
||||
with app.app_context():
|
||||
first_user = create_user("admin-a@example.com", "testpass123")
|
||||
second_user = create_user("admin-b@example.com", "testpass123")
|
||||
set_user_role(first_user["id"], "admin")
|
||||
update_user_plan(second_user["id"], "pro")
|
||||
|
||||
record_file_history(
|
||||
user_id=first_user["id"],
|
||||
tool="compress-pdf",
|
||||
original_filename="one.pdf",
|
||||
output_filename="one-small.pdf",
|
||||
status="completed",
|
||||
download_url="https://example.com/one-small.pdf",
|
||||
)
|
||||
record_file_history(
|
||||
user_id=second_user["id"],
|
||||
tool="repair-pdf",
|
||||
original_filename="broken.pdf",
|
||||
output_filename=None,
|
||||
status="failed",
|
||||
download_url=None,
|
||||
metadata={"error": "Repair failed."},
|
||||
)
|
||||
|
||||
submit_rating("compress-pdf", 5, fingerprint="admin-rating")
|
||||
message = save_message("Admin User", "ops@example.com", "bug", "Need help", "Broken upload")
|
||||
|
||||
login_response = client.post(
|
||||
"/api/auth/login",
|
||||
json={"email": "admin-a@example.com", "password": "testpass123"},
|
||||
)
|
||||
assert login_response.status_code == 200
|
||||
|
||||
response = client.get("/api/internal/admin/overview")
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert data["users"]["total"] == 2
|
||||
assert data["users"]["pro"] == 1
|
||||
assert data["processing"]["total_files_processed"] == 2
|
||||
assert data["processing"]["failed_files"] == 1
|
||||
assert data["ratings"]["rating_count"] == 1
|
||||
assert data["contacts"]["unread_messages"] == 1
|
||||
assert data["contacts"]["recent"][0]["id"] == message["id"]
|
||||
assert data["recent_failures"][0]["tool"] == "repair-pdf"
|
||||
|
||||
def test_contacts_can_be_marked_read(self, app, client):
|
||||
with app.app_context():
|
||||
admin_user = create_user("admin-reader@example.com", "testpass123")
|
||||
set_user_role(admin_user["id"], "admin")
|
||||
message = save_message("Reader", "reader@example.com", "general", "Hello", "Please review")
|
||||
|
||||
login_response = client.post(
|
||||
"/api/auth/login",
|
||||
json={"email": "admin-reader@example.com", "password": "testpass123"},
|
||||
)
|
||||
assert login_response.status_code == 200
|
||||
|
||||
mark_response = client.post(f"/api/internal/admin/contacts/{message['id']}/read")
|
||||
assert mark_response.status_code == 200
|
||||
|
||||
contacts_response = client.get("/api/internal/admin/contacts")
|
||||
assert contacts_response.status_code == 200
|
||||
contacts_data = contacts_response.get_json()
|
||||
assert contacts_data["unread"] == 0
|
||||
assert contacts_data["items"][0]["is_read"] is True
|
||||
|
||||
def test_user_plan_can_be_updated(self, app, client):
|
||||
with app.app_context():
|
||||
admin_user = create_user("admin-plan@example.com", "testpass123")
|
||||
user = create_user("plan-change@example.com", "testpass123")
|
||||
set_user_role(admin_user["id"], "admin")
|
||||
|
||||
login_response = client.post(
|
||||
"/api/auth/login",
|
||||
json={"email": "admin-plan@example.com", "password": "testpass123"},
|
||||
)
|
||||
assert login_response.status_code == 200
|
||||
|
||||
response = client.post(
|
||||
f"/api/internal/admin/users/{user['id']}/plan",
|
||||
json={"plan": "pro"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert data["user"]["plan"] == "pro"
|
||||
|
||||
def test_user_role_can_be_updated(self, app, client):
|
||||
with app.app_context():
|
||||
admin_user = create_user("admin-role@example.com", "testpass123")
|
||||
user = create_user("member-role@example.com", "testpass123")
|
||||
set_user_role(admin_user["id"], "admin")
|
||||
|
||||
login_response = client.post(
|
||||
"/api/auth/login",
|
||||
json={"email": "admin-role@example.com", "password": "testpass123"},
|
||||
)
|
||||
assert login_response.status_code == 200
|
||||
|
||||
response = client.post(
|
||||
f"/api/internal/admin/users/{user['id']}/role",
|
||||
json={"role": "admin"},
|
||||
)
|
||||
|
||||
assert response.status_code == 200
|
||||
data = response.get_json()
|
||||
assert data["user"]["role"] == "admin"
|
||||
|
||||
def test_allowlisted_admin_role_cannot_be_changed(self, app, client):
|
||||
app.config["INTERNAL_ADMIN_EMAILS"] = ("bootstrap-admin@example.com",)
|
||||
with app.app_context():
|
||||
actor = create_user("actor-admin@example.com", "testpass123")
|
||||
bootstrap = create_user("bootstrap-admin@example.com", "testpass123")
|
||||
set_user_role(actor["id"], "admin")
|
||||
|
||||
login_response = client.post(
|
||||
"/api/auth/login",
|
||||
json={"email": "actor-admin@example.com", "password": "testpass123"},
|
||||
)
|
||||
assert login_response.status_code == 200
|
||||
|
||||
response = client.post(
|
||||
f"/api/internal/admin/users/{bootstrap['id']}/role",
|
||||
json={"role": "user"},
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "INTERNAL_ADMIN_EMAILS" in response.get_json()["error"]
|
||||
|
||||
def test_admin_cannot_remove_own_role(self, app, client):
|
||||
with app.app_context():
|
||||
admin_user = create_user("self-admin@example.com", "testpass123")
|
||||
set_user_role(admin_user["id"], "admin")
|
||||
|
||||
login_response = client.post(
|
||||
"/api/auth/login",
|
||||
json={"email": "self-admin@example.com", "password": "testpass123"},
|
||||
)
|
||||
assert login_response.status_code == 200
|
||||
|
||||
response = client.post(
|
||||
f"/api/internal/admin/users/{admin_user['id']}/role",
|
||||
json={"role": "user"},
|
||||
)
|
||||
|
||||
assert response.status_code == 400
|
||||
assert "cannot remove your own admin role" in response.get_json()["error"].lower()
|
||||
Reference in New Issue
Block a user