228 lines
6.9 KiB
Python
228 lines
6.9 KiB
Python
|
|
# retoor <retoor@molodetz.nl>
|
||
|
|
|
||
|
|
import time
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
import requests
|
||
|
|
|
||
|
|
from devplacepy.database import REPORTABLE_TARGETS, get_table, refresh_snapshot
|
||
|
|
from tests.conftest import BASE_URL
|
||
|
|
|
||
|
|
JSON = {"Accept": "application/json"}
|
||
|
|
_counter = [0]
|
||
|
|
|
||
|
|
|
||
|
|
def _unique(prefix="rep"):
|
||
|
|
_counter[0] += 1
|
||
|
|
return f"{prefix}{int(time.time() * 1000)}{_counter[0]}"
|
||
|
|
|
||
|
|
|
||
|
|
def _member():
|
||
|
|
name = _unique()
|
||
|
|
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 _post(session, title="Reportable post"):
|
||
|
|
response = session.post(
|
||
|
|
f"{BASE_URL}/posts/create",
|
||
|
|
data={"title": title, "content": "A post that can be reported by others."},
|
||
|
|
headers=JSON,
|
||
|
|
)
|
||
|
|
assert response.status_code == 200, response.text
|
||
|
|
return response.json()["data"]["uid"]
|
||
|
|
|
||
|
|
|
||
|
|
def _reports_for(target_type, target_uid):
|
||
|
|
refresh_snapshot()
|
||
|
|
return list(
|
||
|
|
get_table("content_reports").find(
|
||
|
|
target_type=target_type, target_uid=target_uid, deleted_at=None
|
||
|
|
)
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.fixture(scope="module")
|
||
|
|
def author(app_server):
|
||
|
|
return _member()
|
||
|
|
|
||
|
|
|
||
|
|
def test_report_reasons_are_public(app_server):
|
||
|
|
response = requests.get(f"{BASE_URL}/reports/reasons", headers=JSON)
|
||
|
|
assert response.status_code == 200
|
||
|
|
keys = {entry["key"] for entry in response.json()["reasons"]}
|
||
|
|
assert {"harassment", "spam", "intellectual_property", "other"} <= keys
|
||
|
|
|
||
|
|
|
||
|
|
def test_the_reasons_page_is_indexable_with_breadcrumbs_and_a_site_schema(app_server):
|
||
|
|
html = requests.get(f"{BASE_URL}/reports/reasons").text
|
||
|
|
assert '<meta name="robots" content="index,follow">' in html
|
||
|
|
assert 'class="breadcrumb"' in html
|
||
|
|
assert "Report reasons" in html
|
||
|
|
assert '"@type": "WebSite"' in html
|
||
|
|
|
||
|
|
|
||
|
|
def test_the_reasons_page_title_carries_one_site_suffix(app_server):
|
||
|
|
html = requests.get(f"{BASE_URL}/reports/reasons").text
|
||
|
|
start = html.index("<title>") + len("<title>")
|
||
|
|
title = html[start : html.index("</title>", start)]
|
||
|
|
assert title == "Report reasons - DevPlace", title
|
||
|
|
|
||
|
|
|
||
|
|
def test_reporting_a_post_records_one_row_with_the_owner(author):
|
||
|
|
author_session, author_name = author
|
||
|
|
post_uid = _post(author_session)
|
||
|
|
reporter, _ = _member()
|
||
|
|
response = reporter.post(
|
||
|
|
f"{BASE_URL}/reports/post/{post_uid}",
|
||
|
|
data={"reason": "harassment", "detail": "Targets me personally."},
|
||
|
|
headers=JSON,
|
||
|
|
)
|
||
|
|
assert response.status_code == 200, response.text
|
||
|
|
assert response.json()["data"]["status"] == "open"
|
||
|
|
rows = _reports_for("post", post_uid)
|
||
|
|
assert len(rows) == 1
|
||
|
|
owner = get_table("users").find_one(username=author_name)
|
||
|
|
assert rows[0]["owner_uid"] == owner["uid"]
|
||
|
|
assert rows[0]["origin"] == "member"
|
||
|
|
|
||
|
|
|
||
|
|
def test_second_report_by_the_same_reporter_updates_instead_of_duplicating(author):
|
||
|
|
author_session, _ = author
|
||
|
|
post_uid = _post(author_session)
|
||
|
|
reporter, _ = _member()
|
||
|
|
for reason in ("spam", "harassment"):
|
||
|
|
response = reporter.post(
|
||
|
|
f"{BASE_URL}/reports/post/{post_uid}",
|
||
|
|
data={"reason": reason},
|
||
|
|
headers=JSON,
|
||
|
|
)
|
||
|
|
assert response.status_code == 200
|
||
|
|
rows = _reports_for("post", post_uid)
|
||
|
|
assert len(rows) == 1
|
||
|
|
assert rows[0]["reason"] == "harassment"
|
||
|
|
|
||
|
|
|
||
|
|
def test_a_different_reporter_creates_a_second_row(author):
|
||
|
|
author_session, _ = author
|
||
|
|
post_uid = _post(author_session)
|
||
|
|
for _ in range(2):
|
||
|
|
reporter, _ = _member()
|
||
|
|
reporter.post(
|
||
|
|
f"{BASE_URL}/reports/post/{post_uid}",
|
||
|
|
data={"reason": "spam"},
|
||
|
|
headers=JSON,
|
||
|
|
)
|
||
|
|
assert len(_reports_for("post", post_uid)) == 2
|
||
|
|
|
||
|
|
|
||
|
|
def test_guests_cannot_report(author):
|
||
|
|
author_session, _ = author
|
||
|
|
post_uid = _post(author_session)
|
||
|
|
response = requests.post(
|
||
|
|
f"{BASE_URL}/reports/post/{post_uid}",
|
||
|
|
data={"reason": "spam"},
|
||
|
|
headers=JSON,
|
||
|
|
)
|
||
|
|
assert response.status_code == 401
|
||
|
|
|
||
|
|
|
||
|
|
def test_reporting_your_own_content_is_refused(author):
|
||
|
|
author_session, _ = author
|
||
|
|
post_uid = _post(author_session)
|
||
|
|
response = author_session.post(
|
||
|
|
f"{BASE_URL}/reports/post/{post_uid}",
|
||
|
|
data={"reason": "spam"},
|
||
|
|
headers=JSON,
|
||
|
|
)
|
||
|
|
assert response.status_code == 400
|
||
|
|
assert _reports_for("post", post_uid) == []
|
||
|
|
|
||
|
|
|
||
|
|
def test_an_unknown_target_type_is_refused(author):
|
||
|
|
reporter, _ = _member()
|
||
|
|
response = reporter.post(
|
||
|
|
f"{BASE_URL}/reports/not_a_surface/whatever",
|
||
|
|
data={"reason": "spam"},
|
||
|
|
headers=JSON,
|
||
|
|
)
|
||
|
|
assert response.status_code == 400
|
||
|
|
|
||
|
|
|
||
|
|
def test_an_unknown_reason_is_refused(author):
|
||
|
|
author_session, _ = author
|
||
|
|
post_uid = _post(author_session)
|
||
|
|
reporter, _ = _member()
|
||
|
|
response = reporter.post(
|
||
|
|
f"{BASE_URL}/reports/post/{post_uid}",
|
||
|
|
data={"reason": "not_a_reason"},
|
||
|
|
headers=JSON,
|
||
|
|
)
|
||
|
|
assert response.status_code == 422
|
||
|
|
|
||
|
|
|
||
|
|
def test_every_registry_target_type_is_accepted_by_the_route(author):
|
||
|
|
author_session, _ = author
|
||
|
|
reporter, _ = _member()
|
||
|
|
for target_type in REPORTABLE_TARGETS:
|
||
|
|
response = reporter.post(
|
||
|
|
f"{BASE_URL}/reports/{target_type}/missing-{target_type}",
|
||
|
|
data={"reason": "other"},
|
||
|
|
headers=JSON,
|
||
|
|
)
|
||
|
|
assert response.status_code == 200, f"{target_type}: {response.text}"
|
||
|
|
|
||
|
|
|
||
|
|
def test_the_report_dialog_uses_the_shared_auth_field_classes(app_server):
|
||
|
|
reporter, _ = _member()
|
||
|
|
html = reporter.get(f"{BASE_URL}/feed").text
|
||
|
|
assert 'id="report-dialog"' in html
|
||
|
|
assert 'class="auth-field auth-field-gap"' in html
|
||
|
|
assert 'class="modal-footer"' in html
|
||
|
|
assert 'id="report-reason"' in html
|
||
|
|
assert 'id="report-detail"' in html
|
||
|
|
|
||
|
|
|
||
|
|
def test_your_reports_page_loads_the_admin_table_styles(author):
|
||
|
|
author_session, _ = author
|
||
|
|
post_uid = _post(author_session)
|
||
|
|
reporter, _ = _member()
|
||
|
|
reporter.post(
|
||
|
|
f"{BASE_URL}/reports/post/{post_uid}", data={"reason": "spam"}, headers=JSON
|
||
|
|
)
|
||
|
|
html = reporter.get(f"{BASE_URL}/reports/mine").text
|
||
|
|
assert "css/admin.css" in html
|
||
|
|
assert 'class="admin-table"' in html
|
||
|
|
|
||
|
|
|
||
|
|
def test_mine_lists_only_your_own_reports(author):
|
||
|
|
author_session, _ = author
|
||
|
|
post_uid = _post(author_session)
|
||
|
|
mine, _ = _member()
|
||
|
|
other, _ = _member()
|
||
|
|
mine.post(
|
||
|
|
f"{BASE_URL}/reports/post/{post_uid}", data={"reason": "spam"}, headers=JSON
|
||
|
|
)
|
||
|
|
other.post(
|
||
|
|
f"{BASE_URL}/reports/post/{post_uid}",
|
||
|
|
data={"reason": "harassment"},
|
||
|
|
headers=JSON,
|
||
|
|
)
|
||
|
|
payload = mine.get(f"{BASE_URL}/reports/mine", headers=JSON).json()
|
||
|
|
reasons = {entry["reason"] for entry in payload["reports"]}
|
||
|
|
assert "spam" in reasons
|
||
|
|
assert "harassment" not in reasons
|
||
|
|
assert requests.get(f"{BASE_URL}/reports/mine", headers=JSON).status_code == 401
|