# Mais Conteúdos — Estratégia de Testes

## 1. Estado atual

Não há testes para o módulo. A suíte existente em `v6/tests` cobre Dashboard, Google
Analytics e Revisões (`v6/tests/Feature/`, `v6/tests/Unit/`), com o bootstrap
`vendor/autoload.php` definido em `v6/phpunit.xml`.

Não há factory de `CategoryContent`/`Content` em `v6/database/factories/` (o diretório
contém `HighlightFactory.php`, `ManualAreaFactory.php`, `ManualFactory.php` e
`UserFactory.php`).

## 2. Estratégia proposta

| Camada | Arquivo | Foco |
|---|---|---|
| Unit | `tests/Unit/CategoryTest.php` | Model: relações, scopes, `getAllDescendants` |
| Unit | `tests/Unit/CategorySlugTest.php` | Geração e desambiguação de slug |
| Feature | `tests/Feature/CategoryControllerTest.php` | CRUD, regras de exclusão, reordenação |
| Feature | `tests/Feature/CategoryApiTest.php` | API interna (`/cms/categories`) |
| Feature | `tests/Feature/CategoryMenuTest.php` | Item de menu "Mais Conteúdos" |
| Feature | `tests/Feature/CategoryPortalTest.php` | Rotas `portal/{slug}` e views genéricas |

## 3. Fixtures necessárias

```php
// database/factories/CategoryFactory.php
$factory->define(\App\Prefecture\CategoryContent::class, function (Faker $faker) {
    return [
        'name'                => $faker->words(2, true),
        'slug'                => $faker->unique()->slug,
        'status_create'       => 0,          // "Mais Conteúdos"
        'category_contents_id'=> null,       // categoria de topo
        'order'               => 1,
        'status'              => true,
        'quantidade_itens'    => 5,
        'layout'              => 1,
    ];
});

// categoria de sistema
$factory->state(\App\Prefecture\CategoryContent::class, 'system', function () {
    return ['status_create' => 1];
});
```

```php
// database/factories/ContentFactory.php (trecho)
'title'              => $faker->sentence(4),
'slug'               => $faker->unique()->slug,
'text'               => $faker->paragraph,
'status'             => true,
'publication_date'   => now()->subDay(),
'category_contents_id' => $category->id,
```

## 4. Casos de teste

### 4.1 Model (Unit)

| # | Arrange | Act | Assert |
|---|---|---|---|
| U1 | Categoria com `status_create = 0` e pai nulo | `Category::userDefined()->count()` | 1 |
| U2 | Categoria com `status_create = 1` | `Category::userDefined()->count()` | 0 |
| U3 | Categoria com pai | `Category::userDefined()->count()` | 0 (não é de topo) |
| U4 | Árvore A → B → C | `$a->getAllDescendants()` | 2 elementos (B e C) |
| U5 | Árvore A → B → C | `$a->getAllDescendants(true)` | Nomes prefixados com o pai |
| U6 | Categoria com pai `publicacoes` | `getAllDescendants(true)` | Nome **não** prefixado |
| U7 | `category_contents_id` apontando para si | `getAllDescendants()` | Deve lançar/limitar (hoje: recursão infinita) |

### 4.2 Controller — CRUD (Feature)

| # | Arrange | Act | Assert |
|---|---|---|---|
| C1 | Usuário autenticado | `POST /cms/categoria-conteudo` com `name` | `assertDatabaseHas('category_contents', ['status_create' => 0])` |
| C2 | Idem | `POST` sem `name` | `assertSessionHasErrors('name')` |
| C3 | Idem | `POST` com `name` > 190 | `assertSessionHasErrors('name')` |
| C4 | Categoria existente com mesmo nome e pai | `POST` com o mesmo `name` | `slug` recebe prefixo `cat<id>_` (comportamento legado) |
| C5 | Categorias com `order` 1 e 2 | `POST` com `order = 2` | As posições são rebalanceadas (+1) |
| C6 | Categoria de sistema (`status_create = 1`) | `DELETE /cms/categoria-conteudo/{id}` |flash "Esta categoria não pode ser deletada"; registro permanece |
| C7 | Categoria com conteúdos | `DELETE` | Flash "não pode ser excluída"; registro permanece |
| C8 | Categoria sem conteúdo e com `menu_items` | `DELETE` | Categoria removida (soft delete) e `menu_items` removidos |
| C9 | Categoria | `PUT /cms/categoria-conteudo/{id}` | Nome/slug atualizados |
| C10 | Categoria | `POST /cms/ajust-categorias` com `sortable_data` | `order` reescrito conforme a nova sequência |

