# Testes — Editor Estrutural + Landing Sections Editor

> Caminhos relativos à raiz `lotep/` (legado) e a `public-v7/` (alvo). Nenhum teste foi executado
> nesta análise; os números vêm dos arquivos e da baseline declarada em `V2/TEST_BASELINE.md`.

---

## 1. Estratégia

| Camada | Tipo | Por quê |
|---|---|---|
| Contrato da home (`parseConfig`) | unit puro | função determinística, sem banco |
| `BlockRenderer` | unit com views de fixture | comparação com `view()->make()` |
| `SleCmsController` | unit com `DatabaseTransactions` | regras de área/criação/desativação |
| Schemas/widgets | unit | sanitize e registro |
| Endpoints do serviço | feature | rotas, middleware, cache, JSON |
| Render da home | feature/HTML | ordem, hidden, fallback |

O legado usa `Tests\TestCase` do Laravel 5.5, com `setUp()` sem tipo de retorno (PHP 7.4).

---

## 2. Testes existentes

### 2.1 `tests/Feature/V2/LandingSectionsEditorTest.php`

**Todos os 6 testes são pulados** no `setUp`:

```php
$this->markTestSkipped(
    'LandingSectionsEditor requer implementação das rotas v2.landing-sections.* e v2.layout-base.*'
);
```

Fonte: `:29-33`. A justificativa está desatualizada: as rotas do serviço existem; a rota
`v2.layout-base.*` não existe.

| Teste | Arrange | Act | Assert |
|---|---|---|---|
| `test_get_form_endpoint` (`:125`) | login Sentinel + shares de view | `GET form` sem tipo e com `type=hero` | 400; 200 contendo o título |
| `test_validate_section_endpoint` (`:143`) | sem CSRF | `POST validate` com título vazio e válido | 422 com `errors`; 200 `success` |
| `test_preview_save_and_render_endpoint` (`:172`) | token único | `POST preview/save` e `GET preview/render` | cache contém a seção; HTML contém o título |
| `test_layout_base_integration` (`:209`) | rota `v2.layout-base.update` | POST `landing-sections` | redirect + `LayoutBaseConfig` |
| `test_render_section_endpoint` (`:237`) | sem CSRF | POST `render-section` | `{success, html}` com o título |
| `test_media_rotation_returns_404_for_invalid_id` (`:258`) | id 9999 | `POST media/9999/rotate` | 404 |

**Problemas do arquivo:**

- `setUp()` referencia `App\Admin\User`, `Sentinel::login()` e `route('v2.layout-base.update')`, que
  não existe neste módulo.
- `Route` de `layout-base` não está declarada em nenhum arquivo do módulo.
- O skip esconde regressões reais dos endpoints do serviço.

### 2.2 `HomepageSectionsServiceTest` — 11 casos

| Teste | Assert principal |
|---|---|
| `test_section_defaults_cover_all_static_types` (`:23`) | `count(defaults) == count(STATIC_TYPES)` |
| `test_default_sections_list_has_nine_elements_in_default_order` (`:34`) | 9 itens, tipos iguais a `ALL_TYPES` |
| `test_parse_config_without_config_returns_defaults` (`:46`) | `null` e `version 2` devolvem defaults |
| `test_parse_config_valid_list_returns_order_and_static_parameters` (`:59`) | ordem preservada, desconhecido ignorado |
| `test_parse_config_dynamic_content_is_never_exposed_as_section_data` (`:81`) | `sections` vazio para dinâmica |
| `test_parse_config_extracts_header_widget_outside_sections_order` (`:96`) | `header.title` extraído |
| `test_parse_config_hide_flag_marks_hidden_and_preserves_position` (`:118`) | `hidden` + posição |
| `test_comunicado_enters_config_order_but_not_default_layout` (`:137`) | comunicado só com config |
| `test_type_slugs_mapping_is_complete` (`:158`) | `knownTypes` ↔ `TYPE_SLUGS` |
| `test_jogos_autorizados_is_optional_not_in_default_order` (`:168`) | 9 default, opcional fora |
| `test_parse_config_accepts_optional_section_in_order_without_exposing_data` (`:179`) | ordem inclui, `sections` não |

É o **melhor ativo de teste** do conjunto: cobre o contrato sem banco.

### 2.3 `BlockRendererSandboxTest` — 3 casos

| Teste | O que prova |
|---|---|
| `test_sandbox_produz_output_idêntico_à_view_canônica` (`:90`) | 9 amostras de Blade (HTML puro, `{{ }}`, `{!! !!}`, `@if`, `@foreach`, `@php`, `@include`, multilinha) produzem saída idêntica a `View::make` |
| `test_template_com_erro_php_nao_explode_e_devolve_marcador` (`:117`) | variável inexistente → `<!-- SLE render error -->` |
| `test_vazio_retorna_vazio` (`:126`) | `''` e `null` → `''` |

Fixtures: diretório `storage/framework/views/testing-sle-fixtures`, namespace temporário
`v2-sletest`, `layoutConfigs` determinístico via `View::share` (`:44-87`).

**Limite:** o teste valida paridade, não segurança. Ele **confirma** que a diretiva `@php` e o `@include`
são executados — o comportamento esperado do sandbox, mas indesejado no V7.

### 2.4 `SleCmsControllerTest` — 9 casos

