Тестирование¶
Как протестировать интеграцию с TrueNum, не звоня с реальных телефонов на каждый push.
Уровень 1: ручное тестирование с настоящим звонком¶
Самый честный тест.
- Создайте API-ключ.
- Заведите webhook-приёмник:
- Для быстрой проверки —
https://webhook.site/<your-uuid>(сторонний сервис, показывает входящие POST в браузере). - Для своего бэкенда —
ngrok http <port>+https://<random>.ngrok.io/webhook. - Через ваш бэкенд (или curl) создайте верификацию.
- Со своего телефона позвоните на возвращённый
dial_number. - Проверьте, что webhook прилетел и подпись валидна.
Полная инструкция — Быстрый старт.
Уровень 2: тесты приёма webhook'а¶
Не зависит от наших серверов и реальных звонков. Тестируйте свой обработчик локально, подавая ему «фейковый» webhook с реальной подписью.
# test_webhook_handler.py
import hashlib
import hmac
import json
import os
import time
import pytest
WEBHOOK_SECRET = os.environ.get("TRUENUM_WEBHOOK_SECRET", "whsec_test_local")
def make_signed_request(payload: dict, ts: int | None = None) -> tuple[bytes, str]:
"""Возвращает (raw_body, signature_header) для тестового webhook'а."""
ts = ts or int(time.time())
raw_body = json.dumps(payload, separators=(",", ":")).encode()
sig = hmac.new(
WEBHOOK_SECRET.encode(),
f"{ts}.".encode() + raw_body,
hashlib.sha256,
).hexdigest()
return raw_body, f"t={ts},v1={sig}"
def test_completed_marks_user_verified(client):
payload = {
"type": "verification.completed",
"verification_id": "ver_01HXTEST00000000000000000",
"phone": "+79991234567",
"dial_number": "+74951230001",
"client_ref": "user-42",
"caller_id": "+79991234567",
"completed_at": "2026-05-26T12:30:00+00:00",
}
raw, sig = make_signed_request(payload)
resp = client.post(
"/webhooks/truenum",
data=raw,
content_type="application/json",
HTTP_X_TRUENUM_SIGNATURE=sig,
)
assert resp.status_code == 200
assert User.objects.get(id=42).phone_verified is True
Тестируйте также негативные сценарии:
- Подпись неверная →
403. - Подпись валидная, но
t— час назад →403(replay). type: "verification.expired"→ пользователь НЕ помечается верифицированным.- Повторный валидный webhook с тем же
verification_id→ второй раз обработчик ничего не делает (идемпотентность).
Уровень 3: интеграционный тест с моком API¶
Если хотите end-to-end-тест без зависимостей от реального API, замокайте HTTP-клиент:
import responses
@responses.activate
def test_signup_flow_creates_verification():
responses.add(
method=responses.POST,
url="https://app.truenum.ru/api/v1/verifications/",
json={
"verification_id": "ver_01HXTEST00000000000000000",
"phone": "+79991234567",
"dial_number": "+74951230001",
"client_ref": "user-42",
"status": "pending",
"ttl_seconds": 300,
"created_at": "2026-05-26T12:30:00+00:00",
"expires_at": "2026-05-26T12:35:00+00:00",
"completed_at": None,
"caller_id_received": "",
},
status=201,
)
# ваш код, который вызывает TrueNum
result = my_signup_service.start_phone_verification(
user_id=42, phone="+79991234567"
)
assert result.dial_number == "+74951230001"
assert len(responses.calls) == 1
Песочница / sandbox-окружение¶
Отдельное sandbox-окружение не предоставляется. Все верификации делаются в продакшене, оплата идёт за успешные (status=completed). Тестовые попытки, которые не доходят до звонка (например, ваш webhook-приёмник отдаёт 500 и мы исчерпываем ретраи), не тарифицируются по основному прайсу — уточнить детали можно в саппорте.
Тестирование без расхода DID¶
Если просто хотите подёргать API и убедиться, что Bearer-аутентификация работает:
# Должно вернуть 200 OK с пустой/непустой страницей верификаций
curl -i https://app.truenum.ru/api/v1/verifications/ver_does_not_exist/ \
-H 'Authorization: Bearer tn_live_<prefix>_<secret>'
# Ожидаем 404 — это значит, что аутентификация прошла, но ресурса нет.
Если в ответе 401 — токен неверный. Если 404 — токен валиден, всё ок.
Чек-лист интеграции перед раскатом в прод¶
- [ ] API-ключ создан и хранится в env / secret manager.
- [ ] Webhook secret получен и хранится там же.
- [ ] Webhook-эндпоинт принимает POST на HTTPS публичном URL.
- [ ] Подпись
X-Truenum-Signatureпроверяется (тест с фейковой подписью возвращает 403). - [ ]
caller_idсравнивается сphoneисходной верификации. - [ ] Обработчик идемпотентен (повторный webhook не дублирует бизнес-эффект).
- [ ] Эндпоинт отвечает
2xx< 10 секунд (тяжёлая логика — в фон). - [ ] Мониторинг: алерт на
5xx-ответы вашего приёмника. - [ ] Логи запросов на TrueNum API (
/verifications/) — на случай сетевых таймаутов. - [ ] Стратегия retry на
503 no_dial_number_available— с backoff, макс. 3 попытки. - [ ] Покрытие тестами happy-path и негативных сценариев (подпись, replay, expired).