### 4.3 API interna (Feature)

| # | Arrange | Act | Assert |
|---|---|---|---|
| A1 | Sem sessão | `POST /cms/categories` | 401/403 (`Sentinel::check()`) |
| A2 | Com sessão | `POST /cms/categories` com `name`, `order`, `parent_id` | 201 e `{"success": ..., "id": ...}` |
| A3 | Com sessão | `POST` sem `parent_id` | 422 com `{"errors": {...}}` |
| A4 | Com sessão | `POST` com `parent_id` inexistente | 422 (`exists:category_contents,id`) |
| A5 | Com sessão | `PUT /cms/categories` com `categories[]` | Todas as categorias atualizadas |
| A6 | Com sessão | `DELETE /cms/categories` com `id` | Categoria removida |
| A7 | Com sessão | `GET /cms/categories/{slug}` | JSON com `id`, `name`, `order`, `parentId` |

### 4.4 Menu (Feature)

| # | Arrange | Act | Assert |
|---|---|---|---|
| M1 | 2 categorias com `status_create = 0` e pai nulo | `GET /cms` | O menu contém "Mais Conteúdos" com 2 sub-itens |
| M2 | Categoria com `status_create = 1` | `GET /cms` | A categoria **não** aparece no submenu |
| M3 | Categoria com pai | `GET /cms` | Não aparece no submenu |
| M4 | `modulos.status = 0` para `mais-conteudos` | `GET /cms` | O item "Mais Conteúdos" não aparece |

### 4.5 Portal (Feature)

| # | Arrange | Act | Assert |
|---|---|---|---|
| P1 | Categoria "Aluguéis" com 2 subcategorias | `GET /portal/alugueis` | HTTP 200; view `generic.index` com as subcategorias |
| P2 | Subcategoria com 15 conteúdos | `GET /portal/alugueis/residenciais` | Paginação com 12 itens por página |
| P3 | Subcategoria com conteúdos de 2 anos | `GET /portal/alugueis/residenciais` | O filtro de anos lista 2 anos |
| P4 | Conteúdo com `status = false` | `GET /portal/alugueis/residenciais/aluguel-x` | Não exibe o conteúdo |
| P5 | Conteúdo com `publication_date` futura | idem | Não exibe o conteúdo |
| P6 | Conteúdo publicado | idem | Exibe e incrementa `contents.clicks` |
| P7 | Slug inexistente | `GET /portal/nao-existe` | Redirect para `home` |
| P8 | Slug de sistema (`noticias`) | `GET /portal/noticias` | Redirect para `noticia.index` |

### 4.6 Select de categoria do CRUD de conteúdos

| # | Arrange | Act | Assert |
|---|---|---|---|
| S1 | 1 categoria "Mais Conteúdos" com 1 subcategoria | `GET /cms/conteudo/create` | O select contém a subcategoria |
| S2 | Categoria de sistema | `GET /cms/conteudo/create` | Não aparece no select |

## 5. Cenários manuais (QA)

1. Criar "Aluguéis" → aparece no submenu "Mais Conteúdos".
2. Criar subcategoria "Residenciais" → aparece como categoria pai possível.
3. Publicar conteúdo em "Residenciais" → visível em `/portal/alugueis/residenciais`.
4. Tentar excluir "Aluguéis" com conteúdo → mensagem de bloqueio.
5. Excluir categoria vazia com item de menu → menu limpo.
6. Arrastar categorias na lista → ordem persistida.

## 6. Como rodar

```bash
cd v6

./vendor/bin/phpunit                                        # suíte completa
./vendor/bin/phpunit tests/Feature/CategoryControllerTest.php
./vendor/bin/phpunit tests/Feature/CategoryPortalTest.php
./vendor/bin/phpunit tests/Unit/CategoryTest.php

./vendor/bin/phpunit --filter test_recorte_de_mais_conteudos
```

Para os testes de portal, garantir `configurations.key = 'layout'` definido (o
`getTemplateActived()` depende dele) e as views genéricas do modelo ativo presentes.
