Skip to content

[Feature] Web API: интеграционный test suite для headless Nuxt journey #574

Description

@Ibochkarev

Описание функции

Интеграционный test suite для Web API (api.php/api/v1), который гоняет headless Nuxt customer journey на уровне HTTP-контракта: Router + middleware + controllers + envelope. Не domain facades и не source-scan.

Сейчас в репозитории много unit/smoke и несколько Level-2 сервисов. Почти нет тестов, которые проходят storefront flow так же, как Nuxt.

Проблема, которую решает

Регрессии в TokenMiddleware, Response::normalizeResponse/getData(), CORS, cart→order bind и submit ломают Nuxt, а CI остаётся зелёным: тесты проверяют SQLite-facade или static helpers, не wire-контракт.

Нужен один suite «headless journey», который падает, если изменился JSON/HTTP публичного API.

Текущее покрытие (проверено по journey)

Легенда: HTTP = Router::dispatch / Web API envelope. Domain = facade/service без Web boundary. Static = helpers / source-scan / DTO.

# Сценарий Nuxt Покрытие сейчас Тип
1 Public catalog GET /product/list ProductCatalogServiceTest — limit/offset/sort helpers Static
2 Product details GET /product/get/{id} Нет HTTP; нет DB catalog get
3 Add to cart POST /cart/add CartFacadeDraftStoreTestCart facade + SQLite products Domain
4 Change qty POST /cart/change тот же facade test Domain
5 Change option POST /cart/change-option только наличие метода в CartCustomerFacadeStructureTest; runtime test нет Static
6 Retrieve cart GET /cart/get нет HTTP; форма cart/status не контракт-тестится (#570)
7 Register POST /customer/register CustomerAuthEndpointsTest / Routes / ProcessorsDto HTTP* stub processors
8 Login POST /customer/login то же + AuthManagerLifecycleTest (token rotate, SQLite/MySQL) Domain + HTTP* stubs
9 Set address нет Web: order/address/set, customer/addresses*
10 Select delivery нет Web set delivery_id; pairing — DeliveryPaymentAvailabilityTest source-scan Static
11 Select payment нет Web set payment_id
12 Calculate costs GET /order/cost* OrderCostEngineTest, manager recalculator — не Web OrderController Domain
13 Create/draft order cart draft via facade; ProgrammaticOrderServiceTest = sessionless, не storefront Domain
14 Submit POST /order/submit OrderFinalizeServiceTest domain finalize — не Web submit + redirect Domain
15 Retrieve order GET /customer/orders* CustomerOrderServiceDtoTest DTO/params — не HTTP Static

* CustomerAuthEndpointsTest делает Router::loadRoutes(web.php) + dispatch, но runProcessor заглушен. Это mapping auth routes/status codes, не полный login+DB+cart transfer.

Что ещё есть (не journey)

Сквозного headless HTTP journey нет. Domain coverage cart/order/auth кусками есть. Для Nuxt gate этого мало.

Предлагаемое решение

Архитектура suite

tests/Integration/WebApi/
  HeadlessStorefrontJourneyTest.php   # happy path 1→15 (где endpoints существуют)
  WebApiTestCase.php                  # bootstrap Router + fixtures + token helpers
  support/…                           # fixtures / HTTP client wrapper

Фазы:

  1. Phase A: паттерн CustomerAuthEndpointsTestRouter + web.php + stubbed services/DI для cart/order/product, assert HTTP status + envelope keys. Быстро в CI.
  2. Phase B: MySQL/SQLite fixtures как AuthManagerMysqlLifecycleTest / cart SQLite — draft rows, published product, delivery/payment members.
  3. Phase C (optional): HTTP к api.php через built-in server — nightly, не блокер merge.

Минимум для закрытия issue: Phase A+B на PHPUnit (composer test или composer test:web-api).

Auth handling

  1. GET /customer/token/get или TokenMiddleware auto-mint → сохранить token.
  2. Authorization: Bearer и отдельно cookie ms3_token.
  3. Guest token → register/login → новый token + cart transfer (см. [Bug] Login/Register: rebind guest API-токена без ротации (token fixation) #412 / [Feature] Web API: контракт customer auth для Nuxt SSR (me / TTL / cookie+Bearer) #571).
  4. Cabinet addresses/orders: customer_id > 0.

Database fixtures (Phase B)

  • 1 published product (+ option keys для change-option).
  • Active delivery + payment + msDeliveryMember.
  • Customer credentials для login/register.
  • Cleanup per test / transaction.

Полный MODX manager bootstrap не обязателен, если xPDO/SQLite harness уже принят в Integration/.

CORS

Happy path (assert envelope)

Где endpoint существует:

Error cases (минимум)

Кейс Ожидание
product get unknown id 404 envelope
cart without token (если не auto-mint) 401
expired/invalid Bearer 401 (ms3_err_token_*)
change-option empty options 400
order submit empty cart / missing fields 400 + message
payment not linked to delivery business error (#374)
customer orders as guest 401
rate limit (optional isolated) 429

Чего suite не должен ждать

В web.php пока нет public delivery/list, payment/list, category API (#563#569). Journey ставит delivery_id/payment_id из fixture IDs (как Fenom form), с пометкой «discovery later».

Затронутые места

  • tests/Integration/WebApi/* (новые)
  • composer.json scripts / scripts/ci-php.sh / CI workflow
  • Переиспользовать stubs: WebApiModxStub, CustomerAuthPdoStore, OrderProductSqliteStore где возможно
  • Не дублировать mgr ACL tests

Альтернативные варианты

  1. Только расширять facade tests. Не ловит Web controller / getData() / middleware.
  2. Browser e2e (Playwright). Дорого. Не заменяет API contract suite.
  3. Полагаться на docs ([Feature] Docs: полный справочник Web API для Nuxt/TypeScript (сверка с web.php) #573). Docs не gate.

Примеры использования

cd core/components/minishop3
composer test:web-api
# или: vendor/bin/phpunit --testsuite WebApi

Nuxt-команда перед релизом витрины: зелёный journey = контракт /api/v1 не сломан.

Обратная совместимость

  • Новые тесты не меняют runtime API.
  • Flaky MySQL: @group mysql / skip без DSN (как существующие Mysql integration).

Критерии приёмки

  • PHPUnit (или эквивалент в CI) suite бьёт Web routes через Router/api boundary.
  • Happy path: product list+get → cart add/change/(change-option) → get cart → token/login → set address + delivery_id + payment_id → order/cost → submit → customer order get (или явный skip с reason).
  • Assertы на HTTP status + success/data (не только domain arrays).
  • Bearer и хотя бы один cookie auth path.
  • Error cases: 401 token, 404 product, validation/business на submit или set.
  • CORS preflight или config+middleware smoke в том же suite/adjacent test.
  • CI: suite в composer ci:php или отдельный required job; skip policy для MySQL задокументирован.
  • README/tests: как запустить локально.
  • Не требует несуществующих endpoints [Feature] Web API: публичный Category API для headless (list / get / tree) #563[Feature] Web API: публичный список методов оплаты (payment/list) #569.

Дополнительный контекст

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requestpriority: mediumСредний приоритет

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions