# Testes — integracao-wms-desacoplada (Fase 1)

## Cobertura

| Componente | Tipo | Path | Assertions | Status |
|---|---|---|---|---|
| WmsProviderRegistry + WmsProviderFactory | TDD unit | `componentes/Missoes/Logistica/sinais/IntegracaoWms/Testes/Provedores/WmsProviderRegistryTddTest.php` | 27 | PASS |
| GalpaoProvider paridade + IntegracaoWmsEnvConfig defaults | TDD unit | `componentes/Missoes/Logistica/sinais/IntegracaoWms/Testes/Provedores/GalpaoProviderParidadeTddTest.php` | 35 | PASS |
| IntegracaoWmsConfigSonda (sem banco) | TDD unit | `componentes/Missoes/Logistica/sinais/IntegracaoWms/Testes/Config/IntegracaoWmsConfigSondaTddTest.php` | 21 | PASS |
| ErpAlocacaoWmsHookStrategy + WmsAlocacaoWmsHookStrategy — resolução provedor via Registry (não-regressão B1) | TDD unit | `componentes/Missoes/Logistica/sinais/IntegracaoWms/Testes/Strategies/AlocacaoHookStrategyTddTest.php` | 41 | PASS |
| Migration 001 + Config Laboratorios + não-regressão | Integration | `tests/integration/IntegracaoWms/ConfigIntegracaoTest.php` | 47 | PASS |

**Total: 171 assertions / 171 PASS / 0 FAIL**

## Como rodar

```bash
# TDD unit (sem banco)
php componentes/Missoes/Logistica/sinais/IntegracaoWms/Testes/Provedores/WmsProviderRegistryTddTest.php
php componentes/Missoes/Logistica/sinais/IntegracaoWms/Testes/Provedores/GalpaoProviderParidadeTddTest.php
php componentes/Missoes/Logistica/sinais/IntegracaoWms/Testes/Config/IntegracaoWmsConfigSondaTddTest.php
php componentes/Missoes/Logistica/sinais/IntegracaoWms/Testes/Strategies/AlocacaoHookStrategyTddTest.php

# Integration (requer dev_gx_goldie)
php tests/integration/IntegracaoWms/ConfigIntegracaoTest.php
```

## O que mudou nesta rodada (pós-review 06-review.md)

### AlocacaoHookStrategyTddTest (NOVO)

Endereça M1 do review: nenhum teste anterior exercitava as HookStrategies refatoradas.

Técnica: um `SpyWmsProvider` com estado estático é registrado no `WmsProviderRegistry`
sob o nome `'galpao'` via `WmsProviderRegistry::registrar('galpao', SpyWmsProvider::class)`.
**Não usa `setOverride()`** — o override mascara a resolução por nome e não pegaria
uma regressão camelCase (`$config->provedorSaida` → null). A factory passa por
`resolver('galpao')` instanciando o spy, que captura o `WmsEnvioContexto` real.

Caminho exercitado:
1. `ErpAlocacaoWmsHookStrategy::sincronizar()` → `build($config->provedor_saida)` → spy
2. `ErpAlocacaoWmsHookStrategy::estornar()` → idem
3. `WmsAlocacaoWmsHookStrategy::sincronizar()` → `build($config->provedor_saida)` → spy
4. `WmsAlocacaoWmsHookStrategy::estornar()` → idem

Com o bug B1 ativo (`$config->provedorSaida` camelCase = null), `build(null)` lança
`TypeError` antes do spy ser chamado → `SpyWmsProvider::$invocado === false` → FAIL.

Paridade coberta (sobre contexto capturado/real, não literais reimplementados à mão):
- URL base: `$ctx->url` === `IntegracaoWmsEnvConfig::urlErpAlocacao()` / `urlWmsSync()`
- URL-final (GalpaoProvider formula): `$ctx->url . '?opc=' . urlencode($ctx->opc) . '&retorno=1'`
  === string literal legada de `enviarRequisicaoJson`
- Body: `json_encode($ctx->payload, JSON_UNESCAPED_UNICODE)` é JSON válido com opc correto
- Token: `$ctx->bearerToken === 'gnesistk'` (Authorization: Bearer gnesistk)
- missaoId e referencia no contexto (paridade de log)
- Estorno: `opc === 'cancelar_localizacao_wms'` e `body['acao'] === 'estorno'`
- Skip de item inválido e lista vazia

