# Testes — Tradutores restantes (`movimentacao` + `inventario`)

Escopo testado: techplan §Fase 3, passo 17, conforme implementado e reportado
em `14-backend-tradutores.md`. O dev já havia escrito/atualizado 14 arquivos da
suíte unit do sinal (393 asserts), migrado `ReceberOrdemLaboratorioTddTest`
(seção 6) e `tests/integration/IntegracaoWms/ReceberOrdemApiTest.php`
(B2/B3/GRUPO C + GRUPOS F/G novos, 108 asserts). Este documento cobre a
**auditoria** dessa entrega e o **fechamento de 2 gaps reais** encontrados: a
matriz de decisão do subtipo do inventário não tinha um caso para
`subtipo` **inválido** (só "ausente" e "explícito válido"), e não havia uma
prova de que a identidade (endereço) é validada **antes** do tradutor no
caminho `inventario_endereco` especificamente (só produto estava coberto nessa
posição do pipeline). `separacao` segue bloqueado por decisão de produto — nada
a testar além de "continua rejeitado com motivo" (já coberto).

Todos os arquivos rodam com `php <caminho>.php` (sem flags, sem phpunit).

## Cobertura

| Componente | Tipo | Path | Status |
|---|---|---|---|
| `OperacaoWmsOrdemTradutorBase` (refactor) — não-regressão | TDD integração (DB) — já existia | `Testes/Tradutores/AlocacaoOrdemTradutorTddTest.php` | ✅ pass (24/24, idêntico à Fase 3 — inclui assert de `requer_aprovacao=0`, herdado da base) |
| `MovimentacaoOrdemTradutor` (itens válidos, sessão livre, produto desconhecido, origem/destino informativos, `requer_aprovacao=0`) | TDD integração (DB) — já escrito pelo dev | `Testes/Tradutores/MovimentacaoOrdemTradutorTddTest.php` | ✅ pass (25/25) |
| `InventarioOrdemTradutor` — matriz de subtipo (explícito produtos/endereco, inferido produtos/endereco, ambíguo, indeterminado, subtipo vence ambiguidade) | TDD integração (DB) — já escrito pelo dev | `Testes/Tradutores/InventarioOrdemTradutorTddTest.php` | ✅ pass (31/31 antes da minha adição) |
| **`InventarioOrdemTradutor` — `subtipo` INVÁLIDO (`'xyz'`) → rejeita com motivo, não cai em default silencioso** | TDD integração (DB), **NOVO — gap fechado** | `Testes/Tradutores/InventarioOrdemTradutorTddTest.php` (caso 9) | ✅ pass (+4 asserts, 31→35) |
| `TraducaoOrdemFactory` (mapa: alocacao/movimentacao/inventario resolvem; separacao continua lançando) | unit puro — generalizado pelo dev (loop) | `Testes/Tradutores/TraducaoOrdemFactoryTddTest.php` | ✅ pass (20/20) |
| `ReceberOrdemLaboratorio` — seção 6 migrada (`separacao` é o único "sem tradutor" restante) | TDD integração (DB) — já migrado pelo dev | `Testes/Laboratorios/ReceberOrdemLaboratorioTddTest.php` | ✅ pass (65/65 antes da minha adição) |
| **Identidade (endereço) validada ANTES do tradutor no caminho `inventario_endereco`** (só produto estava coberto nessa posição) | TDD integração (DB), **NOVO — gap fechado** | `Testes/Laboratorios/ReceberOrdemLaboratorioTddTest.php` (seção 4b) | ✅ pass (+8 asserts, 65→73) |
| `POST /api/webhooks/wms-7` E2E — GRUPO F (`movimentacao`: aceite+missão, dedup, ciclo de report) | integration (HTTP real) — já escrito pelo dev | `tests/integration/IntegracaoWms/ReceberOrdemApiTest.php` (GRUPO F1-F3) | ✅ pass |
| `POST /api/webhooks/wms-7` E2E — GRUPO G (`inventario` subtipo='endereco': aceite+missão executável, dedup, ciclo de report) | integration (HTTP real) — já escrito pelo dev | `tests/integration/IntegracaoWms/ReceberOrdemApiTest.php` (GRUPO G1-G3) | ✅ pass |
| GRUPO C (cross-tenant misto: aceite emp7 + rejeição empX, mesmo `external_ref`) | integration — migrado pelo dev nesta rodada | `tests/integration/IntegracaoWms/ReceberOrdemApiTest.php` (GRUPO C) | ✅ pass |
| B2/B3 — `separacao` (único tipo sem tradutor restante) | integration — migrado pelo dev nesta rodada | `tests/integration/IntegracaoWms/ReceberOrdemApiTest.php` (GRUPO B2/B3) | ✅ pass |
| GRUPO E (fix de `findByChannel` — não-regressão de uma rodada anterior) | integration — inalterado | `tests/integration/IntegracaoWms/ReceberOrdemApiTest.php` (GRUPO E) | ✅ pass |
| Baseline `IntegracaoWms` (demais 8 arquivos: Config/Provedores/Sondas/Strategies/Validators/Webhooks) — não-regressão | unit + TDD integração — inalterados | ver "Output da execução" | ✅ pass |
| Baseline `AlocacaoWms` (30 arquivos, domínio irmão só *lido*) — pin da baseline | TDD integração — inalterados | ver "Pin da baseline" | ⚠️ 12 fail + 2 fail + 1 fatal **pré-existentes**, confirmados idênticos ao pin da rodada anterior (`11-testes-fase3.md`) |
| `GestaoMissoesAlocacaoWms` — não-regressão do refactor da base | TDD integração — inalterado | `Designacoes/Testes/Laboratorios/GestaoMissoesAlocacaoWmsTddTest.php` | ✅ pass (30/30, idêntico) |

