Status: Approved · v1 · SPEC-DELIVERY-MOCK-API-MIGRATION-001
depends_on: SPEC-ARCH-QUALITY-001
used_by: IS-MVP-15.1
Checklist de migração Mock→API
Como uma superfície (ambiente ou app) passa de mocks para a API real sem drift de contrato, e como isso é verificado.
O mecanismo de troca
A troca entre mock e API real é por client, no boot do app — nunca por tela.
packages/shared/src/clients/client-config.ts#createHttpClient lê {NEXT_PUBLIC,EXPO_PUBLIC}_USE_MOCKS
uma única vez e devolve ou um MockClient (@raizfc/mocks) ou um ApiClient real, ambos implementando
a mesma interface HttpClient. apps/web/src/shared/http-client.ts e
apps/mobile/src/shared/http-client.ts instanciam esse client uma vez por app (httpClient,
singleton de módulo) e todo hook/serviço o consome — nenhuma tela importa @raizfc/mocks ou monta
fetch diretamente. assertClientConfig recusa mocks habilitados quando environment === 'production',
então não existe caminho de build/runtime em que produção sirva dado mockado.
Checklist para religar uma superfície à API real
Ao trocar USE_MOCKS=true → false para um ambiente (staging, produção) ou ao decidir que um domínio
específico está pronto para sair do preview mockado:
-
{PREFIX}_API_BASE_URLaponta para uma API real acessível a partir do client (CORS, HTTPS). -
pnpm run validate:contract-parityestá verde contra essa API — ver seção seguinte. -
pnpm run buildcomNODE_ENV=productione{PREFIX}_USE_MOCKS=falsecompila sem o guard deassertClientConfigdisparar (client-config.ts). - Os smoke P0 de
SPEC-ARCH-QUALITY-001(/feed,/search,/matches,/teams/:slug,/players/:slug,/matches/:id,/championships/:slug,/management,/profile) foram exercitados manualmente contra a API real pelo menos uma vez antes do primeiro deploy. - Nenhum dado sensível (ver
SPEC-ARCH-SECURITY-001— checklist por endpoint) aparece em um DTO público que só deveria existir no lado autenticado/gestão. - Rollback: reverter
{PREFIX}_USE_MOCKSparatrueé suficiente para voltar ao preview mockado — nenhuma migração de estado é necessária porque mock e API real nunca compartilham storage.
Contract parity — o que é verificado automaticamente
tests/contract-parity/contract-parity.test.mjs (pnpm run validate:contract-parity) chama o mesmo
request contra um MockClient e uma instância real da API (services/api compilado, em processo) e
compara a forma da resposta — o conjunto de caminhos de chave e o tipo primitivo de cada folha,
nunca o valor literal (meta.requestId/meta.timestamp são gerados por request e nunca serão
idênticos byte a byte entre os dois lados). Isso prova exatamente a garantia que uma tela depende: o
DTO que ela recebe tem a mesma forma não importa qual HttpClient o app escolheu no boot.
Cobertura: os nove smoke P0 de SPEC-ARCH-QUALITY-001, mais os domínios de maior prioridade que a
mesma spec lista (auth/permission/privacy, financial/idempotency, match/result/report/ranking) —
login real, GET /auth/me, GET /management/my-entities, carteira do time, cheer idempotente,
acesso anônimo negado a gestão. Não é uma enumeração exaustiva de todos os ~28 módulos da API; cada
domínio já tem sua própria paridade dupla verificada independentemente por
packages/mocks/test/** (282 testes) e services/api/test/** (215 testes), cada lado validado contra
packages/contracts. Este suite root-level cobre a lacuna que nenhum dos dois lados sozinho prova:
que os dois lados concordam entre si.
Achados corrigidos por esta verificação (EP-MVP-15.1)
A primeira execução deste suite encontrou e corrigiu drift real de fixture/funcionalidade entre mock e API (nenhum dos dois "errado" por si só — apenas desalinhados um do outro):
GET /matches/:id—headToHeadda API tinha só 1 de 2 confrontos que o mock já tinha.GET /championships/:slug—bracketda API tinha só 1 de 2 partidas de quartas e não tinha o item "Semifinal" (vazio) que o mock já tinha.GET /torcidas/:slugeGET /fields/:slug—matchesda API não incluía a partida já encerrada (match_last_001) que o mock já incluía.GET /services/:slug—showcaseda API não incluía o segundo item (comhref) que o mock já incluía.GET /matches— a API não suportavapage/pageSizenem devolviameta.pagination, apesar de o mock já implementar paginação real (SPEC-ARCH-PERF-001"listas são paginadas"); implementado emMatchesApplicationService#listMatchesreaproveitandoattachPaginationMeta(o mesmo helper quesearchjá usa) — mesmos defaults do mock (page=1,pageSize=20, máximo100, valor inválido cai para o default em vez de rejeitar a request, já que o endpoint é público/soft-auth).
Divergência registrada, não corrigida nesta pack
context.error('VALIDATION_ERROR', { message }) no lado mock (usado em ~22 pontos de 13 arquivos de
handler, não específico de um domínio) coloca a mensagem específica em error.details.message e deixa
error.message com o texto genérico do catálogo (errorMessages[code]); a API real coloca a mesma
mensagem específica diretamente em error.message e nunca popula details nesse caso. Ambas as formas
são válidas para o contrato (ApiError.message: string, details?: Record<string, unknown>) e nenhum
teste existente depende de details.message, então isso não bloqueia o release — mas um pack futuro
que tocar o helper genérico de erro dos mocks (createMockErrorResponse) deveria decidir se alinha os
~22 call sites para colocar a mensagem específica em message como a API já faz, em vez de duplicá-la
em details.
Não coberto por este checklist
- Calibração de performance real (latência, N+1) —
SPEC-ARCH-PERF-001/IS-MVP-15.3. - Auditoria de permissão/IDOR sistemática além do que os testes por domínio já cobrem —
SPEC-ARCH-SECURITY-001/IS-MVP-15.2. - Escolha de provider real (Pix, storage) — segue condicionada per
SPEC-DELIVERY-READINESS-001.