Skip to content

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

Closed

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 CartFacadeDraftStoreTest → Cart 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: паттерн CustomerAuthEndpointsTest — Router + 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.

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

Activity

  1. Ibochkarev commented on Aug 16, 2026

    @Ibochkarev
    MemberAuthor

    Follow-up после #563 / PR #578

    Публичные GET /api/v1/category/list|get/{id}|tree уже в PR. В Phase B fixtures journey нужны runtime MODX (или xPDO harness) rows:

    Happy path каталога в suite: category/list → category/get/{id} → product/list?parent=… → дальше cart как сейчас.

    AC «Не требует несуществующих endpoints #563–#569» для category устарел: category можно включить в journey после merge #578. Delivery/payment discovery (#568/#569) по-прежнему через fixture IDs.

    Alias resolve (#580, если номер другой — см. linked issue) — отдельный сценарий после появления GET …/get?alias|uri.

  2. Ibochkarev commented on Aug 16, 2026

    @Ibochkarev
    MemberAuthor

    Уточнение: alias|uri resolve + cache — #579 (не #580).

  3. self-assigned this
    on Aug 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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