### GalpaoProviderParidadeTddTest (atualizado)

Seções 7–9 anteriores eram tautológicas: comparavam `$urlEsperada === $urlLegada`
onde ambos os lados eram a mesma expressão PHP computada duas vezes — sempre true,
mesmo com o bug B1 ativo.

Substituídas por 6 asserções não-tautológicas:
- URL-final ERP: `IntegracaoWmsEnvConfig::urlErpAlocacao() + fórmula` === string literal legada
- URL-final WMS: idem para `urlWmsSync()`
- Body UTF-8: `json_encode(payload, JSON_UNESCAPED_UNICODE)` preserva 'São Paulo' literal
- Flag necessária: `json_encode` SEM a flag teria escapado o unicode
- Content-Type application/json é distinto de application/x-www-form-urlencoded
- Authorization header === string literal 'Authorization: Bearer gnesistk'

Contagem: 33 → 35 (net +2; 4 tautológicas removidas, 6 reais adicionadas).

### ConfigIntegracaoTest (integration) — 47/47 PASS

Corrigiu de 38/47 para 47/47. Os 9 FAILs anteriores eram causados pelo bug B2
(coluna `usuario_cadastro` ausente da migration 001 → PDOException no INSERT →
`salvarConfig()` sempre retornava false). A migration 001 foi corrigida com:
- Adição de `usuario_cadastro VARCHAR(255)` no DDL
- `ALTER TABLE integracao_wms_config ADD COLUMN IF NOT EXISTS usuario_cadastro VARCHAR(255)`
  para aplicar em ambientes onde a tabela já existia

## Gaps de cobertura

- `IntegracaoWms::start()` / `processarSalvar()` / `renderView()` — não cobertos: dependem
  da infra Galaxia (GalaxiaRoute, visual, show) que não é inicializável em CLI standalone.
- `HttpWmsProvider` — stub de Fase 2; testado via Registry (resolve e retorna ok=false).
  Cobrir a implementação real é responsabilidade da Fase 2.
- `ConsultarConfigLaboratorio` + `SalvarConfigLaboratorio` com `ativo_saida=0` (gate de saída
  desativada) — caminho coberto indiretamente pelo guard de `ativo_saida` na strategy (que
  retorna early se `!(bool)$config->ativo_saida`).
- Resolução de `resolverEmpresaIdAtual()` via `SessionGalaxia::mainCompany` — coberta pela
  Sonda TDD unit; o caminho de sessão sintética do cron é coberto indiretamente.

## Output da execução (rodada pós-review, 2026-06-30)

```
=== Test 1: WmsProviderRegistryTddTest ===
RESULTADO: PASS=27  FAIL=0
TODOS OS TESTES PASSARAM

=== Test 2: GalpaoProviderParidadeTddTest ===
RESULTADO: PASS=35  FAIL=0
TODOS OS TESTES PASSARAM

=== Test 3: IntegracaoWmsConfigSondaTddTest ===
RESULTADO: PASS=21  FAIL=0
TODOS OS TESTES PASSARAM

=== Test 4: AlocacaoHookStrategyTddTest (NOVO) ===
RESULTADO: PASS=41  FAIL=0
TODOS OS TESTES PASSARAM

=== Test 5: ConfigIntegracaoTest (integration, dev_gx_goldie) ===
[CLEANUP] Linhas de teste removidas.
RESULTADO: PASS=47  FAIL=0
TODOS OS TESTES PASSARAM
```

Saída integral do novo AlocacaoHookStrategyTddTest:

