diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-23 19:30:47 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-23 19:30:47 +0200 |
| commit | 95cec0e0bbc28e930f297f44bbd3dbf4d63f0bc2 (patch) | |
| tree | 355ac2b769885b368d45846fe4c3c59cc8b3865b /packages/meshbay-hub/tests/test_invite_links.py | |
| parent | d87f05f9f131aa7cc92f53355c5fe63be01aa516 (diff) | |
| download | meshbay-95cec0e0bbc28e930f297f44bbd3dbf4d63f0bc2.tar.gz | |
fix(hub): a redeemed invitation link leaves the owner's list
The list under "Invite by link" answered every ticket the group had ever
minted, so a link that somebody had already used sat there saying "used by
<name>" for the thirty days of KEEP_REDEEMED — beside the member row it had
just produced, and above the links that still wait for somebody, which are
the only ones there is anything to do about. The node's own `member list`
had never shown them: it selects `used_at IS NULL`.
The listing now selects `redeemed_by IS NULL`, and drops the `redeemed`
status and the `redeemed_by` field with it. The row itself still lives for
KEEP_REDEEMED, which is what lets a reload or a second tab of the invitation
page be answered rather than refused; its comment says that now instead of
naming a list it is no longer in.
The SPA filters too, because the desktop client's copy of this interface can
be newer than the hub it is signed into.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Diffstat (limited to 'packages/meshbay-hub/tests/test_invite_links.py')
| -rw-r--r-- | packages/meshbay-hub/tests/test_invite_links.py | 32 |
1 files changed, 31 insertions, 1 deletions
diff --git a/packages/meshbay-hub/tests/test_invite_links.py b/packages/meshbay-hub/tests/test_invite_links.py index 1b60dc1..461cf8a 100644 --- a/packages/meshbay-hub/tests/test_invite_links.py +++ b/packages/meshbay-hub/tests/test_invite_links.py @@ -224,8 +224,38 @@ async def test_a_used_link_cannot_be_cancelled_here(client): r = await client.delete(f"/v1/groups/{gid}/invite-links/{link['link_id']}", headers=owner["h"]) assert r.status_code == 409 + + +@pytest.mark.asyncio +async def test_a_used_link_leaves_the_owners_list(client, db_session): + """The invitee is a member now; the link saying so as well is clutter. + + The row itself stays for `KEEP_REDEEMED`, which is what lets a reload of + the invitation page answer the account that used it instead of refusing. + """ + owner = await _account(client, "gone_owner") + invitee = await _account(client, "gone_invitee", email="invitee@example.test") + gid = await _group(client, owner) + waiting = (await _link(client, owner, gid, email="other@example.test")).json() + link = (await _link(client, owner, gid)).json() + r = await client.get(f"/v1/groups/{gid}/invite-links", headers=owner["h"]) - assert r.json()["links"][0]["redeemed_by"] == "used_invitee" + assert {row["link_id"] for row in r.json()["links"]} == {waiting["link_id"], + link["link_id"]} + + r = await client.post("/v1/invite-links/redeem", json={"ticket": link["ticket"]}, + headers=invitee["h"]) + assert r.status_code == 200, r.text + + r = await client.get(f"/v1/groups/{gid}/invite-links", headers=owner["h"]) + rows = r.json()["links"] + assert [row["link_id"] for row in rows] == [waiting["link_id"]] + assert "redeemed" not in r.text + # Still on the hub, so the invitee's second tab is answered, not refused. + assert (await db_session.get(GroupInviteLink, link["link_id"])) is not None + r = await client.post("/v1/invite-links/preview", json={"ticket": link["ticket"]}, + headers=invitee["h"]) + assert r.status_code == 200 and r.json()["already_member"] is True # ── What the hub mails ─────────────────────────────────────────────────────── |