diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-09-25 17:10:15 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-09-25 17:10:15 +0200 |
| commit | 90c69477d5f701158112b3c294eff26312f89da6 (patch) | |
| tree | 2691ca096da5cf93482bc750c7a03599fbc44dc8 /packages/meshbay-hub/tests/test_invite_links.py | |
| parent | 8bb94a39609f57be2c486f579849eebb04606808 (diff) | |
| download | meshbay-main.tar.gz | |
A link is redeemable by whoever opens it first, so it can be sent by any
messaging app. The address is optional (mail + label only); a link lives
7 days, fixed. Adds a Share button; see MESHBAY_DESIGN.md §3.4.
Co-Authored-By: Claude Opus 5.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 | 45 |
1 files changed, 29 insertions, 16 deletions
diff --git a/packages/meshbay-hub/tests/test_invite_links.py b/packages/meshbay-hub/tests/test_invite_links.py index 461cf8a..2217cfe 100644 --- a/packages/meshbay-hub/tests/test_invite_links.py +++ b/packages/meshbay-hub/tests/test_invite_links.py @@ -63,22 +63,16 @@ def sent(monkeypatch): # ── Who gets in ────────────────────────────────────────────────────────────── @pytest.mark.asyncio -async def test_the_addressed_account_joins_and_nobody_else(client, db_session): +async def test_whoever_opens_it_first_joins_and_nobody_after(client, db_session): + # A link travels by any messaging app, so the address the owner typed binds + # nothing: an account registered with another one redeems it. owner = await _account(client, "link_owner") gid = await _group(client, owner) r = await _link(client, owner, gid, email="Invitee@Example.test") assert r.status_code == 201, r.text ticket = r.json()["ticket"] - mallory = await _account(client, "link_mallory") - for route in ("preview", "redeem"): - r = await client.post(f"/v1/invite-links/{route}", json={"ticket": ticket}, - headers=mallory["h"]) - assert r.status_code == 403 and r.json()["detail"] == "invite_other_account" - assert "invitee" not in r.text.lower(), "the refusal must not name the address" - - # Registered with the address the owner typed, case aside. - invitee = await _account(client, "link_invitee", email="invitee@example.test") + invitee = await _account(client, "link_invitee", email="elsewhere@example.test") r = await client.post("/v1/invite-links/preview", json={"ticket": ticket}, headers=invitee["h"]) assert r.status_code == 200, r.text @@ -102,11 +96,30 @@ async def test_the_addressed_account_joins_and_nobody_else(client, db_session): GroupMember.group_id == gid))).scalars().all() assert len(rows) == 2 - # And the one who comes after, even with the right address, gets nothing: - # a twin account cannot exist (addresses are unique), so try the other. - r = await client.post("/v1/invite-links/redeem", json={"ticket": ticket}, - headers=mallory["h"]) - assert r.status_code == 404 + # Whoever comes after, even with the address the owner typed, gets nothing. + late = await _account(client, "link_late", email="invitee@example.test") + for route in ("preview", "redeem"): + r = await client.post(f"/v1/invite-links/{route}", json={"ticket": ticket}, + headers=late["h"]) + assert r.status_code == 404 and r.json()["detail"] == "invite_not_valid" + + +@pytest.mark.asyncio +async def test_a_link_needs_no_address_unless_it_is_mailed(client, db_session, sent): + owner = await _account(client, "noaddr_owner") + gid = await _group(client, owner) + r = await _link(client, owner, gid, email="") + assert r.status_code == 201, r.text + assert r.json()["email_status"] == "not_requested" + row = (await db_session.execute(select(GroupInviteLink))).scalar_one() + assert row.email_masked is None + listed = (await client.get(f"/v1/groups/{gid}/invite-links", + headers=owner["h"])).json()["links"] + assert [link["email"] for link in listed] == [""] + + r = await _link(client, owner, gid, email="", send_email=True, + node_pk=NODE_PK, code=CODE) + assert r.status_code == 422 and sent == [] @pytest.mark.asyncio @@ -115,7 +128,7 @@ async def test_a_ticket_is_stored_only_as_a_hash(client, db_session): gid = await _group(client, owner) ticket = (await _link(client, owner, gid)).json()["ticket"] row = (await db_session.execute(select(GroupInviteLink))).scalar_one() - assert ticket not in (row.ticket_hash, row.email_masked, row.email_hash) + assert ticket not in (row.ticket_hash, row.email_masked) assert row.ticket_hash == invite_links.ticket_hash(ticket) assert "invitee@" not in row.email_masked |