Files
devplacepy/tests/api/nodes/ticket.py
T
retoorandClaude Sonnet 5.5 42b81271b8 Add community node socket: devices, tickets, consent gate, kill switch
Phase 3 of plan.md. Device registration with one-time secrets, scoped single-use
socket tickets, the lock-owner WS /nodes/ws with handshake and heartbeat eviction,
a single batch ConsentGate, and NodesService which disconnects nodes within one tick
when consent, device or the nodes_enabled kill switch changes. A reconcile of 2000
sessions measured about 110 ms after replacing per-session queries with batches.
Devii may withdraw but never grant bandwidth_sharing.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 09:14:53 +02:00

68 lines
2.7 KiB
Python

# retoor <retoor@molodetz.nl>
from devplacepy.database import set_consent
from devplacepy.database.nodes import LIMITS, suspend_device
from devplacepy.services.messaging.tickets import redeem_ticket, redeem_ticket_row
from tests.api.nodes.devices import create
def bearer(secret):
return {"Authorization": f"Bearer {secret}"}
def registered(client, member):
body = create(client, member).json()
return body["device"]["uid"], body["secret"]
def test_no_credentials_or_a_wrong_secret_is_401(client):
assert client.post("/nodes/ticket").status_code == 401
assert client.post("/nodes/ticket", headers=bearer("nope")).status_code == 401
assert client.post("/nodes/ticket", headers=bearer("dpn_unknown")).status_code == 401
def test_a_session_api_key_is_not_a_device_secret(client, member):
assert client.post("/nodes/ticket", headers=member["headers"]).status_code == 401
def test_a_valid_secret_gets_a_short_single_use_ticket(client, member):
device_uid, secret = registered(client, member)
response = client.post("/nodes/ticket", headers=bearer(secret))
assert response.status_code == 200
assert response.headers["cache-control"] == "no-store"
body = response.json()
assert body["expires_in"] == LIMITS.ticket_ttl_seconds
assert body["ws_path"] == "/nodes/ws"
row = redeem_ticket_row(body["ticket"], "nodes")
assert row["device_uid"] == device_uid and row["user_uid"] == member["uid"]
assert redeem_ticket_row(body["ticket"], "nodes") is None
def test_a_node_ticket_cannot_open_a_chat_socket(client, member):
_, secret = registered(client, member)
ticket = client.post("/nodes/ticket", headers=bearer(secret)).json()["ticket"]
assert redeem_ticket(ticket) is None
assert redeem_ticket_row(ticket, "nodes") is not None
def test_withdrawing_consent_stops_new_tickets(client, member):
_, secret = registered(client, member)
set_consent("user", member["uid"], "bandwidth_sharing", False)
response = client.post("/nodes/ticket", headers=bearer(secret))
assert response.status_code == 403
assert response.json()["error"]["reason"] == "no_consent"
def test_a_suspended_device_gets_no_ticket(client, member):
device_uid, secret = registered(client, member)
suspend_device(device_uid, "test", 30)
response = client.post("/nodes/ticket", headers=bearer(secret))
assert response.status_code == 403
assert response.json()["error"]["reason"] == "suspended"
def test_a_deleted_device_secret_stops_working(client, member):
device_uid, secret = registered(client, member)
client.post(f"/nodes/devices/{device_uid}/delete", headers=member["headers"])
assert client.post("/nodes/ticket", headers=bearer(secret)).status_code == 401