ADR-0033: Imitation service mandated per supplier integration

Status: Accepted Date: 2026-04-21 Deciders: Platform, Ingestion

Контекст

Phase 1-a интегрирует ETM как первого поставщика. Локальная разработка и CI нуждаются в детерминированном, не зависящем от квот покрытии контракта Connector, не создавая реального трафика в sandbox-окружение поставщика. Нагрузка на partner-инфраструктуру и дрейф данных в snapshot real-sandbox — два неприемлемых риска для ежедневной итерации. Решение нужно до того, как в Phase 1-b/2 подключаются DKC, Systeme Electric, КЭАЗ и smart-shop.pro: без convention каждый коннектор изобретёт свою схему заглушек.

Решение

Каждая интеграция поставщика — сегодня ETM, завтра Systeme / КЭАЗ / DKC / smart-shop.pro / новые партнёры — поставляется вместе с process-local imitation service в каталоге deploy/docker/resources/mock-<supplier>/. Сервис ОБЯЗАН:

  1. Быть минимальным HTTP (или любым другим транспорт-сервером) повторяющим форму публичной поверхности реального поставщика — пути, заголовки, коды ошибок.
  2. Поставлять fixture-JSON, покрывающий как минимум happy-path для одного SKU и все error-режимы из таксономии ConnectorError: AuthRejected, SessionExpired, RateLimited, NotFound, Transient.
  3. Работать под compose profile test, чтобы docker compose up по умолчанию оставался лёгким.
  4. Лежать в основании provider-contract suite (-tags=provider_contract) и полного e2e ingestion-теста (-tags=e2e) на каждой ветке.
  5. Принадлежать той же команде, что владеет коннектором — это не platform-артефакт. Изменения API-формы поставщика приезжают одновременно с обновлением мока.

Поставщики с не-HTTP транспортами (SOAP, file-feed, headless browser) ОБЯЗАНЫ всё равно предоставлять imitation, экспонирующий ту же семантику порта Connector — будь то локальный file-feed каталог или scripted HTTP→SOAP-фасад.

Последствия

Плюсы

  • Новый шаблон supplier-plan получает acceptance criterion: deploy/docker/resources/mock-<supplier>/ билдится и отдаёт канонический fixture-набор под compose profile test.
  • CI не получает дополнительной стоимости per supplier — job provider-contract гоняется против мока без сетевого egress.
  • Partner-facing rate-limit и квоты не трогаются внутренними итерациями. Прогоны по реальному sandbox гейтятся явным env-флагом TRACIUM_<SUPPLIER>_REAL=1 для ручного smoke.
  • Онбординг разработчика проще: docker compose --profile test up поднимает полностью функциональный локальный supplier-stack без реальных partner-credentials — placeholder значений достаточно.

Минусы

  • Дублирование поверхности API: мок нужно синхронно обновлять при изменении реального поставщика. Митигация — мок и коннектор лежат в одном PR и ревьюятся вместе.
  • Расширение CI matrix на каждого нового поставщика. Митигация — provider-contract job уже шардируется по supplier tag’у.

Нейтральные последствия

  • Фикстуры хранятся в git (а не в S3) — детерминизм важнее размера репозитория на данном горизонте.

Рассмотренные альтернативы

Альтернатива A — shared recorded-response mock

Записывать живые ответы поставщика один раз и прогонять их через generic replay-прокси (например, WireMock). Отвергли: каждый поставщик имеет уникальную семантику auth/сессий/rate-limit, поэтому generic replay превращается в конфиг-ад и не покрывает negative-paths.

Альтернатива B — run tests against partner sandbox

Гонять integration-тесты прямо против partner sandbox. Отвергли: нагрузочный риск, дрейф данных, отсутствие контроля над negative-path инъекциями (RateLimited/SessionExpired невозможно вызвать детерминированно).

Альтернатива C — make mock optional

Оставить mock на усмотрение команды supplier’а. Отвергли: без mandate появляется дивергенция в тестовых стратегиях между поставщиками, что срывает цель “один CI-прогон проверяет весь ingestion-контракт”.

Ссылки

  • ADR-0024 supplier connector contract
  • spec docs/superpowers/specs/2026-04-20-ingestion-phase1-a-design.md §3.1.9, §3.7
  • plan docs/plans/2026-04-20-11-50-phase1a-ingestion-etm.md (mock-ETM)
  • plan docs/plans/2026-04-20-11-55-phase1a-supplier-sync-cmd.md (supplier-sync + ADR)