import uuid import httpx import pytest from app.integrations import ( DependencyFailure, OpenLinesClient, SafetyClient, fresh_openlines_payload, ) from app.settings import Settings def settings() -> Settings: common = { "APP_ENV": "test", "DATABASE_URL": "postgresql+asyncpg://u:p@localhost/db", "REDIS_URL": "redis://localhost/0", "REDIS_REALTIME_URL": "redis://localhost/1", "KEYCLOAK_PUBLIC_URL": "https://auth.example", "KEYCLOAK_INTERNAL_URL": "http://keycloak:8080", "KEYCLOAK_REALM": "han", "KEYCLOAK_AUDIENCE": "api", "MESSAGE_SAFETY_URL": "http://safety:8080", "MESSAGE_SAFETY_SERVICE_TOKEN": "safety-token", "BITRIX_LOCAL_APP_BASE_URL": "http://bitrix:8080", "BITRIX_LOCAL_APP_INTERNAL_TOKEN": "bitrix-token", "BITRIX_API_INBOX_TOKEN": "inbox-token", "KEYCLOAK_SETTINGS_BRIDGE_TOKEN": "settings-token", "SELECTEL_S3_ENDPOINT_URL": "https://s3.example", "SELECTEL_S3_BUCKET_DOCUMENTS": "documents", "SELECTEL_S3_BUCKET_ATTACHMENTS": "attachments", "SELECTEL_S3_BUCKET_QUARANTINE": "quarantine", "SELECTEL_S3_ACCESS_KEY": "access", "SELECTEL_S3_SECRET_KEY": "secret", "CURSOR_HMAC_SECRET": "x" * 32, } return Settings.model_validate(common) @pytest.mark.asyncio async def test_safety_contract_status_and_service_token() -> None: async def handler(request: httpx.Request) -> httpx.Response: assert request.headers["X-Service-Token"] == "safety-token" assert request.url.path == "/internal/safety/v1/messages/check" return httpx.Response(203, json={"verdict": "pending", "task_id": "task-1"}) async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as http: result = await SafetyClient(settings(), http).check( {"message_id": str(uuid.uuid4()), "content_kind": "text", "text": "hello"}, "request-1", ) assert result == {"verdict": "pending", "task_id": "task-1", "_status": 203} @pytest.mark.asyncio async def test_safety_file_body_uses_exact_attachment_schema() -> None: attachment_id = uuid.uuid4() async def handler(request: httpx.Request) -> httpx.Response: body = __import__("json").loads(request.content) assert body["attachment"] == { "attachment_id": str(attachment_id), "quarantine_object_key": "quarantine/users/u/file", "mime_type": "application/pdf", "size_bytes": 42, "checksum": "sha256:" + "a" * 64, } assert "file" not in body return httpx.Response(200, json={"verdict": "allow"}) payload = { "message_id": str(uuid.uuid4()), "content_kind": "file", "text": "", "attachment": { "attachment_id": str(attachment_id), "quarantine_object_key": "quarantine/users/u/file", "mime_type": "application/pdf", "size_bytes": 42, "checksum": "sha256:" + "a" * 64, }, } async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as http: await SafetyClient(settings(), http).check(payload, "request-1") @pytest.mark.asyncio async def test_safety_auth_failure_is_dependency_failure() -> None: async def handler(_: httpx.Request) -> httpx.Response: return httpx.Response(401, json={"error": {"code": "service_unauthorized"}}) async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as http: with pytest.raises(DependencyFailure): await SafetyClient(settings(), http).check( {"message_id": str(uuid.uuid4()), "content_kind": "text", "text": "hello"}, "request-1", ) @pytest.mark.asyncio async def test_openlines_contract_uses_bearer_and_idempotency() -> None: message_id = uuid.uuid4() async def handler(request: httpx.Request) -> httpx.Response: assert request.headers["Authorization"] == "Bearer bitrix-token" assert request.headers["Idempotency-Key"] == str(message_id) assert request.url.path == "/internal/openlines/v1/messages" return httpx.Response(201, json={"status": "accepted"}) async with httpx.AsyncClient(transport=httpx.MockTransport(handler)) as http: result = await OpenLinesClient(settings(), http).send( message_id, {"message_id": str(message_id)}, "request-1" ) assert result["status"] == "accepted" @pytest.mark.asyncio async def test_openlines_payload_gets_fresh_download_url_without_storage_fields() -> None: class FakeS3: calls = 0 async def presign_get(self, bucket: str, key: str, ttl: int = 300) -> str: self.calls += 1 return f"https://download.example/{bucket}/{key}?generation={self.calls}" payload = { "message_id": str(uuid.uuid4()), "external_chat_id": str(uuid.uuid4()), "occurred_at": "2026-07-10T12:00:00+00:00", "user": {"id": str(uuid.uuid4()), "display_name": "+79990000000"}, "message": { "content_kind": "file", "text": "", "files": [{ "attachment_id": str(uuid.uuid4()), "name": "file.pdf", "mime_type": "application/pdf", "size_bytes": 42, "_storage_bucket": "attachments", "_object_key": "dialogs/d/file", }], }, } s3 = FakeS3() first = await fresh_openlines_payload(payload, s3) # type: ignore[arg-type] second = await fresh_openlines_payload(payload, s3) # type: ignore[arg-type] assert first["occurred_at"] and first["user"]["id"] assert first["message"]["content_kind"] == "file" assert ( first["message"]["files"][0]["download_url"] != second["message"]["files"][0]["download_url"] ) assert all(not key.startswith("_") for key in first["message"]["files"][0])