GH GambleHub

Контрактное тестирование

1) Где применять контракты

HTTP REST/JSON: ресурсы, пагинация, фильтры, идемпотентность, коды ошибок.
gRPC/Protobuf: типы сообщений, статусы, семантика `deadline`, backward-compat в.proto.
GraphQL: схемы, non-null, директивы, пермишены на поля.
Сообщения/стримы (Kafka/Pulsar/SQS): event-схемы (Avro/JSON/Protobuf), ключи партиционирования, порядок, идемпотентные ключи.
Внутренние SDK/библиотеки: публичные функции/исключения/контракты производительности.


2) Модель CDC: роли и артефакты

Потребитель публикует контракт ожиданий (примерные запросы/ответы, матчеры типов, инварианты).
Поставщик прогоняет верификацию контрактов против своего сервиса/адаптера/handler’ов.
Брокер контрактов (Pact Broker/Backstage/артефакт-репо) хранит версии, теги (`prod`, `staging`, `canary`) и матрицу совместимости `consumer@v → provider@v`.
Политика релиза: деплой провайдера запрещен, если нарушается любой «прод-релевантный» контракт.


3) Что фиксировать в контракте (HTTP пример)

Минимум:
  • Метод/путь/параметры/заголовки (вкл. auth, идемпотентный ключ).
  • Тело и типовые матчеры (тип/формат/регэксп/диапазоны).
  • Коды и структура ошибок; стабильные `error_code`.
  • Семантические инварианты: сортировка, уникальность, монотонность `created_at`.
  • Нефункциональные ожидания (опционально): p95, лимиты размера, заголовки rate-limit.
Фрагмент контракта (упрощенно):
json
{
"interaction": "GET /v1/users/{id}",
"request": { "method": "GET", "path": "/v1/users/123", "headers": {"Accept":"application/json"} },
"matchers": {
"response.body.id": "type:number",
"response.body.email": "regex:^.+@.+\\..+$",
"response.body.created_at": "format:rfc3339"
},
"response": {
"status": 200,
"headers": {"Content-Type":"application/json"},
"body": {"id": 123, "email": "alice@example.com", "created_at": "2025-10-31T12:00:00Z"}
},
"error_cases": [
{
"name":"not_found",
"request":{"path":"/v1/users/9999"},
"response":{"status":404, "body":{"error_code":"USER_NOT_FOUND"}}
}
]
}

4) Контракты для событий (event-driven)

Схема события: `type`, `version`, `id`, `occurred_at_utc`, `producer`, `subject`, `payload`.
Инварианты: неизменность `id` и идемпотентность по `(type,id)`, порядок в пределах ключа партиции, монотонность `sequence`.
Schema Registry: хранит эволюцию и правила совместимости (backward/forward/full).
Контракт-тесты консьюмера: реплеят «золотые» события и фазы негативов (неизвестные поля, nullable).

Пример Avro-схемы (фрагмент):
json
{
"type":"record","name":"UserRegistered","namespace":"events.v1",
"fields":[
{"name":"id","type":"string"},
{"name":"occurred_at_utc","type":{"type":"long","logicalType":"timestamp-millis"}},
{"name":"email","type":"string"},
{"name":"marketing_opt_in","type":["null","boolean"],"default":null}
]
}

5) Эволюция и совместимость

Версии контрактов: семантика `MAJOR.MINOR.PATCH` (MAJOR — ломающие).

Правила для REST:
  • Не ломайте: не удаляйте поля, не меняйте тип/значение `error_code`.
  • Добавляйте опциональные поля с дефолтом; новые эндпоинты вместо «магии».
  • Депрекация: объявление, параллельная поддержка, удаление по метрикам.
  • GraphQL: поля только добавлять, non-null вводить через фазы; директивы депрекации.
  • gRPC/Proto: не переиспользовать номера полей; только добавлять новые с optional.
  • Events: схема `vN`; консьюмеры обязаны игнорировать неизвестные поля (лениентность).

6) Отрицательные и инвариантные проверки

Negative: неверные типы, запрещенные значения, конфликтные параметры, превышение лимитов.
Invariants: сортировка ответов, уникальность `id`, корректность `next_cursor`, стабильность идемпотентного ответа при повторе.
Контракты временны́х аспектов: `created_at` RFC3339/UTC, корректная проекция локальных суток не является частью транспортного контракта — выносится в бизнес-инварианты.


7) Стаб-генерация и локальная разработка

Из контрактов генерируются стабы провайдера для разработки потребителя.
Для событий — генераторы «валидных/пограничных» сообщений по схеме.
Стабы помечаются версией контракта и датой сборки; запрещена публикация в прод.


8) Встраивание в CI/CD (референс-пайплайн)

1. Consumer CI:

Линт/сборка → генерация контрактов → юнит/контракт-тесты → публикация в contract-broker (tag: `consumer@1.7.0`).

2. Provider CI:

Поднятие сервиса локально/в контейнере → фетч релевантных контрактов (`prod`/`staging`) → верификация → публикация статуса в broker.

3. Release Gate:

Деплой провайдера блокируется, если есть невыполненные контракты.

4. Nightly Matrix:

Матрица совместимости `consumer versions × provider versions`; отчеты и тревоги.