## Gaps de cobertura fechados nesta rodada

### 1. `subtipo` inválido (fora de `'produtos'|'endereco'`) não tinha teste dedicado

A matriz de decisão do `14-backend-tradutores.md` documenta 3 prioridades
(explícito → inferido → indeterminado), mas a suíte só cobria "explícito
válido" e "ausente" (inferido/ambíguo/indeterminado) — nunca "explícito, mas
com um valor que não é nem `'produtos'` nem `'endereco'`". Lendo
`InventarioOrdemTradutor::montarSelecao()`, confirmei que um `subtipo` não-vazio
que não bate com nenhum dos dois valores reconhecidos **pula a inferência**
(`if ($subtipo === '')` é falso) e cai direto na rejeição final — ou seja, NÃO
existe um caminho de "default silencioso" (nunca vira `'produtos'` ou
`'endereco'` por acidente). Adicionei o caso 9 em
`InventarioOrdemTradutorTddTest.php`: `subtipo='xyz'` **com um item de produto
claro no envelope** (para provar que o subtipo explícito, mesmo inválido, não
delega para a inferência) → `ok=false`, motivo presente e não-vazio, nenhuma
missão criada. +4 asserts.

**Observação (não é bug)**: a mensagem de erro é a MESMA para "indeterminado"
(sem subtipo/sem itens) e para "subtipo inválido informado" — ambas dizem
"informe 'subtipo' ('produtos'|'endereco') explicitamente...". Isso é levemente
impreciso para quem mandou um `subtipo='xyz'` (já informou, só que errado), mas
não é um bug funcional (rejeita corretamente, com motivo acionável — lista os
valores aceitos) — cosmético, não bloqueante.

### 2. Identidade (endereço) antes do tradutor — só `produto_ref` estava provado nessa posição do pipeline

