46 lines
1.6 KiB
Python
Raw Normal View History

Make the presence roster the single source of truth for online status Online status had three server-side candidate populations and two client-side deciders, so the feed roster and the /messages indicators could legitimately disagree. PresenceRelayService built its online set from whichever topics happened to be subscribed on a given tick: the roster candidates on /feed, only the per-uid dot rows on /messages. Different populations meant a different hysteresis baseline, so the same user could be online in one place and offline in the other. On top of that, PresenceManager re-derived online status client-side from a frozen data-presence-last-seen with a strict timeout and no hysteresis, re-evaluated every 20s, so any element whose relay frame was missed drifted grey after the timeout and stayed there. AppChat carried a third renderer with its own PubSubClient that only ever wrote online/offline, plus hand-built dot markup duplicating _presence_dot.html. The relay now collapses to one set on one topic. Each tick it reads the online population in a single indexed query (online_candidates, capped by the new PRESENCE_TRACK_LIMIT), applies hysteresis once, and publishes {count, online, users} on public.presence.roster only when the uid set changes. online is the authority for every avatar dot; users is the same set trimmed to PRESENCE_ONLINE_LIMIT for the feed panel. The per-uid public.presence.{uid} topics are gone, which also removes one subscription per distinct author on a page. is_online(user) is now stays_online(seconds_since(last_seen), False), so the server-rendered initial state and the live set apply one formula. PresenceManager makes one subscription and renders every [data-presence-uid] element as membership of that set, with no clock and no expiry timer; before the first frame the server-rendered state stands. Relative "last seen" text is a <time data-dt data-dt-mode="ago"> handled by the shared LocalTime. AppChat lost its presence code entirely, and the new Avatar.badgeElement is the JS twin of _presence_dot.html, so dot markup now lives in exactly two places. Also fix the awards column ensure-block, which the awards tests exposed. backfill_api_keys opened with an "if users not in db.tables" guard, but on a brand-new database that is precisely the state at init_db time, so the whole users ensure-block was skipped. The first signup then created users with only the columns of that INSERT, leaving every ensured-but- unwritten column absent from the server's reflected metadata for the rest of the process lifetime. That is why the awards tab, the prominent award banner and the avatar award badge were invisible on a fresh database. It now calls get_table("users") unconditionally. test_avatar_badge_on_feed_when_prominent asserted that any online user carries an award badge while only awarding a user who was never active, so it passed only by accident. It now makes the awarded user active and asserts the badge on that user's roster entry. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-06 19:19:21 +02:00
# retoor <retoor@molodetz.nl>
from devplacepy.config import PRESENCE_ONLINE_LIMIT
from devplacepy.services.presence_relay import ROSTER_TOPIC, roster_payload
def _rows(count):
return [
{"uid": f"u{index:03d}", "username": f"user{index:03d}", "avatar_seed": None}
for index in range(count)
]
def test_roster_topic_is_the_single_presence_channel():
assert ROSTER_TOPIC == "public.presence.roster"
def test_payload_only_carries_the_online_set(local_db):
rows = _rows(4)
payload = roster_payload(rows, {"u000", "u002"})
assert payload["online"] == ["u000", "u002"]
assert [user["uid"] for user in payload["users"]] == ["u000", "u002"]
assert payload["count"] == 2
def test_dots_and_display_roster_come_from_one_set(local_db):
rows = _rows(PRESENCE_ONLINE_LIMIT + 7)
online = {row["uid"] for row in rows}
payload = roster_payload(rows, online)
# every online user is authoritative for their avatar dot...
assert set(payload["online"]) == online
# ...while the feed's avatar panel stays capped for display
assert len(payload["users"]) == PRESENCE_ONLINE_LIMIT
assert payload["count"] == PRESENCE_ONLINE_LIMIT
assert set(payload["online"]) >= {user["uid"] for user in payload["users"]}
def test_avatar_seed_falls_back_to_username(local_db):
payload = roster_payload([{"uid": "u1", "username": "ada", "avatar_seed": None}], {"u1"})
assert payload["users"][0]["avatar_seed"] == "ada"
def test_offline_users_are_absent_everywhere(local_db):
payload = roster_payload(_rows(3), set())
assert payload == {"count": 0, "online": [], "users": []}