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>/.
Сервис ОБЯЗАН:
- Быть минимальным HTTP (или любым другим транспорт-сервером) повторяющим форму публичной поверхности реального поставщика — пути, заголовки, коды ошибок.
- Поставлять fixture-JSON, покрывающий как минимум happy-path для
одного SKU и все error-режимы из таксономии ConnectorError:
AuthRejected,SessionExpired,RateLimited,NotFound,Transient. - Работать под compose profile
test, чтобыdocker compose upпо умолчанию оставался лёгким. - Лежать в основании provider-contract suite (
-tags=provider_contract) и полного e2e ingestion-теста (-tags=e2e) на каждой ветке. - Принадлежать той же команде, что владеет коннектором — это не 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 profiletest. - 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)