diff options
Diffstat (limited to 'packages/meshbay-hub/tests/test_hub_api.py')
| -rw-r--r-- | packages/meshbay-hub/tests/test_hub_api.py | 45 |
1 files changed, 27 insertions, 18 deletions
diff --git a/packages/meshbay-hub/tests/test_hub_api.py b/packages/meshbay-hub/tests/test_hub_api.py index 37e8e4f..2afd27b 100644 --- a/packages/meshbay-hub/tests/test_hub_api.py +++ b/packages/meshbay-hub/tests/test_hub_api.py @@ -3,13 +3,11 @@ Integration tests for the Hub API. Uses SQLite in-memory + httpx.AsyncClient — no PostgreSQL, no network. """ -import base64 +from datetime import UTC + import pytest -import pytest_asyncio from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey -from cryptography.hazmat.primitives import serialization - from meshbay_common.crypto import pk_to_b64 from meshbay_hub.api.deps import set_admin_usernames @@ -32,7 +30,8 @@ async def _announce_signed(client, token: str) -> tuple[str, str]: The node key is independent of the user's identity key, so this mints a fresh one and signs the domain-separated announce message with it. """ - import base64 as _b64, time as _t + import base64 as _b64 + import time as _t me = await client.get("/v1/users/me", headers={"Authorization": f"Bearer {token}"}) @@ -261,7 +260,9 @@ async def test_group_member_add(client): "pk_user_ed25519": pk_ed, "pk_user_x25519": pk_x}) alice_token = (await client.post("/v1/users/login", - json={"username": "alice2_test", "password": "alicepass99"})).json()["access_token"] + json={"username": "alice2_test", + "password": "alicepass99"}) + ).json()["access_token"] a_hdrs = {"Authorization": f"Bearer {alice_token}"} @@ -299,9 +300,13 @@ async def test_non_admin_cannot_add_member(client): "pk_user_ed25519": pk_ed, "pk_user_x25519": pk_x}) charlie_token = (await client.post("/v1/users/login", - json={"username": "charlie_test", "password": "charliepass"})).json()["access_token"] + json={"username": "charlie_test", + "password": "charliepass"}) + ).json()["access_token"] dan_token = (await client.post("/v1/users/login", - json={"username": "dan_test", "password": "danpass1234"})).json()["access_token"] + json={"username": "dan_test", + "password": "danpass1234"}) + ).json()["access_token"] r = await client.post("/v1/groups", json={"name": "charlies-group"}, headers={"Authorization": f"Bearer {charlie_token}"}) @@ -339,7 +344,9 @@ async def test_jwt_contains_groups_claim(client): # Alice creates a group and adds Bob alice_token = (await client.post("/v1/users/login", - json={"username": "grp_alice", "password": "alicepass99"})).json()["access_token"] + json={"username": "grp_alice", + "password": "alicepass99"}) + ).json()["access_token"] r = await client.post("/v1/groups", json={"name": "testgroup"}, headers={"Authorization": f"Bearer {alice_token}"}) group_id = r.json()["group_id"] @@ -399,9 +406,10 @@ async def test_my_groups(client, db_session): # A group no node has announced is shown to its owner only — a member would # otherwise see a name they cannot open. Stamped here so the rest of this # test is about membership, which is what it was written for. - from datetime import datetime, timezone + from datetime import datetime + from meshbay_hub.db.models import Group - (await db_session.get(Group, group_id)).hosted_at = datetime.now(timezone.utc) + (await db_session.get(Group, group_id)).hosted_at = datetime.now(UTC) await db_session.commit() # Re-login to get fresh token with group claims @@ -434,7 +442,6 @@ async def test_my_groups(client, db_session): @pytest.mark.asyncio async def test_group_online_nodes(client): """GET /v1/groups/{id}/nodes returns online nodes serving the group.""" - import json from meshbay_hub.api.revocation import _connected_nodes, _node_groups pk_ed, pk_x, _ = _gen_user_keys() @@ -541,7 +548,7 @@ async def test_admin_can_revoke(client): @pytest.mark.asyncio async def test_email_encrypted_at_rest(client): """Email stored in DB must not contain plaintext address.""" - from meshbay_hub.auth import encrypt_email, decrypt_email + from meshbay_hub.auth import decrypt_email, encrypt_email encrypted = encrypt_email("test@example.com") assert "@" not in encrypted assert decrypt_email(encrypted) == "test@example.com" @@ -589,9 +596,10 @@ async def test_password_rehash_on_login(client, app): user = result.scalar_one() user.pw_version = 1 # Re-hash with v1 params so verify_password(version=1) succeeds - from meshbay_hub.auth import _ARGON2_VERSIONS, _ARGON2_KEY_LEN, _ARGON2_LANES - from cryptography.hazmat.primitives.kdf.argon2 import Argon2id import os + + from cryptography.hazmat.primitives.kdf.argon2 import Argon2id + from meshbay_hub.auth import _ARGON2_KEY_LEN, _ARGON2_LANES, _ARGON2_VERSIONS salt = os.urandom(16) params = _ARGON2_VERSIONS[1] pw_hash = Argon2id( @@ -760,13 +768,14 @@ async def test_webrtc_signaling_roundtrip(client, app): @pytest.mark.asyncio async def test_ip_log_cleanup(app): """Old IP log entries are purged by cleanup task.""" - from datetime import datetime, timezone, timedelta + from datetime import datetime, timedelta + from meshbay_hub.db.engine import get_db from meshbay_hub.db.models import IPLog from meshbay_hub.tasks.cleanup import purge_old_ip_logs async for db in get_db(): - old_ts = datetime.now(timezone.utc) - timedelta(days=400) + old_ts = datetime.now(UTC) - timedelta(days=400) db.add(IPLog(event="test_old", ip_address="1.2.3.4", timestamp=old_ts)) db.add(IPLog(event="test_recent", ip_address="5.6.7.8")) await db.commit() @@ -774,7 +783,7 @@ async def test_ip_log_cleanup(app): deleted = await purge_old_ip_logs(db, retention_days=365) assert deleted == 1 - from sqlalchemy import select, func + from sqlalchemy import func, select count = (await db.execute( select(func.count()).where(IPLog.event.in_(["test_old", "test_recent"])) )).scalar_one() |