"""Integration tests for group self-service: create, join, members.""" import pytest from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey from meshbay_common.crypto import pk_to_b64 def _gen_user_keys(): sk_ed = Ed25519PrivateKey.generate() sk_x = X25519PrivateKey.generate() return pk_to_b64(sk_ed.public_key()), pk_to_b64(sk_x.public_key()) async def _register(client, username, email="test@x.com", password="testpass99"): pk_ed, pk_x = _gen_user_keys() r = await client.post("/v1/users/register", json={ "username": username, "email": email, "password": password, "pk_user_ed25519": pk_ed, "pk_user_x25519": pk_x, }) assert r.status_code == 201 return r.json()["user_id"] async def _login(client, username, password="testpass99"): r = await client.post("/v1/users/login", json={ "username": username, "password": password, }) assert r.status_code == 200 return r.json()["access_token"] async def _create_group(client, token, name="test-group", visibility="public", join_policy="open"): r = await client.post("/v1/groups", json={ "name": name, "visibility": visibility, "join_policy": join_policy, }, headers={"Authorization": f"Bearer {token}"}) assert r.status_code == 201 return r.json()["group_id"] @pytest.mark.asyncio async def test_create_group(client): await _register(client, "alice", email="a@x.com") token = await _login(client, "alice") r = await client.post("/v1/groups", json={ "name": "my-group", "visibility": "public", "join_policy": "open", }, headers={"Authorization": f"Bearer {token}"}) assert r.status_code == 201 data = r.json() assert data["name"] == "my-group" assert "group_id" in data @pytest.mark.asyncio async def test_join_open_group(client): await _register(client, "alice", email="a@x.com") alice_token = await _login(client, "alice") gid = await _create_group(client, alice_token, "open-group") await _register(client, "bob", email="b@x.com") bob_token = await _login(client, "bob") r = await client.post(f"/v1/groups/{gid}/join", headers={"Authorization": f"Bearer {bob_token}"}) assert r.status_code == 200 assert r.json()["status"] == "joined" @pytest.mark.asyncio async def test_join_invite_group_rejected(client): await _register(client, "alice", email="a@x.com") alice_token = await _login(client, "alice") gid = await _create_group(client, alice_token, "invite-group", join_policy="invite") await _register(client, "bob", email="b@x.com") bob_token = await _login(client, "bob") r = await client.post(f"/v1/groups/{gid}/join", headers={"Authorization": f"Bearer {bob_token}"}) assert r.status_code == 403 @pytest.mark.asyncio async def test_join_already_member(client): await _register(client, "alice", email="a@x.com") alice_token = await _login(client, "alice") gid = await _create_group(client, alice_token, "dup-group") r = await client.post(f"/v1/groups/{gid}/join", headers={"Authorization": f"Bearer {alice_token}"}) assert r.status_code == 409 @pytest.mark.asyncio async def test_group_members(client): await _register(client, "alice", email="a@x.com") alice_token = await _login(client, "alice") gid = await _create_group(client, alice_token, "team-group") await _register(client, "bob", email="b@x.com") bob_token = await _login(client, "bob") await client.post(f"/v1/groups/{gid}/join", headers={"Authorization": f"Bearer {bob_token}"}) r = await client.get(f"/v1/groups/{gid}/members", headers={"Authorization": f"Bearer {alice_token}"}) assert r.status_code == 200 data = r.json() usernames = [m["username"] for m in data["members"]] assert "alice" in usernames assert "bob" in usernames assert data["admin_id"] is not None @pytest.mark.asyncio async def test_group_members_non_member_denied(client): await _register(client, "alice", email="a@x.com") alice_token = await _login(client, "alice") gid = await _create_group(client, alice_token, "private-group", visibility="private", join_policy="invite") await _register(client, "bob", email="b@x.com") bob_token = await _login(client, "bob") r = await client.get(f"/v1/groups/{gid}/members", headers={"Authorization": f"Bearer {bob_token}"}) assert r.status_code == 403 @pytest.mark.asyncio async def test_group_search(client): await _register(client, "alice", email="a@x.com") token = await _login(client, "alice") await _create_group(client, token, "alpha-team") await _create_group(client, token, "beta-team") r = await client.get("/v1/groups?q=alpha") assert r.status_code == 200 names = [g["name"] for g in r.json()["groups"]] assert "alpha-team" in names assert "beta-team" not in names @pytest.mark.asyncio async def test_join_triggers_notification(client): await _register(client, "alice", email="a@x.com") alice_token = await _login(client, "alice") gid = await _create_group(client, alice_token, "notif-group") await _register(client, "bob", email="b@x.com") bob_token = await _login(client, "bob") await client.post(f"/v1/groups/{gid}/join", headers={"Authorization": f"Bearer {bob_token}"}) r = await client.get(f"/v1/groups/{gid}/members", headers={"Authorization": f"Bearer {alice_token}"}) assert len(r.json()["members"]) == 2