| Teste (`:`) | Arrange | Act | Assert |
|---|---|---|---|
| `test_editor_returns_correct_view` (`:53`) | suporte mockado | `editor()` | view + 8 áreas |
| `test_saveEditor_creates_new_content_for_empty_areas` (`:75`) | request com 2 áreas | `saveEditor` | linhas criadas |
| `test_saveEditor_updates_existing_content` (`:111`) | linha existente | `saveEditor` | conteúdo atualizado |
| `test_saveEditor_preserves_content_type_per_area` (`:135`) | áreas css/js | `saveEditor` | tipo preservado/derivado |
| `test_saveEditor_deactivates_when_content_emptied` (`:169`) | conteúdo vazio | `saveEditor` | `active=false`, linha mantida |
| `test_saveEditor_ignores_empty_untouched_areas` (`:207`) | área vazia sem registro | `saveEditor` | nenhuma linha |
| `test_authorizeSupportAccess_blocks_non_support` (`:235`) | e-mail diferente | `meta()` | `HttpException` 403 |
| `test_editor_returns_null_for_missing_areas` (`:259`) | banco sem linhas | `editor()` | `null` por área |
| `test_saveEditor_calls_activity_logger` (`:287`) | mock do logger | `saveEditor` | `activity()` chamado |

Mocks: `Sentinel::shouldReceive` com usuário `stdClass` (`:38-50`); `V2AuditLogger` por
`$this->app->instance` (`:52-58`).

### 2.5 Widgets

| Arquivo | Teste | Assert |
|---|---|---|
| `HomeComunicadoWidgetTest` (`:15`) | sanitize preserva `<p>`, `style`, remove `<script>`/`onclick` | passa com o regex atual |
| `HomeJogosAutorizadosWidgetTest` (`:14`) | tipo registrado | `arrayHasKey` |
| `HomeJogosAutorizadosWidgetTest` (`:22`) | render do partial | `data-section`, `data-testid` e valores |

---

## 3. Fixtures e factories necessárias

| Fixture | Para quê |
|---|---|
| usuário de suporte | `Sentinel::login` + `config(['v2.support.email'])` |
| `LayoutBaseConfig` com `homepage_sections` v3 | render/parse |
| `StructuralPortalContent` por área | editor e fallback |
| `v2_gallery_media` com `id`, `file_path`, `variants`, `alt_text`, `updated_at` | `prepareSections` |
| `SleAsset` css/js/image | biblioteca |
| view temporária `v2-sletest::partials.sandbox-child` | paridade do sandbox |

Não há factories no legado: os testes usam `create()` direto ou `DB::table`.

---

## 4. Lacunas de cobertura

| Lacuna | Risco |
|---|---|
| Nenhum teste dos 10 endpoints do serviço (todos skipped) | rotas sem RBAC passam despercebidas |
| Nenhum teste de `sanitizeContent` com tipo desconhecido | payload arbitrário persistido |
| Nenhum teste de `prepareSections` (consulta única, mídia deletada) | N+1/regressão de mídia |
| Nenhum teste de `saveHomepage` (sanitização + auditoria) | payload cru no audit |
| Nenhum teste de reordenação/duplicidade | posição única não garantida |
| Nenhum teste de permissão nas rotas do módulo | gate por e-mail não regride? |
| Nenhum teste de upload de assets | extensão/MIME/snippet |
| Nenhum teste de `html-free`/`bloco-sle` | execução de HTML/template |
| Nenhum teste de concorrência do preview | dois editores no mesmo token |
| Nenhum teste de `LayoutBaseComposer` | cache estático entre requests em runtime de longa duração |

---

## 5. Casos propostos para o V7

### 5.1 Contrato de layout (unit)

| Caso | Arrange | Assert |
|---|---|---|
| ordem default | nenhum slot | 9 tipos na ordem canônica |
| reorder | 3 slots | posições 0..2, sem colisão |
| hidden | slot `hidden=true` | aparece na árvore, não no render |
| dinâmico | `type=home.imprensa` | `props` ignoradas |
| header | config `portal.header` | renderiza no slot, fora de `main` |
| tipo desconhecido | `widget_type=foo` | 422 no save; ignorado no render |

### 5.2 Autorização (feature)

| Caso | Esperado |
|---|---|
| usuário sem `layout.ver` | 403 no GET |
| usuário com `layout.editar` sem `aprovar` | pode rascunhar, 403 no publish |
| perfil `suporte` | permitido, mas auditado |
| módulo inativo (ADR-034) | 404 em qualquer área |

### 5.3 Preview

| Caso | Esperado |
|---|---|
| usuário A salva preview | usuário B não lê o conteúdo de A |
| TTL expirado | 404/redirect, sem render |
| rascunho com bloco inválido | 422, não entra no cache |

### 5.4 Mídia e render

| Caso | Esperado |
|---|---|
| 5 blocos com 1 mídia cada | **1** query em `gallery_media` |
| mídia deletada | bloco não renderiza imagem; log |
| `media_id` inválido | 422 na validação |
| `<script>` em props de texto | escapado, não executado |
| comunicado | AST → HTML semântico, sem `{!! !!}` |

### 5.5 Snapshot

Um teste de snapshot do HTML da home com as 9 seções default, comparando com a baseline visual
aprovada. Protege a decisão de "sem config = layout atual".

---

## 6. Comandos

Legado:

```bash
vendor/bin/phpunit tests/Unit/V2/Modules/Support/StructuralLandingEditor
vendor/bin/phpunit tests/Unit/V2/Services/LandingSectionsEditor
vendor/bin/phpunit tests/Feature/V2/LandingSectionsEditorTest.php
```

V7:

```bash
php artisan test --filter=LandingLayout
php artisan test --filter=StructuralLandingEditor
```

---

## 7. Critério de saída

- Nenhum `markTestSkipped` no diretório do módulo/serviço.
- Contrato v3 coberto por teste que **não** depende de banco.
- Autorização testada em todas as rotas de escrita.
- Teste de query única para `prepareSections`.
- Teste que falha se HTML/Blade voltar a ser aceito em qualquer coluna.