`ReceberOrdemLaboratorioTddTest.php` seção 4 já provava que
`OrdemIdentidadeValidator` roda **antes** de `TraducaoOrdemFactory`/tradutor
(produto desconhecido → rejeitado com o motivo do Validator, nunca chega a
criar/consultar tradutor) — mas só com `produto_ref`. Como o novo
`InventarioOrdemTradutor` introduz um escopo **endereço-primeiro**
(`inventario_endereco`, via `resolverEnderecoIds()`), faltava a mesma prova
para `endereco_ref`. Adicionei a seção 4b: ordem `tipo='inventario'`,
`subtipo='endereco'` explícito, item com `endereco_ref` desconhecido → rejeita
com o motivo do Validator ("Endereço desconhecido..."), **explicitamente
confirmando que o motivo NÃO menciona "escopo"** (a mensagem que o tradutor
usaria se tivesse rodado) — prova que `InventarioOrdemTradutor::montarSelecao()`
nunca chegou a ser chamado; nenhuma missão criada. +8 asserts.

## Não-regressão confirmada (pedidos específicos da auditoria)

- **Refactor da base** (`OperacaoWmsOrdemTradutorBase`): `AlocacaoOrdemTradutorTddTest`
  continua 24/24 sem qualquer edição minha; `requer_aprovacao='0'` está na base
  (1 só lugar) e **cada** um dos 3 TddTests de tradutor (`Alocacao`/`Movimentacao`/
  `Inventario`) tem o assert de regressão dedicado (`requer_aprovacao === 0`,
  ver linha ~157-160 de cada arquivo) — confirmado por grep, não só leitura.
- **E2E GRUPOS F/G fecham o ciclo completo**: aceite (200/aceita, missão real
  com `tipo_missao_id=14`/`referencia_tipo`/`referencia_id`/`configuracoes.origem`/
  `operacao_tipo` corretos) → dedup (reenvio não cria 2ª missão) → report real
  (`ReportarOrdemLaboratorio::reportar()` avança `wms_ordens.status` para
  `'reportada'`) — confirmado rodando os 2 grupos (G1-G3, F1-F3), 0 falhas.
  GRUPO G confirma especificamente que a missão `inventario_endereco` nasce
  executável: `configuracoes.operacao_tipo='inventario_endereco'` (o valor que
  a tela `App/html/operacaowms/inventario_endereco.*`/`InventarioEnderecoAdapter`
  usam para escolher o fluxo certo).
- **Cenário "sem tradutor"**: B2/B3 usam `separacao` (o único tipo restante) —
  confirmado representativo (rejeitado com "sem tradutor implementado", mesmo
  texto/mecanismo que `movimentacao`/`inventario` tinham antes de ganharem
  tradutor). O comentário "ARMADILHA CONHECIDA" está presente e correto nos
  dois arquivos (`ReceberOrdemLaboratorioTddTest.php` seção 6,
  `ReceberOrdemApiTest.php` B2 + GRUPO C), sinalizando o próximo passo (trocar
  para um tipo fora do enum fechado) se/quando `separacao` for desbloqueado.
- **Cross-tenant (GRUPO C misto)**: aceite `movimentacao`/emp7 (200, missão
  criada, `block_empresa_id=7` confirmado na missão) + rejeição
  `separacao`/empX (422) com o MESMO `external_ref` → 2 linhas físicas
  distintas em `wms_ordens`, sem colisão, sem vazamento de tenant — confirmado.

## Contagem total (domínio desta feature — `IntegracaoWms`)

| Categoria | Arquivos | Asserções antes | Asserções depois | Passing | Failing |
|---|---|---|---|---|---|
| TDD do sinal (`componentes/.../IntegracaoWms/Testes/`) — 14 arquivos | 14 | 393 | **405** (+12) | 405 | 0 |
| Integration (`tests/integration/IntegracaoWms/`) — 3 arquivos | 3 | 196 (108+47+41) | 196 (inalterado) | 196 | 0 |
| **Total desta feature** | **17** | **589** | **601** | **601** | **0** |

Exit code de todos os arquivos: `0`.

## Pin da baseline — suíte `AlocacaoWms` (domínio irmão, só *lido* por esta feature)

