"""Integration tests for Evotor webhook endpoints.""" import json from unittest.mock import patch import pytest from web.models.connections import EvotorConnection from web.models.user import User, UserStatusEnum WEBHOOK_SECRET = "test-secret-abc" def auth_headers(secret=WEBHOOK_SECRET): return {"Authorization": f"Bearer {secret}"} # ── /user/create ────────────────────────────────────────────────────────────── @pytest.mark.asyncio @patch("web.routes.evotor_webhooks.send_email_task") async def test_user_create_new_user(mock_task, client, override_db, monkeypatch): monkeypatch.setattr("web.routes.evotor_webhooks.settings.EVOTOR_WEBHOOK_SECRET", WEBHOOK_SECRET) monkeypatch.setattr("web.routes.evotor_webhooks.settings.INVITE_EXPIRE_HOURS", 48) monkeypatch.setattr("web.routes.evotor_webhooks.settings.BASE_URL", "http://test") payload = { "userId": "evo-001", "customField": json.dumps({"email": "newuser@test.com", "phone": "+79001234501"}), } resp = await client.post("/user/create", json=payload, headers=auth_headers()) assert resp.status_code == 200 data = resp.json() assert data["userId"] == "evo-001" assert "token" in data assert len(data["token"]) > 10 user = override_db.query(User).filter(User.evotor_user_id == "evo-001").first() assert user is not None assert user.status == UserStatusEnum.pending assert user.invite_token is not None assert user.password_hash is None conn = override_db.query(EvotorConnection).filter( EvotorConnection.evotor_user_id == "evo-001" ).first() assert conn is not None assert conn.api_token == data["token"] mock_task.delay.assert_called_once() @pytest.mark.asyncio @patch("web.routes.evotor_webhooks.send_email_task") async def test_user_create_links_existing_user_by_email(mock_task, client, override_db, user_factory, monkeypatch): monkeypatch.setattr("web.routes.evotor_webhooks.settings.EVOTOR_WEBHOOK_SECRET", WEBHOOK_SECRET) monkeypatch.setattr("web.routes.evotor_webhooks.settings.INVITE_EXPIRE_HOURS", 48) monkeypatch.setattr("web.routes.evotor_webhooks.settings.BASE_URL", "http://test") existing = user_factory.create(email="existing@test.com") assert existing.evotor_user_id is None payload = { "userId": "evo-link-001", "customField": json.dumps({"email": "existing@test.com"}), } resp = await client.post("/user/create", json=payload, headers=auth_headers()) assert resp.status_code == 200 override_db.refresh(existing) assert existing.evotor_user_id == "evo-link-001" @pytest.mark.asyncio async def test_user_create_wrong_secret(client, monkeypatch): monkeypatch.setattr("web.routes.evotor_webhooks.settings.EVOTOR_WEBHOOK_SECRET", WEBHOOK_SECRET) resp = await client.post("/user/create", json={"userId": "x"}, headers={"Authorization": "Bearer wrong"}) assert resp.status_code == 401 @pytest.mark.asyncio async def test_user_create_missing_user_id(client, monkeypatch): monkeypatch.setattr("web.routes.evotor_webhooks.settings.EVOTOR_WEBHOOK_SECRET", "") resp = await client.post("/user/create", json={"customField": "{}"}) assert resp.status_code == 400 @pytest.mark.asyncio @patch("web.routes.evotor_webhooks.send_email_task") async def test_user_create_no_secret_dev_mode(mock_task, client, override_db, monkeypatch): monkeypatch.setattr("web.routes.evotor_webhooks.settings.EVOTOR_WEBHOOK_SECRET", "") monkeypatch.setattr("web.routes.evotor_webhooks.settings.INVITE_EXPIRE_HOURS", 48) monkeypatch.setattr("web.routes.evotor_webhooks.settings.BASE_URL", "http://test") resp = await client.post("/user/create", json={"userId": "evo-dev-001"}) assert resp.status_code == 200 assert resp.json()["userId"] == "evo-dev-001" # ── /user/verify ────────────────────────────────────────────────────────────── @pytest.mark.asyncio async def test_user_verify_success(client, override_db, user_factory, monkeypatch): monkeypatch.setattr("web.routes.evotor_webhooks.settings.EVOTOR_WEBHOOK_SECRET", WEBHOOK_SECRET) user = user_factory.create(evotor_user_id="evo-verify-001") conn = EvotorConnection( user_id=user.id, evotor_user_id="evo-verify-001", access_token="evotor-access-token", api_token="my-api-token-xyz", ) override_db.add(conn) override_db.commit() resp = await client.post("/user/verify", json={ "userId": "evo-verify-001", "username": user.email, "password": "testpass123", }, headers=auth_headers()) assert resp.status_code == 200 data = resp.json() assert data["userId"] == "evo-verify-001" assert "token" in data @pytest.mark.asyncio async def test_user_verify_wrong_password(client, user_factory, monkeypatch): monkeypatch.setattr("web.routes.evotor_webhooks.settings.EVOTOR_WEBHOOK_SECRET", WEBHOOK_SECRET) user = user_factory.create() resp = await client.post("/user/verify", json={ "userId": "x", "username": user.email, "password": "wrongpass", }, headers=auth_headers()) assert resp.status_code == 401 @pytest.mark.asyncio async def test_user_verify_suspended(client, user_factory, monkeypatch): monkeypatch.setattr("web.routes.evotor_webhooks.settings.EVOTOR_WEBHOOK_SECRET", WEBHOOK_SECRET) user = user_factory.create(status=UserStatusEnum.suspended) resp = await client.post("/user/verify", json={ "userId": "x", "username": user.email, "password": "testpass123", }, headers=auth_headers()) assert resp.status_code == 403 @pytest.mark.asyncio async def test_user_verify_no_password_hash(client, user_factory, monkeypatch): monkeypatch.setattr("web.routes.evotor_webhooks.settings.EVOTOR_WEBHOOK_SECRET", WEBHOOK_SECRET) user = user_factory.create(password_hash=None) resp = await client.post("/user/verify", json={ "userId": "x", "username": user.email, "password": "anything", }, headers=auth_headers()) assert resp.status_code == 401 # ── /user/token ─────────────────────────────────────────────────────────────── @pytest.mark.asyncio async def test_user_token_updates_connection(client, override_db, user_factory, monkeypatch): monkeypatch.setattr("web.routes.evotor_webhooks.settings.EVOTOR_WEBHOOK_SECRET", WEBHOOK_SECRET) user = user_factory.create(evotor_user_id="evo-token-001") old_conn = EvotorConnection( user_id=user.id, evotor_user_id="evo-token-001", access_token="old-token", api_token="api-tok", ) override_db.add(old_conn) override_db.commit() resp = await client.post("/user/token", json={ "userId": "evo-token-001", "token": "new-evotor-token-xyz", }, headers=auth_headers()) assert resp.status_code == 200 assert resp.json() == {} override_db.refresh(old_conn) assert old_conn.access_token == "new-evotor-token-xyz" assert old_conn.is_online is True @pytest.mark.asyncio async def test_user_token_unknown_user(client, monkeypatch): monkeypatch.setattr("web.routes.evotor_webhooks.settings.EVOTOR_WEBHOOK_SECRET", WEBHOOK_SECRET) resp = await client.post("/user/token", json={ "userId": "does-not-exist", "token": "some-token", }, headers=auth_headers()) assert resp.status_code == 404