Files
retoor 8e9d3fad98 Add the trust and safety subsystem and the App Store compliance work
Implements the moderation and consent obligations a social platform carries,
so the web version and any client that speaks to it enforce the same rules.

Moderation core (services/moderation/, database/moderation.py): a reportable
target registry, the content filter and its choke points, the report queue with
atomic resolution, enforcement actions, consent tracking, maturity gating, and
account deletion with a grace window.

Surfaces: POST /reports plus the member report list, /admin/moderation and the
per-report admin view, /workspaces, terms acceptance at /auth/terms, consent and
account deletion under /profile, the report button and dialog partials, the
maturity gate, and the moderation stylesheet and ReportDialog client.

Every user-generated surface stays reportable by construction: new content tables
are registered in REPORTABLE_TARGETS or listed in UNREPORTABLE_TABLES with a
reason, and the registry test fails the suite on anything left unclassified.

Docs: community guidelines, content moderation, intellectual property, privacy,
terms, contact, and the admin-only moderation operations page, plus the
moderation API group and the Devii moderation actions.

Compliance record: applecomp.md is the requirement register, applechanges.md the
gap analysis against this codebase, and appleimpl.md the implementation design
they resolve to.

Tests cover the report flow, admin moderation, consent, account deletion, terms
acceptance, workspaces, and the registry invariant across the unit, api, and e2e
tiers.
2026-08-09 00:18:20 +02:00

216 lines
7.7 KiB
Python