9) Примеры практик по доменам

9.1 REST: пагинация курсорами (контрактный инвариант)

Ответ содержит `items[]`, `next_cursor` (nullable), `limit`, `total` (опционально).
Инварианты: `len(items) ≤ limit`, повторный вызов с тем же `cursor` → идемпотентный набор.
Ошибка, если одновременно заданы `cursor` и `page`.

9.2 Идемпотентность POST

Контракт требует заголовок `Idempotency-Key`.
Инвариант: повторный запрос с тем же ключом возвращает тот же `id`/статус.

9.3 События: гарантии порядка

Ключ партиционирования в контракте: `partition_key = user_id`.
Инвариант: `sequence` монотонно возрастает внутри ключа; консьюмер обязан обрабатывать повторы.


10) Безопасность и приватность в контрактах

Не включать ПДн/секреты в примеры — только синтетика.
Фиксировать обязательные заголовки безопасности: `Authorization`, `X-Signature`, `Replay-Prevention`.
Для вебхуков — контракт подписи и ответа `2xx`/ретраев.
В логах контракт-тестов — маскирование чувствительных полей.


11) Инструменты

Pact / Pactflow / Pact Broker — HTTP/Message контракты, матрица совместимости.
OpenAPI/AsyncAPI — спецификации + тест-генераторы (Dredd, Schemathesis).
Karate/REST Assured — сценарные проверки контрактов REST.
Protobuf/gRPC — `buf`, `protolint`, тесты совместимости; Schema Registry для Avro/JSON/Proto в потоках.
Conformance-тесты для GraphQL (graphql-compat), snapshot тесты схем.


12) Псевдокод верификации провайдера (упрощенно)

python def verify_contract(provider, contract):
for case in contract["cases"]:
req = build_request(case["request"])
res = provider.handle(req) # локально/контейнер assert match_status(res.status, case["response"]["status"])
assert match_headers(res.headers, case["response"].get("headers", {}))
assert match_body(res.body, case["matchers"], allow_extra_fields=True)
for neg in contract.get("error_cases", []):
res = provider.handle(build_request(neg["request"]))
assert res.status == neg["response"]["status"]
assert res.json.get("error_code") == neg["response"]["body"]["error_code"]

13) Анти-паттерны

«Скриншоты Postman — это контракт»: нет версий/типовых матчеров/автоматической валидации.
Оверснейпинг: контракт фиксирует точные значения вместо типов/паттернов → ложные падения.
Один общий контракт для разных регионов/каналов: игнорирует вариативность (флаги, geo-правила).
Контракты без брокера/матрицы: нельзя понять, какие версии совместимы.
Ставка на e2e вместо контрактов: медленно, дорого, нестабильно.
Отсутствие негативных/инвариантных кейсов: тестируется только «зеленая дорожка».


14) Наблюдаемость и эксплуатация

Экспорт статуса в broker + дашборд «health контрактов».
Алерты: новые падения провайдера против `prod`-контрактов, рост «unknown field» в событиях.
Трассировка: `contract_id`, `version`, `decision_id` в логах верификации.


15) Процесс депрекации

1. Добавить поле/эндпоинт (не ломающий).
2. Отметить старое как `deprecated` в спецификации, объявить сроки.
3. Отслеживать потребителей по логам/брокеру; миграционные гайды.
4. Включить «теневой» deny в стейдже (dry-run), затем enforce.
5. Удалить после нулевой доли использования и подтверждения совместимости.


16) Чек-лист архитектора

1. Определены потребители и их владельцы? Контракты версионируются?
2. Есть broker и матрица совместимости с тэгами сред?
3. В контракт включены негативы и инварианты (идиempotентность, курсоры, сортировка)?
4. Для событий настроен Schema Registry и режим совместимости?
5. Пайплайн блокирует релиз провайдера при нарушении прод-контрактов?
6. Описан процесс депрекации и политика эволюции?
7. Генерируются стабы из контрактов, есть локальные генераторы событий?
8. Маскирование ПД и обязательные заголовки безопасности задокументированы?
9. Метрики/алерты по контрактам подключены, есть отчеты по дрифту?
10. Контракты проверяются в CI у обеих сторон (consumer и provider)?


Заключение

Контрактное тестирование переносит «истину» о взаимодействиях в версионируемые артефакты и делает интеграции предсказуемыми. CDC, брокер контрактов и дисциплина эволюции схем заменяют «ломающие сюрпризы» на управляемый процесс: быстрые проверки, четкие инварианты и прозрачная совместимость версий. Это снижает стоимость e2e, ускоряет релизы и улучшает качество всей платформы.

Contact

Свяжитесь с нами

Обращайтесь по любым вопросам или за поддержкой.Мы всегда готовы помочь!

Начать интеграцию

Email — обязателен. Telegram или WhatsApp — по желанию.

Ваше имя необязательно
Email необязательно
Тема необязательно
Сообщение необязательно
Telegram необязательно
@
Если укажете Telegram — мы ответим и там, в дополнение к Email.
WhatsApp необязательно
Формат: +код страны и номер (например, +380XXXXXXXXX).

Нажимая кнопку, вы соглашаетесь на обработку данных.