"""Tests for moderation — reports + blocklist.""" 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 from meshbay_hub.api.deps import set_admin_usernames FAKE_HASH = "a" * 64 # valid blake3 hex @pytest.fixture async def auth_headers(client): sk_ed = Ed25519PrivateKey.generate() sk_x = X25519PrivateKey.generate() await client.post("/v1/users/register", json={ "username": "mod_admin", "email": "m@t.com", "password": "modpass99", "pk_user_ed25519": pk_to_b64(sk_ed.public_key()), "pk_user_x25519": pk_to_b64(sk_x.public_key()), }) r = await client.post("/v1/users/login", json={"username": "mod_admin", "password": "modpass99"}) set_admin_usernames(["mod_admin"]) return {"Authorization": f"Bearer {r.json()['access_token']}"} @pytest.mark.asyncio async def test_report_content_logged(client): r = await client.post("/v1/reports", json={ "content_hash": FAKE_HASH, "reason": "illegal"}) assert r.status_code == 201 data = r.json() assert data["report_count"] == 1 assert data["status"] == "logged" @pytest.mark.asyncio async def test_auto_block_on_threshold(client): """Second report triggers auto-block.""" hash2 = "b" * 64 await client.post("/v1/reports", json={"content_hash": hash2, "reason": "spam"}) r = await client.post("/v1/reports", json={"content_hash": hash2, "reason": "spam"}) assert r.json()["status"] == "auto_blocked" assert r.json()["report_count"] == 2 @pytest.mark.asyncio async def test_blocklist_check(client): hash3 = "c" * 64 # Not blocked yet r = await client.get(f"/v1/blocklist/check?hash={hash3}") assert r.json()["blocked"] is False # Report twice to auto-block await client.post("/v1/reports", json={"content_hash": hash3, "reason": "illegal"}) await client.post("/v1/reports", json={"content_hash": hash3, "reason": "illegal"}) r = await client.get(f"/v1/blocklist/check?hash={hash3}") assert r.json()["blocked"] is True @pytest.mark.asyncio async def test_admin_add_remove_blocklist(client, auth_headers): hash4 = "d" * 64 r = await client.post("/v1/admin/blocklist", json={"content_hash": hash4, "reason": "csam"}, headers=auth_headers) assert r.status_code == 201 r = await client.get(f"/v1/blocklist/check?hash={hash4}") assert r.json()["blocked"] is True r = await client.delete(f"/v1/admin/blocklist/{hash4}", headers=auth_headers) assert r.status_code == 200 r = await client.get(f"/v1/blocklist/check?hash={hash4}") assert r.json()["blocked"] is False @pytest.mark.asyncio async def test_invalid_hash_rejected(client): r = await client.post("/v1/reports", json={ "content_hash": "not-a-valid-blake3-hash", "reason": "test"}) assert r.status_code == 422 @pytest.mark.asyncio async def test_full_blocklist(client, auth_headers): hash5 = "e" * 64 await client.post("/v1/admin/blocklist", json={"content_hash": hash5, "reason": "test"}, headers=auth_headers) r = await client.get("/v1/blocklist") assert r.status_code == 200 assert hash5 in r.json()["hashes"]