# retoor <retoor@molodetz.nl>
from datetime import datetime, timedelta, timezone
import uuid_utils
from devplacepy.config import PRESENCE_TIMEOUT_SECONDS, PRESENCE_WRITE_SECONDS
from devplacepy.database import get_table, set_consent
from devplacepy.services import presence
def _iso(seconds_ago):
return (datetime.now(timezone.utc) - timedelta(seconds=seconds_ago)).isoformat()
def test_is_online_recent():
assert presence.is_online({"last_seen": _iso(5)}) is True
def test_is_online_stale():
assert presence.is_online({"last_seen": _iso(PRESENCE_TIMEOUT_SECONDS + 30)}) is False
def test_is_online_missing_and_empty():
assert presence.is_online({"last_seen": ""}) is False
assert presence.is_online({}) is False
assert presence.is_online(None) is False
def test_is_online_unparseable_is_offline():
assert presence.is_online({"last_seen": "not-a-timestamp"}) is False
def test_is_online_is_the_first_observation_of_stays_online():
from devplacepy.config import PRESENCE_ONLINE_MARGIN_SECONDS
for seconds in (0, 5, PRESENCE_TIMEOUT_SECONDS - 1, PRESENCE_TIMEOUT_SECONDS + 1,
PRESENCE_TIMEOUT_SECONDS + PRESENCE_ONLINE_MARGIN_SECONDS + 1):
row = {"last_seen": _iso(seconds)}
expected = presence.stays_online(
presence.seconds_since(row["last_seen"]), was_online=False
)
assert presence.is_online(row) is expected
def test_seconds_since_none_on_missing_or_bad():
assert presence.seconds_since(None) is None
assert presence.seconds_since("") is None
assert presence.seconds_since("garbage") is None
assert presence.seconds_since(_iso(10)) >= 10
def test_touch_writes_once_then_throttles(local_db):
uid = str(uuid_utils.uuid7())
users = get_table("users")
users.insert(
{
"uid": uid,
"username": f"presence_{uid[:8]}",
"terms_version": "1",
"email": f"{uid[:8]}@presence.test",
"last_seen": None,
}
)
set_consent("user", uid, "activity_recording", True)
presence._last_write.pop(uid, None)
presence.touch(uid)
first = users.find_one(uid=uid)["last_seen"]
assert first
presence.touch(uid)
assert users.find_one(uid=uid)["last_seen"] == first
presence._last_write[uid] = presence.time.monotonic() - PRESENCE_WRITE_SECONDS - 1
presence.touch(uid)
second = users.find_one(uid=uid)["last_seen"]
assert second >= first
assert presence.is_online(users.find_one(uid=uid)) is True
def test_get_online_users_filters_by_cutoff(local_db):
from devplacepy.database import get_online_users
now = datetime.now(timezone.utc)
fresh = str(uuid_utils.uuid7())
stale = str(uuid_utils.uuid7())
users = get_table("users")
users.insert(
{"uid": fresh, "username": f"onl_{fresh[:8]}", "email": f"{fresh[:8]}@o.test", "last_seen": now.isoformat()}
)
users.insert(
{"uid": stale, "username": f"onl_{stale[:8]}", "email": f"{stale[:8]}@o.test", "last_seen": (now - timedelta(seconds=99999)).isoformat()}
)
cutoff = (now - timedelta(seconds=60)).isoformat()
online = {u["uid"] for u in get_online_users(cutoff, limit=1000)}
assert fresh in online
assert stale not in online
def test_presence_online_users_includes_recent(local_db):
fresh = str(uuid_utils.uuid7())
get_table("users").insert(
{"uid": fresh, "username": f"po_{fresh[:8]}", "email": f"{fresh[:8]}@o.test", "last_seen": datetime.now(timezone.utc).isoformat()}
)
assert fresh in {u["uid"] for u in presence.online_users(limit=1000)}
def test_online_users_sorted_alphabetically(local_db):
now = datetime.now(timezone.utc).isoformat()
users = get_table("users")
made = []
for label in ("zeta", "alpha", "mike"):
uid = str(uuid_utils.uuid7())
name = f"srt{label}{uid[:6]}"
users.insert({"uid": uid, "username": name, "email": f"{uid[:8]}@o.test", "last_seen": now})
made.append(name)
listed = [u["username"] for u in presence.online_users(limit=1000) if u["username"] in made]
assert listed == sorted(made, key=str.lower)
def test_stays_online_hysteresis():
from devplacepy.config import (
PRESENCE_ONLINE_MARGIN_SECONDS,
PRESENCE_TIMEOUT_SECONDS,
)
within = PRESENCE_TIMEOUT_SECONDS - 5
band = PRESENCE_TIMEOUT_SECONDS + 5
beyond = PRESENCE_TIMEOUT_SECONDS + PRESENCE_ONLINE_MARGIN_SECONDS + 5
# fresh activity: online regardless of prior state
assert presence.stays_online(within, was_online=False) is True
assert presence.stays_online(within, was_online=True) is True
# in the grace band: only an already-online user stays online (hysteresis)
assert presence.stays_online(band, was_online=False) is False
assert presence.stays_online(band, was_online=True) is True
# beyond the grace: offline even if previously online
assert presence.stays_online(beyond, was_online=True) is False
# no last_seen: always offline
assert presence.stays_online(None, was_online=True) is False
def test_online_candidates_uses_grace_window(local_db):
from devplacepy.config import (
PRESENCE_ONLINE_MARGIN_SECONDS,
PRESENCE_TIMEOUT_SECONDS,
)
now = datetime.now(timezone.utc)
band = str(uuid_utils.uuid7())
beyond = str(uuid_utils.uuid7())
users = get_table("users")
users.insert(
{"uid": band, "username": f"cb_{band[:8]}", "email": f"{band[:8]}@o.test",
"last_seen": (now - timedelta(seconds=PRESENCE_TIMEOUT_SECONDS + 3)).isoformat()}
)
users.insert(
{"uid": beyond, "username": f"cb_{beyond[:8]}", "email": f"{beyond[:8]}@o.test",
"last_seen": (now - timedelta(seconds=PRESENCE_TIMEOUT_SECONDS + PRESENCE_ONLINE_MARGIN_SECONDS + 30)).isoformat()}
)
candidates = {u["uid"] for u in presence.online_candidates(limit=1000)}
strict = {u["uid"] for u in presence.online_users(limit=1000)}
# a grace-band user is a candidate (for hysteresis) but NOT strictly online
assert band in candidates
assert band not in strict
# a user past the grace window is in neither
assert beyond not in candidates
def test_online_candidates_tracks_beyond_the_display_limit(local_db):
from devplacepy.config import PRESENCE_ONLINE_LIMIT, PRESENCE_TRACK_LIMIT
# the display roster is capped for the feed panel, the tracked authority set is not
assert PRESENCE_TRACK_LIMIT > PRESENCE_ONLINE_LIMIT
now = datetime.now(timezone.utc).isoformat()
users = get_table("users")
made = []
for index in range(PRESENCE_ONLINE_LIMIT + 5):
uid = str(uuid_utils.uuid7())
users.insert(
{"uid": uid, "username": f"trk{index:03d}{uid[:6]}",
"email": f"{uid[:8]}@o.test", "last_seen": now}
)
made.append(uid)
tracked = {u["uid"] for u in presence.online_candidates()}
assert set(made) <= tracked
def test_touch_is_silent_without_activity_recording_consent(local_db):
uid = str(uuid_utils.uuid7())
users = get_table("users")
users.insert(
{
"uid": uid,
"username": f"presence_{uid[:8]}",
"terms_version": "1",
"email": f"{uid[:8]}@presence.test",
"last_seen": None,
}
)
presence._last_write.pop(uid, None)
presence.touch(uid)
assert users.find_one(uid=uid)["last_seen"] is None
set_consent("user", uid, "activity_recording", True)
presence._last_write.pop(uid, None)
presence.touch(uid)
assert users.find_one(uid=uid)["last_seen"]
set_consent("user", uid, "activity_recording", False)
presence._last_write.pop(uid, None)
written = users.find_one(uid=uid)["last_seen"]
presence.touch(uid)
assert users.find_one(uid=uid)["last_seen"] == written