Перейти к содержанию

Тестирование

Как протестировать интеграцию с TrueNum, не звоня с реальных телефонов на каждый push.

Уровень 1: ручное тестирование с настоящим звонком

Самый честный тест.

  1. Создайте API-ключ.
  2. Заведите webhook-приёмник:
  3. Для быстрой проверки — https://webhook.site/<your-uuid> (сторонний сервис, показывает входящие POST в браузере).
  4. Для своего бэкенда — ngrok http <port> + https://<random>.ngrok.io/webhook.
  5. Через ваш бэкенд (или curl) создайте верификацию.
  6. Со своего телефона позвоните на возвращённый dial_number.
  7. Проверьте, что 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).