2026-03-20 20:44:00 +02:00
|
|
|
"""
|
|
|
|
|
Integration tests for POST /api/register.
|
2026-03-21 08:12:01 +02:00
|
|
|
|
|
|
|
|
UUID notes: RegisterRequest.uuid requires a valid UUID v4 pattern
|
|
|
|
|
(^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$).
|
|
|
|
|
All UUID constants below satisfy this constraint.
|
|
|
|
|
|
|
|
|
|
BATON-SEC-003: /api/register now returns api_key in the response.
|
2026-03-20 20:44:00 +02:00
|
|
|
"""
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
os.environ.setdefault("BOT_TOKEN", "test-bot-token")
|
|
|
|
|
os.environ.setdefault("CHAT_ID", "-1001234567890")
|
|
|
|
|
os.environ.setdefault("WEBHOOK_SECRET", "test-webhook-secret")
|
|
|
|
|
os.environ.setdefault("WEBHOOK_URL", "https://example.com/api/webhook/telegram")
|
|
|
|
|
os.environ.setdefault("FRONTEND_ORIGIN", "http://localhost:3000")
|
2026-03-21 08:12:01 +02:00
|
|
|
os.environ.setdefault("ADMIN_TOKEN", "test-admin-token")
|
2026-03-20 20:44:00 +02:00
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
from tests.conftest import make_app_client
|
|
|
|
|
|
2026-03-21 08:12:01 +02:00
|
|
|
# Valid UUID v4 constants for register tests
|
|
|
|
|
_UUID_REG_1 = "b0000001-0000-4000-8000-000000000001"
|
|
|
|
|
_UUID_REG_2 = "b0000002-0000-4000-8000-000000000002"
|
|
|
|
|
_UUID_REG_3 = "b0000003-0000-4000-8000-000000000003"
|
|
|
|
|
_UUID_REG_4 = "b0000004-0000-4000-8000-000000000004"
|
|
|
|
|
_UUID_REG_5 = "b0000005-0000-4000-8000-000000000005"
|
|
|
|
|
_UUID_REG_6 = "b0000006-0000-4000-8000-000000000006"
|
|
|
|
|
|
2026-03-20 20:44:00 +02:00
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_register_new_user_success():
|
2026-03-21 08:12:01 +02:00
|
|
|
"""POST /api/register returns 200 with user_id > 0 and api_key."""
|
2026-03-20 20:44:00 +02:00
|
|
|
async with make_app_client() as client:
|
|
|
|
|
resp = await client.post(
|
|
|
|
|
"/api/register",
|
2026-03-21 08:12:01 +02:00
|
|
|
json={"uuid": _UUID_REG_1, "name": "Alice"},
|
2026-03-20 20:44:00 +02:00
|
|
|
)
|
|
|
|
|
assert resp.status_code == 200
|
|
|
|
|
data = resp.json()
|
|
|
|
|
assert data["user_id"] > 0
|
2026-03-21 08:12:01 +02:00
|
|
|
assert data["uuid"] == _UUID_REG_1
|
|
|
|
|
assert "api_key" in data
|
|
|
|
|
assert len(data["api_key"]) == 64 # secrets.token_hex(32) = 64 hex chars
|
2026-03-20 20:44:00 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_register_idempotent():
|
|
|
|
|
"""Registering the same uuid twice returns the same user_id."""
|
|
|
|
|
async with make_app_client() as client:
|
|
|
|
|
r1 = await client.post(
|
|
|
|
|
"/api/register",
|
2026-03-21 08:12:01 +02:00
|
|
|
json={"uuid": _UUID_REG_2, "name": "Bob"},
|
2026-03-20 20:44:00 +02:00
|
|
|
)
|
|
|
|
|
r2 = await client.post(
|
|
|
|
|
"/api/register",
|
2026-03-21 08:12:01 +02:00
|
|
|
json={"uuid": _UUID_REG_2, "name": "Bob"},
|
2026-03-20 20:44:00 +02:00
|
|
|
)
|
|
|
|
|
assert r1.status_code == 200
|
|
|
|
|
assert r2.status_code == 200
|
|
|
|
|
assert r1.json()["user_id"] == r2.json()["user_id"]
|
|
|
|
|
|
|
|
|
|
|
2026-03-21 08:12:01 +02:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_register_idempotent_returns_api_key_on_every_call():
|
|
|
|
|
"""Each registration call returns an api_key (key rotation on re-register)."""
|
|
|
|
|
async with make_app_client() as client:
|
|
|
|
|
r1 = await client.post(
|
|
|
|
|
"/api/register",
|
|
|
|
|
json={"uuid": _UUID_REG_3, "name": "Carol"},
|
|
|
|
|
)
|
|
|
|
|
r2 = await client.post(
|
|
|
|
|
"/api/register",
|
|
|
|
|
json={"uuid": _UUID_REG_3, "name": "Carol"},
|
|
|
|
|
)
|
|
|
|
|
assert r1.status_code == 200
|
|
|
|
|
assert r2.status_code == 200
|
|
|
|
|
assert "api_key" in r1.json()
|
|
|
|
|
assert "api_key" in r2.json()
|
|
|
|
|
|
|
|
|
|
|
2026-03-20 20:44:00 +02:00
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_register_empty_name_returns_422():
|
|
|
|
|
"""Empty name must fail validation with 422."""
|
|
|
|
|
async with make_app_client() as client:
|
|
|
|
|
resp = await client.post(
|
|
|
|
|
"/api/register",
|
2026-03-21 08:12:01 +02:00
|
|
|
json={"uuid": _UUID_REG_4, "name": ""},
|
2026-03-20 20:44:00 +02:00
|
|
|
)
|
|
|
|
|
assert resp.status_code == 422
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_register_missing_uuid_returns_422():
|
|
|
|
|
"""Missing uuid field must return 422."""
|
|
|
|
|
async with make_app_client() as client:
|
|
|
|
|
resp = await client.post(
|
|
|
|
|
"/api/register",
|
|
|
|
|
json={"name": "Charlie"},
|
|
|
|
|
)
|
|
|
|
|
assert resp.status_code == 422
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_register_missing_name_returns_422():
|
|
|
|
|
"""Missing name field must return 422."""
|
|
|
|
|
async with make_app_client() as client:
|
|
|
|
|
resp = await client.post(
|
|
|
|
|
"/api/register",
|
2026-03-21 08:12:01 +02:00
|
|
|
json={"uuid": _UUID_REG_4},
|
|
|
|
|
)
|
|
|
|
|
assert resp.status_code == 422
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_register_invalid_uuid_format_returns_422():
|
|
|
|
|
"""Non-UUID4 string as uuid must return 422."""
|
|
|
|
|
async with make_app_client() as client:
|
|
|
|
|
resp = await client.post(
|
|
|
|
|
"/api/register",
|
|
|
|
|
json={"uuid": "not-a-uuid", "name": "Dave"},
|
2026-03-20 20:44:00 +02:00
|
|
|
)
|
|
|
|
|
assert resp.status_code == 422
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_register_user_stored_in_db():
|
|
|
|
|
"""After register, the user is persisted (second call returns same id)."""
|
|
|
|
|
async with make_app_client() as client:
|
|
|
|
|
r1 = await client.post(
|
|
|
|
|
"/api/register",
|
2026-03-21 08:12:01 +02:00
|
|
|
json={"uuid": _UUID_REG_5, "name": "Dana"},
|
2026-03-20 20:44:00 +02:00
|
|
|
)
|
|
|
|
|
r2 = await client.post(
|
|
|
|
|
"/api/register",
|
2026-03-21 08:12:01 +02:00
|
|
|
json={"uuid": _UUID_REG_5, "name": "Dana"},
|
2026-03-20 20:44:00 +02:00
|
|
|
)
|
|
|
|
|
assert r1.json()["user_id"] == r2.json()["user_id"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
|
|
|
async def test_register_response_contains_uuid():
|
|
|
|
|
"""Response body includes the submitted uuid."""
|
|
|
|
|
async with make_app_client() as client:
|
|
|
|
|
resp = await client.post(
|
|
|
|
|
"/api/register",
|
2026-03-21 08:12:01 +02:00
|
|
|
json={"uuid": _UUID_REG_6, "name": "Eve"},
|
2026-03-20 20:44:00 +02:00
|
|
|
)
|
2026-03-21 08:12:01 +02:00
|
|
|
assert resp.json()["uuid"] == _UUID_REG_6
|