```
====================================================
UNIT — AlocacaoHookStrategy: resolução de provedor
====================================================

[PASS] precondição: build("galpao") resolves via Registry (não override)
--- ErpAlocacaoWmsHookStrategy::sincronizar() ---
[PASS] ERP sincronizar: spy invocado via Registry (resolução $config->provedor_saida)
[PASS] ERP sincronizar: spy invocado exatamente 1 vez (1 item)
[PASS] ERP sincronizar: WmsEnvioContexto capturado do contexto real da strategy
[PASS] ERP sincronizar: destino = "erp"
[PASS] ERP sincronizar: URL base no contexto = urlErpAlocacao() do env (paridade legado)
[PASS] ERP sincronizar: URL-final (GalpaoProvider formula) matches URL legada de enviarRequisicaoJson
[PASS] ERP sincronizar: body = json_encode(payload, JSON_UNESCAPED_UNICODE) válido e não-vazio
[PASS] ERP sincronizar: body contém opc correto para alocação
[PASS] ERP sincronizar: body contém importacao_id do item
[PASS] ERP sincronizar: bearerToken = "gnesistk" (legado, sem override de env)
[PASS] ERP sincronizar: Authorization header = "Authorization: Bearer gnesistk"
[PASS] ERP sincronizar: body é JSON válido (Content-Type: application/json, não form-encoded)
[PASS] ERP sincronizar: missaoId = 99 (restaura paridade de log com enviarRequisicaoJson)
[PASS] ERP sincronizar: referencia = importacao_id (restaura paridade de log)

--- ErpAlocacaoWmsHookStrategy::estornar() ---
[PASS] ERP estornar: spy invocado via Registry (resolução $config->provedor_saida)
[PASS] ERP estornar: destino = "erp"
[PASS] ERP estornar: URL base = urlErpAlocacao() (paridade legado)
[PASS] ERP estornar: opc de estorno = "cancelar_localizacao_wms"
[PASS] ERP estornar: bearerToken = "gnesistk"
[PASS] ERP estornar: body contém acao=estorno

--- WmsAlocacaoWmsHookStrategy::sincronizar() ---
[PASS] WMS sincronizar: spy invocado via Registry (resolução $config->provedor_saida)
[PASS] WMS sincronizar: spy invocado exatamente 1 vez (1 item)
[PASS] WMS sincronizar: destino = "wms"
[PASS] WMS sincronizar: URL base = urlWmsSync() do env (paridade legado)
[PASS] WMS sincronizar: URL-final (GalpaoProvider formula) matches URL legada de sync WMS
[PASS] WMS sincronizar: opc = "sync_localizacao_wms"
[PASS] WMS sincronizar: bearerToken = "gnesistk" (legado)
[PASS] WMS sincronizar: missaoId = 88
[PASS] WMS sincronizar: body = json_encode(payload, JSON_UNESCAPED_UNICODE) válido
[PASS] WMS sincronizar: body contém opc correto

--- WmsAlocacaoWmsHookStrategy::estornar() ---
[PASS] WMS estornar: spy invocado via Registry (resolução $config->provedor_saida)
[PASS] WMS estornar: destino = "wms"
[PASS] WMS estornar: URL base = urlWmsSync() (paridade legado)
[PASS] WMS estornar: opc de estorno = "cancelar_localizacao_wms"
[PASS] WMS estornar: bearerToken = "gnesistk"
[PASS] WMS estornar: body contém acao=estorno

--- Skip de item inválido (sem importacao_id / sem produto_id) ---
[PASS] ERP sincronizar: item sem importacao_id é pulado (spy não chamado)
[PASS] WMS sincronizar: item sem produto_id é pulado (spy não chamado)

--- Lista vazia: spy não invocado ---
[PASS] ERP sincronizar([]): lista vazia → spy não chamado
[PASS] WMS estornar([]): lista vazia → spy não chamado

====================================================
RESULTADO: PASS=41  FAIL=0
TODOS OS TESTES PASSARAM
```

## Bugs encontrados

Nenhum novo. Os bugs pré-existentes (B1 e B2) foram corrigidos pelo dev backend antes desta rodada,
conforme documentado em `03-backend.md §Correções pós-review`. A suíte confirma que as correções funcionam:

- **B1 corrigido**: `AlocacaoHookStrategyTddTest` invoca o spy 8 vezes (4 métodos × 2 strategies)
  sem nenhum TypeError. Com o bug ativo, `build(null)` teria lançado TypeError antes do spy → 0 invocações → FAIL.
- **B2 corrigido**: `ConfigIntegracaoTest` passou de 38/47 para 47/47. O INSERT via
  `ModelInteligencia::create()` (que injeta `usuario_cadastro`) agora encontra a coluna na tabela.
