# Novidades — Estratégia de Testes

> O módulo **não tem testes** na v6. A suíte existente
> (`v6/tests/`) cobre apenas o Dashboard e a configuração GA.

---

## 1. Precedente de harness

`v6/tests/DashboardTestCase.php`:
`createRole()`, `createUser()`, `setModules()`, `DatabaseTransactions`,
`withoutMiddleware(VerifyCsrfToken::class)`.

Para este módulo, o `createRole()` com as permissões `changelog.*` é
essencial — o recurso `changelog` **não existe** na base atual, então o
teste precisa criá-lo (ou a v7 precisa de um seeder idempotente).

---

## 2. Estratégia

| Tipo | Escopo |
|---|---|
| **Feature** | CRUD, validação, permissões, renderização da timeline, modal |
| **Unit** | Conversão de data, sanitização do HTML, extração do resumo |
| **Integração** | Consistência entre `change_logs` e `update_logs` (rodapé) |

---

## 3. Fixtures necessárias

### 3.1 Factory

```php
// database/factories/ChangeLogFactory.php
class ChangeLogFactory extends Factory
{
    protected $model = ChangeLog::class;

    public function definition(): array
    {
        return [
            'title'         => fake()->sentence(4),
            'numero_versao' => (string) fake()->numberBetween(1, 6).'.0',
            'data_versao'   => now()->subDays(fake()->numberBetween(1, 500)),
            'descricao'     => '<p>'.fake()->paragraph().'</p>',
            'icon'          => 'fas fa-code-branch',
        ];
    }
}
```

### 3.2 Seeder de catálogo

```php
// database/seeds/ChangelogResourceSeeder.php
class ChangelogResourceSeeder extends Seeder
{
    public function run(): void
    {
        Resource::updateOrCreate(
            ['slug' => 'changelog'],
            [
                'name'        => 'Novidades',
                'description' => 'Changelog / Novidades do sistema',
                'modulo_id'   => 10,   // modulos.slug = 'novidades'
                'created_at'  => date('Y-m-d H:i:s'),
            ]
        );
    }
}
```

> Na v7, um **seeder idempotente de recursos** deve rodar em todas as
> instalações, garantindo que todo módulo registrado em `modulos` tenha
> pelo menos um recurso.

---

## 4. Casos de teste

### 4.1 Autorização

| # | Arrange | Act | Assert |
|---|---|---|---|
| A1 | Sem autenticação | `GET /cms/novidades` | 302 (middleware `check.logged`) |
| A2 | Usuário autenticado sem `changelog.index` | `GET /cms/novidades` | 200 (a rota não checa permissão) + a **timeline** renderizada, mas o cabeçalho "Novidades" **ausente** |
| A3 | Perfil com `changelog.index` (recurso criado) | `GET /cms/novidades` | 200 + cabeçalho "Novidades" + botão "Cadastrar Novidades" |
| A4 | Perfil sem `changelog.edit` | `GET /cms/novidades` | o botão de lápis **ausente**; o botão "Saber mais" **presente** |
| A5 | Perfil sem `changelog.delete` | `GET /cms/novidades` | o botão de lixeira **ausente** |
| A6 | `super-admin` | `GET /cms/novidades` | todos os botões presentes |
| A7 | Usuário autenticado | o menu do AdminLTE | o link "Novidades" presente |

### 4.2 CRUD

| # | Arrange | Act | Assert |
|---|---|---|---|
| M1 | Perfil com `changelog.create` | `POST /cms/novidades` com todos os campos | 302 para `changelog.index`; linha em `change_logs` |
| M2 | Idem, `title` com 2 caracteres | `POST` | 302 + erro `min:3` |
| M3 | Idem, `title` com 121 caracteres | `POST` | 302 + erro `max:120` (o `maxlength=150` do cliente **não** impede) |
| M4 | Idem, sem `numero_versao` | `POST` | 302 + erro `required` |
| M5 | Idem, sem `icon` | `POST` | 302 + erro `required` |
| M6 | Idem, sem `descricao` | `POST` | 302 + erro `required` |
| M7 | Idem, `data_versao` em `d/m/Y H:i:s` | `POST` | `data_versao` gravada como `Y-m-d H:i:s` |
| M8 | Idem, `data_versao` em `d/m/Y` | `POST` | `data_versao` gravada como `Y-m-d 00:00:00` |
| M9 | Idem, `data_versao` em `2026-01-01` | `POST` | **comportamento atual**: erro 500 (o segundo `catch` é vazio); **v7**: 302 + erro de data |
| M10 | Registro existente | `GET /cms/novidades/editar/{id}` | 200 + view `edit` com os valores |
| M11 | Registro existente | `PUT /cms/novidades/update/{id}` | 302 + linha atualizada |
| M12 | Registro existente | `DELETE /cms/novidades/delete/{id}` | 302 + linha **removida fisicamente** (comportamento atual) |
| M13 | Idem | `GET /cms/novidades/{id}` | **500** (rota sem método) — v7: a rota não deve existir |
| M14 | 3 registros com `id` e `data_versao` distintos | `GET /cms/novidades` | a timeline vem na ordem de `id` **decrescente** (não por data) |
| M15 | Registro com `data_versao` antiga e `id` alto | `GET /cms/novidades` | ele aparece **primeiro** (comportamento atual) |

