""" Tests for BATON-FIX-012: UUID v4 validation regression guard. BATON-SEC-005 added UUID v4 pattern validation to RegisterRequest.uuid and SignalRequest.user_id. Tests in test_db.py / test_baton_005.py / test_telegram.py previously used placeholder strings ('uuid-001', 'create-uuid-001', 'agg-uuid-001') that are not valid UUID v4 — causing 25 regressions. This file locks down the behaviour so the same mistake cannot recur silently: - Old-style placeholder strings are rejected by Pydantic - All UUID constants used across the fixed test files are valid UUID v4 - RegisterRequest and SignalRequest accept exactly-valid v4 UUIDs - They reject strings that violate version (bit 3 of field-3 must be 4) or variant (top bits of field-4 must be 10xx) requirements """ 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") os.environ.setdefault("ADMIN_TOKEN", "test-admin-token") import pytest from pydantic import ValidationError from backend.models import RegisterRequest, SignalRequest # --------------------------------------------------------------------------- # UUID constants from fixed test files — all must be valid UUID v4 # --------------------------------------------------------------------------- # test_db.py constants (_UUID_DB_1 .. _UUID_DB_6) _DB_UUIDS = [ "d0000001-0000-4000-8000-000000000001", "d0000002-0000-4000-8000-000000000002", "d0000003-0000-4000-8000-000000000003", "d0000004-0000-4000-8000-000000000004", "d0000005-0000-4000-8000-000000000005", "d0000006-0000-4000-8000-000000000006", ] # test_baton_005.py constants (_UUID_ADM_*) _ADM_UUIDS = [ "e0000000-0000-4000-8000-000000000000", "e0000001-0000-4000-8000-000000000001", "e0000002-0000-4000-8000-000000000002", "e0000003-0000-4000-8000-000000000003", "e0000004-0000-4000-8000-000000000004", "e0000005-0000-4000-8000-000000000005", "e0000006-0000-4000-8000-000000000006", "e0000007-0000-4000-8000-000000000007", "e0000008-0000-4000-8000-000000000008", "e0000009-0000-4000-8000-000000000009", "e000000a-0000-4000-8000-000000000010", ] # test_telegram.py constants (aggregator UUIDs) _AGG_UUIDS = [ "a9900001-0000-4000-8000-000000000001", "a9900099-0000-4000-8000-000000000099", ] + [f"a990000{i}-0000-4000-8000-00000000000{i}" for i in range(5)] # --------------------------------------------------------------------------- # Old-style placeholder UUIDs (pre-fix) must be rejected # --------------------------------------------------------------------------- @pytest.mark.parametrize("bad_uuid", [ "uuid-001", "uuid-002", "uuid-003", "uuid-004", "uuid-005", "uuid-006", "create-uuid-001", "create-uuid-002", "create-uuid-003", "pass-uuid-001", "pass-uuid-002", "block-uuid-001", "unblock-uuid-001", "delete-uuid-001", "delete-uuid-002", "regress-admin-uuid-001", "unauth-uuid-001", "agg-uuid-001", "agg-uuid-clr", ]) def test_register_request_rejects_old_placeholder_uuid(bad_uuid: str) -> None: """RegisterRequest.uuid must reject all pre-BATON-SEC-005 placeholder strings.""" with pytest.raises(ValidationError): RegisterRequest(uuid=bad_uuid, name="Test") @pytest.mark.parametrize("bad_uuid", [ "uuid-001", "agg-uuid-001", "create-uuid-001", ]) def test_signal_request_accepts_any_user_id_string(bad_uuid: str) -> None: """SignalRequest.user_id is optional (no pattern) — validation is at endpoint level.""" req = SignalRequest(user_id=bad_uuid, timestamp=1700000000000) assert req.user_id == bad_uuid # --------------------------------------------------------------------------- # All UUID constants from the fixed test files are valid UUID v4 # --------------------------------------------------------------------------- @pytest.mark.parametrize("valid_uuid", _DB_UUIDS) def test_register_request_accepts_db_uuid_constants(valid_uuid: str) -> None: """RegisterRequest accepts all _UUID_DB_* constants from test_db.py.""" req = RegisterRequest(uuid=valid_uuid, name="Test") assert req.uuid == valid_uuid @pytest.mark.parametrize("valid_uuid", _ADM_UUIDS) def test_register_request_accepts_adm_uuid_constants(valid_uuid: str) -> None: """RegisterRequest accepts all _UUID_ADM_* constants from test_baton_005.py.""" req = RegisterRequest(uuid=valid_uuid, name="Test") assert req.uuid == valid_uuid @pytest.mark.parametrize("valid_uuid", _AGG_UUIDS) def test_signal_request_accepts_agg_uuid_constants(valid_uuid: str) -> None: """SignalRequest accepts all aggregator UUID constants from test_telegram.py.""" req = SignalRequest(user_id=valid_uuid, timestamp=1700000000000) assert req.user_id == valid_uuid # --------------------------------------------------------------------------- # UUID v4 structural requirements — version digit and variant bits # --------------------------------------------------------------------------- def test_register_request_rejects_uuid_v1_version_digit() -> None: """UUID with version digit = 1 (not 4) must be rejected by RegisterRequest.""" with pytest.raises(ValidationError): # third group starts with '1' — version 1, not v4 RegisterRequest(uuid="550e8400-e29b-11d4-a716-446655440000", name="Test") def test_register_request_rejects_uuid_v3_version_digit() -> None: """UUID with version digit = 3 must be rejected.""" with pytest.raises(ValidationError): RegisterRequest(uuid="550e8400-e29b-31d4-a716-446655440000", name="Test") def test_signal_request_accepts_any_variant_bits() -> None: """SignalRequest.user_id is now optional and unvalidated (JWT auth doesn't use it).""" req = SignalRequest(user_id="550e8400-e29b-41d4-0716-446655440000", timestamp=1700000000000) assert req.user_id is not None def test_signal_request_without_user_id() -> None: """SignalRequest works without user_id (JWT auth mode).""" req = SignalRequest(timestamp=1700000000000) assert req.user_id is None def test_register_request_accepts_all_valid_v4_variants() -> None: """RegisterRequest accepts UUIDs with variant nibbles 8, 9, a, b.""" for variant in ("8", "9", "a", "b"): uuid = f"550e8400-e29b-41d4-{variant}716-446655440000" req = RegisterRequest(uuid=uuid, name="Test") assert req.uuid == uuid