Files
evo-sync/tests/test_routes_evotor_webhooks.py

197 lines
7.6 KiB
Python
Raw Normal View History

"""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