### 4.3 Renderização e modal

| # | Arrange | Act | Assert |
|---|---|---|---|
| V1 | Registro com título de 100 caracteres | `GET /cms/novidades` | o título exibido tem no máximo 50 caracteres (`str_limit`) |
| V2 | Registro com descrição HTML longa | `GET /cms/novidades` | o resumo tem no máximo 250 caracteres **sem** tags HTML |
| V3 | Registro com descrição contendo `<script>` | `GET /cms/novidades` | o **resumo** não contém a tag (usa `{{ }}`); o `input hidden` **contém** o HTML (comportamento atual) |
| V4 | Nenhum registro | `GET /cms/novidades` | "Nenhum registro encontrado" |
| V5 | Registro | o HTML do modal | `#modalTitle`, `#content-modal` e `#version-modal` presentes |

### 4.4 Segurança

| # | Arrange | Act | Assert |
|---|---|---|---|
| S1 | Descrição com `<script>alert(1)</script>` | abrir o modal (navegador) | **comportamento atual**: o script executa; **v7**: o HTML é sanitizado |
| S2 | Título com `<img src=x onerror=alert(1)>` | idem | v7: sanitizado |

### 4.5 Rodapé e versão do sistema

| # | Arrange | Act | Assert |
|---|---|---|---|
| V6 | `update_logs` com a última versão `6.0.0` | `GET /cms/dashboard` | o rodapé mostra "Versão 6.0.0" com link para `changelog.index` |
| V7 | `update_logs` vazio | `GET /cms/dashboard` | **erro** (`->last()` retorna `null` e `->version` falha) — v7: tratar o estado vazio |
| V8 | `change_logs` sem entrada para a versão do rodapé | `GET /cms/dashboard` | o link leva a uma lista sem a versão correspondente (dessincronia) |

### 4.6 Catálogo de permissões

| # | Arrange | Act | Assert |
|---|---|---|---|
| C1 | Base migrada | `DB::table('resources')->where('slug','changelog')->exists()` | **hoje: `false`** — v7: `true` |
| C2 | Recurso criado com `modulo_id` do módulo `novidades` | renderizar a matriz de permissões | a linha "Changelog / Novidades" aparece |
| C3 | Módulo `novidades` desativado | renderizar a matriz | a linha desaparece (filtro por módulo ativo) |

---

## 5. Fixtures de view

| View | Variáveis |
|---|---|
| `prefecture.configurations.changelog.index` | `changelogs` (Collection), `title` |
| `prefecture.configurations.changelog.create` | `title` |
| `prefecture.configurations.changelog.edit` | `changelogs` (model), `title` |
| `prefecture.configurations.changelog.form` | `changelogs` (opcional), `errors` |

---

## 6. Como rodar

```bash
cd v6/
composer install
./vendor/bin/phpunit --testsuite Feature
./vendor/bin/phpunit tests/Feature/Changelog/ChangelogCrudTest.php
./vendor/bin/phpunit --filter testCadastraNovidadeComDataEmFormatoBr
```

Ambiente (de `v6/phpunit.xml`): `APP_ENV=testing`,
`CACHE_DRIVER=array`, `SESSION_DRIVER=array`, `QUEUE_DRIVER=sync`.

> A suíte usa `DatabaseTransactions`; o banco de teste precisa estar
> migrado e com o recurso `changelog` presente (executar o seeder de
> catálogo no setup).

---

## 7. Cobertura mínima aceitável para a v7

| Arquivo de teste | Casos |
|---|---|
| `tests/Feature/Changelog/ChangelogAuthorizationTest.php` | A1-A7 |
| `tests/Feature/Changelog/ChangelogCrudTest.php` | M1-M15 |
| `tests/Feature/Changelog/ChangelogRenderingTest.php` | V1-V5, S1-S2 |
| `tests/Feature/Changelog/ReleaseNoteFooterTest.php` | V6-V8 |
| `tests/Feature/Changelog/ResourceCatalogTest.php` | C1-C3 |
| `tests/Unit/DateConversionTest.php` | M7-M9 (conversão de data isolada) |

Meta: 100% das actions de `ChangeLogController` (exceto `show`, que deve
ser removida) e cobertura da sanitização do HTML.
