# retoor <retoor@molodetz.nl>
import time
import pytest
import requests
from devplacepy.database import get_table, refresh_snapshot, set_setting
from devplacepy.schemas import AdminUserOut
from tests.conftest import BASE_URL
JSON = {"Accept": "application/json"}
_counter = [0]
def _unique(prefix="mod"):
_counter[0] += 1
return f"{prefix}{int(time.time() * 1000)}{_counter[0]}"
def _member(prefix="mod"):
name = _unique(prefix)
session = requests.Session()
session.post(
f"{BASE_URL}/auth/signup",
data={
"username": name,
"email": f"{name}@t.dev",
"password": "secret123",
"confirm_password": "secret123",
"birth_date": "1990-01-01",
"accept_terms": "1",
},
allow_redirects=True,
)
return session, name
def _admin_session(app_server):
session = requests.Session()
session.post(
f"{BASE_URL}/auth/login",
data={"email": "alice@test.devplace", "password": "secret123"},
allow_redirects=True,
)
return session
def _post(session, content="A post that will be reported by another member."):
response = session.post(
f"{BASE_URL}/posts/create",
data={"title": "Queue subject", "content": content},
headers=JSON,
)
assert response.status_code == 200, response.text
return response.json()["data"]["uid"]
def _report(target_uid, reason="harassment"):
reporter, name = _member("rep")
response = reporter.post(
f"{BASE_URL}/reports/post/{target_uid}",
data={"reason": reason, "detail": "Queue test"},
headers=JSON,
)
assert response.status_code == 200, response.text
refresh_snapshot()
row = get_table("content_reports").find_one(
target_uid=target_uid, deleted_at=None, order_by=["-created_at"]
)
return row["uid"], reporter, name
@pytest.fixture(scope="module")
def admin(app_server, seeded_db):
set_setting("moderation_sla_hours", "24")
return _admin_session(app_server)
def test_queue_is_admin_only(admin, app_server):
member, _ = _member()
assert member.get(f"{BASE_URL}/admin/moderation", headers=JSON).status_code == 403
assert requests.get(f"{BASE_URL}/admin/moderation", headers=JSON).status_code == 401
assert admin.get(f"{BASE_URL}/admin/moderation", headers=JSON).status_code == 200
def test_queue_reports_the_sla_snapshot(admin):
payload = admin.get(f"{BASE_URL}/admin/moderation", headers=JSON).json()
assert payload["sla"]["sla_hours"] == 24
assert "oldest_open_hours" in payload["sla"]
assert set(payload["counts"]) == {"open", "acknowledged", "actioned", "dismissed"}
def test_queue_is_ordered_oldest_open_first(admin):
author, _ = _member("auth")
first = _report(_post(author))[0]
second = _report(_post(author))[0]
payload = admin.get(f"{BASE_URL}/admin/moderation?status=open", headers=JSON).json()
order = [entry["uid"] for entry in payload["reports"]]
assert order.index(first) < order.index(second)
def test_the_report_detail_projects_the_subject_through_the_admin_user_model(admin):
author, author_name = _member("subj")
report_uid = _report(_post(author))[0]
payload = admin.get(
f"{BASE_URL}/admin/moderation/{report_uid}", headers=JSON
).json()
subject = payload["subject"]
assert subject["username"] == author_name
assert "password_hash" not in subject
assert "api_key" not in subject
assert set(subject) <= set(AdminUserOut.model_fields)
def test_acknowledging_keeps_the_report_open_for_a_decision(admin):
author, _ = _member("auth")
report_uid = _report(_post(author))[0]
response = admin.post(
f"{BASE_URL}/admin/moderation/{report_uid}/status",
data={"status": "acknowledged"},
headers=JSON,
)
assert response.status_code == 200
refresh_snapshot()
assert (
get_table("content_reports").find_one(uid=report_uid)["status"]
== "acknowledged"
)
def test_a_decision_records_an_action_row_and_resolves_the_report(admin):
author, _ = _member("auth")
post_uid = _post(author)
report_uid, _, _ = _report(post_uid)
response = admin.post(
f"{BASE_URL}/admin/moderation/{report_uid}/decide",
data={"action": "remove_content", "reason": "Breaks the guidelines"},
headers=JSON,
)
assert response.status_code == 200, response.text
refresh_snapshot()
report = get_table("content_reports").find_one(uid=report_uid)
assert report["status"] == "actioned"
assert report["resolved_at"]
action = get_table("moderation_actions").find_one(report_uid=report_uid)
assert action["action"] == "remove_content"
assert action["reason"] == "Breaks the guidelines"
assert get_table("posts").find_one(uid=post_uid)["deleted_at"]
def test_deciding_a_resolved_report_twice_is_refused(admin):
author, _ = _member("auth")
report_uid = _report(_post(author))[0]
first = admin.post(
f"{BASE_URL}/admin/moderation/{report_uid}/decide",
data={"action": "dismiss"},
headers=JSON,
)
assert first.status_code == 200
second = admin.post(
f"{BASE_URL}/admin/moderation/{report_uid}/decide",
data={"action": "dismiss"},
headers=JSON,
)
assert second.status_code == 409
def test_escalating_raises_severity_and_keeps_the_report_open(admin):
author, _ = _member("auth")
report_uid = _report(_post(author), reason="spam")[0]
response = admin.post(
f"{BASE_URL}/admin/moderation/{report_uid}/decide",
data={"action": "escalate"},
headers=JSON,
)
assert response.status_code == 200
refresh_snapshot()
report = get_table("content_reports").find_one(uid=report_uid)
assert report["severity"] == "critical"
assert report["status"] == "acknowledged"
def test_suspension_blocks_writing_but_not_reading_or_deleting(admin):
author, author_name = _member("susp")
refresh_snapshot()
subject = get_table("users").find_one(username=author_name)
response = admin.post(
f"{BASE_URL}/admin/users/{subject['uid']}/suspend",
data={"reason": "Testing enforcement", "duration_hours": "24"},
headers=JSON,
)
assert response.status_code == 200, response.text
try:
blocked = author.post(
f"{BASE_URL}/posts/create",
data={"title": "nope", "content": "This must not be published at all."},
headers=JSON,
)
assert blocked.status_code == 403
assert author.get(f"{BASE_URL}/feed", headers=JSON).status_code == 200
assert (
author.get(f"{BASE_URL}/profile/{author_name}/delete", headers=JSON).status_code
== 200
)
finally:
admin.post(f"{BASE_URL}/admin/users/{subject['uid']}/lift", headers=JSON)
allowed = author.post(
f"{BASE_URL}/posts/create",
data={"title": "back", "content": "Writing works again after the lift."},
headers=JSON,
)
assert allowed.status_code == 200
def test_ban_revokes_every_session(admin):
victim, victim_name = _member("ban")
refresh_snapshot()
subject = get_table("users").find_one(username=victim_name)
response = admin.post(
f"{BASE_URL}/admin/users/{subject['uid']}/ban",
data={"reason": "Testing the ban path"},
headers=JSON,
)
assert response.status_code == 200
refresh_snapshot()
assert get_table("users").find_one(uid=subject["uid"])["is_active"] in (0, False)
assert victim.get(f"{BASE_URL}/reports/mine", headers=JSON).status_code == 401
def test_an_admin_cannot_enforce_against_themselves(admin, seeded_db):
refresh_snapshot()
alice = get_table("users").find_one(username="alice_test")
response = admin.post(
f"{BASE_URL}/admin/users/{alice['uid']}/suspend",
data={"reason": "self", "duration_hours": "1"},
headers=JSON,
)
assert response.status_code == 200
refresh_snapshot()
assert not (get_table("users").find_one(uid=alice["uid"])["suspended_until"] or "")