Re-confirmado idêntico ao pin já registrado em `11-testes-fase3.md` (mesmos
arquivos, mesmas causas, investigadas então por leitura de código — não por
`git stash`, que segue não usado):

| Arquivo | Resultado | Causa confirmada (ver `11-testes-fase3.md` para a investigação completa) |
|---|---|---|
| `ConfirmarAlocacaoValidatorTddTest.php` | 7 pass / **12 fail** | Fixture dinâmica (`SELECT ... LIMIT 1` sem `ORDER BY`/filtro de `operacao_tipo`) poluída por resíduo de missões `movimentacao`/`inventario_*` de outras suítes no mesmo `block=22`/`lider_id=1` |
| `CronEstornoSmokeTddTest.php` | 0 pass / **2 fail** | `crons/wms_alocacao_integracao.php` nunca chama `IntegracaoWms::bootstrap()` |
| `HookEstornoTddTest.php` | **1 fatal** | Mesma causa acima, via chamada direta do Hook Strategy sem bootstrap |
| Demais 27 arquivos | pass | — |

**Nota sobre contagem total**: a suíte tem hoje **30 arquivos** (era 29 na
auditoria da Fase 3, 27 no relatório original do backend da Fase 3) — a
diferença **não é desta feature**; é o trabalho paralelo não commitado
(`separacao-endereco-wms`, visível no `git log` desta sessão) continuando a
adicionar testes ao domínio `AlocacaoWms` entre uma rodada e outra. Contagem
bruta desta rodada: **403 PASS / 14 FAIL** + 1 fatal (números absolutos
diferentes do "348/12" do `14-backend-tradutores.md` pelo mesmo motivo — mais
arquivos, não mais bugs). **O que importa para o pin — os MESMOS 3 arquivos
falhando pelos MESMOS motivos, nenhum novo** — está confirmado. Nenhum arquivo
de produção do domínio `AlocacaoWms` foi tocado nesta rodada (nem pelo backend,
nem por mim).

## Output da execução

### Suíte unit do sinal (14 arquivos)
```
IntegracaoWmsConfigSondaTddTest.php     -> PASS=21  FAIL=0
ReceberOrdemLaboratorioTddTest.php      -> PASS=73  FAIL=0   (+8 nesta rodada — seção 4b, identidade endereço)
ReportarOrdemLaboratorioTddTest.php     -> PASS=18  FAIL=0
GalpaoProviderParidadeTddTest.php       -> PASS=35  FAIL=0
WmsProviderRegistryTddTest.php          -> PASS=27  FAIL=0
WmsOrdemSondaTddTest.php                -> PASS=37  FAIL=0
AlocacaoHookStrategyTddTest.php         -> PASS=41  FAIL=0
AlocacaoOrdemTradutorTddTest.php        -> PASS=24  FAIL=0   (idêntico — não-regressão do refactor)
InventarioOrdemTradutorTddTest.php      -> PASS=35  FAIL=0   (+4 nesta rodada — caso 9, subtipo inválido)
MovimentacaoOrdemTradutorTddTest.php    -> PASS=25  FAIL=0
TraducaoOrdemFactoryTddTest.php         -> PASS=20  FAIL=0
OrdemEnvelopeValidatorTddTest.php       -> PASS=15  FAIL=0
OrdemIdentidadeValidatorTddTest.php     -> PASS=20  FAIL=0
WmsPayloadNormalizerTddTest.php         -> PASS=14  FAIL=0
----
TOTAL: PASS=405  FAIL=0
```

