aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/tests/test_ops_links.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-25 17:10:15 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-25 17:10:15 +0200
commit90c69477d5f701158112b3c294eff26312f89da6 (patch)
tree2691ca096da5cf93482bc750c7a03599fbc44dc8 /packages/meshbay-node/tests/test_ops_links.py
parent8bb94a39609f57be2c486f579849eebb04606808 (diff)
downloadmeshbay-90c69477d5f701158112b3c294eff26312f89da6.tar.gz
feat: invitation links no longer bound to an e-mail addressHEADmain
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-node/tests/test_ops_links.py')
-rw-r--r--packages/meshbay-node/tests/test_ops_links.py32
1 files changed, 26 insertions, 6 deletions
diff --git a/packages/meshbay-node/tests/test_ops_links.py b/packages/meshbay-node/tests/test_ops_links.py
index 00258d3..9ad01d2 100644
--- a/packages/meshbay-node/tests/test_ops_links.py
+++ b/packages/meshbay-node/tests/test_ops_links.py
@@ -1,13 +1,15 @@
"""
Invitation links from the operator's own machine (`member invite --link`).
-The CLI makes both halves itself: the node's code, then the hub's ticket bound
-to an address, then the link. What can go wrong is a half left behind — a code
-the hub never ticketed, occupying one of the group's places, or a ticket whose
-code was cancelled — and the CLI asking the hub to mail, which it may not.
+The CLI makes both halves itself: the node's code, then the hub's ticket, then
+the link. The address is optional and only labels the link. What can go wrong
+is a half left behind — a code the hub never ticketed, occupying one of the
+group's places, or a ticket whose code was cancelled — and the CLI asking the
+hub to mail, which it may not.
"""
import re
+from datetime import UTC, datetime, timedelta
import pytest
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
@@ -76,12 +78,30 @@ async def test_a_refused_ticket_takes_the_code_back(roster):
assert [i for i in await roster.list_invites() if i["kind"] == KIND_LINK] == []
-async def test_an_address_is_required(roster):
+async def test_an_address_is_optional_but_must_be_one_when_given(roster):
+ hub = _Hub()
+ out = await ops.create_link_invitation(_state(roster, hub), GROUP)
+ assert LINK.match(out["link"]), out["link"]
+ assert hub.created == [(GROUP, "", out["invite_id"])]
with pytest.raises(ops.OpError) as refused:
- await ops.create_link_invitation(_state(roster, _Hub()), GROUP, "alice")
+ await ops.create_link_invitation(_state(roster, hub), GROUP, "alice")
assert refused.value.status == 422
+async def test_a_link_lives_seven_days_whatever_the_invite_setting(roster):
+ class _Node:
+ invite_ttl_hours = 24 * 30
+
+ class _Config:
+ node = _Node()
+
+ state = _state(roster, _Hub())
+ state["config"] = _Config()
+ out = await ops.create_link_invite(state, GROUP)
+ left = datetime.fromisoformat(out["expires_at"]) - datetime.now(UTC)
+ assert timedelta(days=6, hours=23) < left <= timedelta(days=7)
+
+
async def test_cancelling_takes_back_both_halves(roster):
hub = _Hub()
state = _state(roster, hub)