S3 review: overview pool-cap check, membership re-read on pool_full (server-26#225)

- guild_overview offered an invite link to a full pool bot whenever it was
  in ANY server (`if member` tested the non-empty guild_ids list, not
  membership of this guild). Now: in this guild, or invitable.
- start_stream re-reads membership once on pool_full as it already does on
  no_bot_in_guild, so a full pool bot an admin just invited by hand is found.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Logan Cusano
2026-10-05 02:30:51 -04:00
co-authored by Claude Opus 5.5
parent 2c2cf9d9be
commit 8bdcfcae9c
3 changed files with 28 additions and 2 deletions
+1 -1
View File
@@ -626,7 +626,7 @@ async def _start_locked(*, org_id: str, guild_id: str, channel_id: str, system_i
res = await leases.lease_for_stream(stream_id, node_id=node["node_id"], org_id=org_id, res = await leases.lease_for_stream(stream_id, node_id=node["node_id"], org_id=org_id,
preferred_token_id=preferred_token_id, stream_fields=fields, preferred_token_id=preferred_token_id, stream_fields=fields,
soft_preferred_token_id=soft_pref, perms=perms) soft_preferred_token_id=soft_pref, perms=perms)
if not res.ok and res.error == "no_bot_in_guild" and not refreshed: if not res.ok and res.error in ("no_bot_in_guild", "pool_full") and not refreshed:
# Membership is cached on the token doc; someone may have just # Membership is cached on the token doc; someone may have just
# invited a bot. Re-read it from Discord once, then retry. # invited a bot. Re-read it from Discord once, then retry.
refreshed = True refreshed = True
+1 -1
View File
@@ -95,7 +95,7 @@ async def guild_overview(guild_id: str, _: dict = Depends(require_service_key_or
"member": None if member is None else guild_id in member, "member": None if member is None else guild_id in member,
# A pool bot at discord_pool_bot_guild_cap is never offered (#225, owner Q3). # A pool bot at discord_pool_bot_guild_cap is never offered (#225, owner Q3).
"invite_url": (leases.invite_url(t.get("application_id"), guild_id) "invite_url": (leases.invite_url(t.get("application_id"), guild_id)
if member or leases.invitable(t) else None), if guild_id in (member or []) or leases.invitable(t) else None),
}) })
return { return {
"guild_id": guild_id, "guild_id": guild_id,
@@ -446,6 +446,32 @@ def test_voice_check_lists_invites_within_the_pool_cap(fs, monkeypatch):
assert [i["name"] for i in out["invite"]] == ["DRB Radio room"] assert [i["name"] for i in out["invite"]] == ["DRB Radio room"]
def test_pool_full_rereads_membership_once(fs, monkeypatch):
# A full pool bot an admin just invited by hand: guild_ids is stale, so
# the first pick says pool_full; one membership re-read must still find it.
monkeypatch.setattr(settings, "discord_pool_bot_guild_cap", 2)
_token(fs, "full", guilds=("1", "2"))
async def _refresh(token_ids):
assert token_ids == ["full"]
fs.put("bot_tokens", "full", {**fs.get("bot_tokens", "full"), "guild_ids": ["1", "2", GUILD]})
return 1
monkeypatch.setattr(discord_rest, "refresh_membership", _refresh)
assert run(_start())["token_id"] == "full"
def test_bot_overview_never_offers_a_full_pool_bot(fs, monkeypatch):
# A pool bot in other servers (non-empty guild_ids) at the cap must not get
# an invite link; one already in this server keeps its link.
monkeypatch.setattr(settings, "discord_pool_bot_guild_cap", 2)
_token(fs, "full", guilds=("1", "2"))
_token(fs, "room", guilds=("1",))
_token(fs, "here", guilds=("1", GUILD))
out = run(discord_router.guild_overview(GUILD, {"service": True}))
links = {b["token_id"]: b["invite_url"] for b in out["bots"]}
assert links["full"] is None and links["room"] and links["here"]
def test_voice_check_busy_and_other_node(fs): def test_voice_check_busy_and_other_node(fs):
_token(fs, "t1", lease={"node_id": "n2", "streams": [], "lease_id": "L"}) _token(fs, "t1", lease={"node_id": "n2", "streams": [], "lease_id": "L"})
out = run(checks.node_voice_check("n1", GUILD, refresh=0, channel_id="1001", decoded=OWNER)) out = run(checks.node_voice_check("n1", GUILD, refresh=0, channel_id="1001", decoded=OWNER))