### `InventarioOrdemTradutorTddTest.php` — caso 9 (NOVO — subtipo inválido)
```
--- 9. traduzir(): subtipo inválido ('xyz') → rejeita, sem default silencioso ---
[PASS] subtipo='xyz' (inválido) → ok=false (não cai em default silencioso)
[PASS] subtipo='xyz' → motivo de erro presente (não fica em branco)
[PASS] motivo menciona "escopo" (mesma família de mensagem do indeterminado)
[PASS] subtipo inválido → NENHUMA missão criada (rejeição antes de qualquer persistência, item de produto presente não muda o desfecho)

[CLEANUP] Missões, itens, ordens e endereços de teste removidos.

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

### `ReceberOrdemLaboratorioTddTest.php` — seção 4b (NOVO — identidade endereço antes do tradutor)
```
--- 4b. Identidade desconhecida (variante inventario_endereco) ---
[PASS] inventario_endereco: endereço desconhecido → ok=false
[PASS] inventario_endereco: status="rejeitada"
[PASS] inventario_endereco: motivo é o do Validator de IDENTIDADE ("Endereço desconhecido"), não o de escopo do tradutor
[PASS] inventario_endereco: motivo NÃO menciona "escopo" — prova que o tradutor nunca chegou a rodar
[PASS] inventario_endereco: ordem_id gravado (persistiu a rejeição)
[PASS] inventario_endereco: linha existe em wms_ordens
[PASS] inventario_endereco: status persistido = "rejeitada"
[PASS] inventario_endereco: NENHUMA missão criada (identidade barrou antes de chegar ao tradutor)
```
(seção completa do arquivo: `PASS=73  FAIL=0`)

### Integration — `tests/integration/IntegracaoWms/` (3 arquivos, inalterados nesta rodada)
```
ReceberOrdemApiTest.php   -> PASS=108  FAIL=0  (dos quais BUGS=0)
ConfigIntegracaoTest.php  -> PASS=47   FAIL=0
Fase2MigrationsTest.php   -> PASS=41   FAIL=0
```

### Não-regressão — domínio consumido
```
Designacoes/Testes/Laboratorios/GestaoMissoesAlocacaoWmsTddTest.php -> PASS=30  FAIL=0  (idêntico)
```

### Pin da baseline `AlocacaoWms` (arquivos que falham, reexecutados)
```
ConfirmarAlocacaoValidatorTddTest.php -> PASS=7  FAIL=12  (idêntico ao pin da Fase 3)
CronEstornoSmokeTddTest.php           -> FAIL=2            (idêntico ao pin da Fase 3)
HookEstornoTddTest.php                -> FATAL (RuntimeException: Provedor WMS não registrado: 'galpao')  (idêntico ao pin da Fase 3)
```

## Bugs encontrados

Nenhum bug novo de produção encontrado nesta rodada. A entrega dos tradutores
`movimentacao`/`inventario` está correta e bem coberta; os 2 gaps fechados
eram lacunas de TESTE (cenários da própria matriz de decisão documentada pelo
backend que ainda não tinham um caso dedicado), não bugs de comportamento —
em ambos os casos, o código de produção já se comportava corretamente quando
auditado (rejeição com motivo claro, sem default silencioso, sem criar missão
antes da hora); só faltava a prova formal em teste.

## `php -l`

Sem erros nos 2 arquivos tocados nesta rodada:
- `componentes/Missoes/Logistica/sinais/IntegracaoWms/Testes/Tradutores/InventarioOrdemTradutorTddTest.php`
- `componentes/Missoes/Logistica/sinais/IntegracaoWms/Testes/Laboratorios/ReceberOrdemLaboratorioTddTest.php`

## Resíduo no banco

Confirmado zero resíduo de `__test_*`/`ZZ-TEST*` em `wms_ordens`,
`wms_movimentos`, `wms_localizacoes`, `webhook_endpoints`, `integracao_wms_config`
e `missao_logistica` (ligadas a ordens de teste) após a execução completa da
suíte desta feature.

## Nota sobre commits

Nenhum `git commit` foi executado nesta sessão. O ambiente segue com um
mecanismo de checkpoint automático que já havia capturado trabalho de uma
sessão paralela (`separacao-endereco-wms`) antes desta auditoria começar — os
2 arquivos que editei (`InventarioOrdemTradutorTddTest.php`,
`ReceberOrdemLaboratorioTddTest.php`) aparecem como modificações não
commitadas no `git status` ao final deste trabalho.
