# Feriados Municipais — Estratégia de Testes

## 1. Situação atual

O módulo **não tem nenhum teste** na v6 nem na v5.

| Verificação | Resultado |
|---|---|
| `v6/tests/Feature` | Somente testes do Dashboard (`Dashboard*Test.php`) |
| `v6/tests/Unit` | Sem testes de feriados |
| `v5/tests` | `ExampleTest.php` e `CnpjAlphanumericValidationTest.php` |

Infraestrutura disponível:

- `v6/phpunit.xml` — suítes `Feature` e `Unit`, `APP_ENV=testing`,
  `CACHE_DRIVER=array`, `SESSION_DRIVER=array`, `QUEUE_DRIVER=sync`.
  **Não define `DB_CONNECTION`**: os testes rodam contra a conexão de
  `v6/config/database.php`.
- `v6/tests/TestCase.php` — classe base mínima.
- `v6/tests/CreatesApplication.php` — bootstrap da aplicação.
- `v6/tests/DashboardTestCase.php` — base com helpers de papéis e usuários
  (`createRole`, `createUser`, `setModules`) que serve de modelo.

## 2. Estratégia proposta para a v7

| Tipo | Escopo | Arquivo sugerido |
|---|---|---|
| **Feature (portal)** | Página pública, filtro de ano, agrupamento por mês | `tests/Feature/Portal/FeriadosMunicipaisTest.php` |
| **Feature (CMS)** | CRUD, validação, permissões, exclusão em lote | `tests/Feature/Cms/FeriadosMunicipaisAdminTest.php` |
| **Unit** | Conversão de data, agrupamento por mês | `tests/Unit/FeriadoMunicipalTest.php` |
| **Segurança** | Autorização das rotas do CMS | `tests/Feature/Security/FeriadosMunicipaisAuthorizationTest.php` |

## 3. Casos de teste concretos

### 3.1 Portal — página pública

| # | Arrange | Act | Assert |
|---|---|---|---|
| P1 | 2 feriados no ano corrente (janeiro e março) | `GET /feriados-municipais` | Status 200; view `portal.layouts.modelo1.feriados-municipais.index`; HTML contém "Janeiro" e "Março" |
| P2 | 1 feriado no ano corrente e 1 no ano anterior | `GET /feriados-municipais` | O do ano anterior **não** aparece no HTML |
| P3 | 2 feriados no mesmo mês | `GET /feriados-municipais` | Apenas **um** cartão com o nome do mês; os 2 feriados listados dentro |
| P4 | 1 feriado com `description` "Feriado Municipal" | `GET /feriados-municipais` | HTML contém "Feriado Municipal" |
| P5 | 1 feriado com `icon` = `fas fa-flag` | `GET /feriados-municipais` | HTML contém `class="fas fa-flag"` |
| P6 | 1 feriado com `deleted_at` preenchido | `GET /feriados-municipais` | Não aparece |
| P7 | Nenhum feriado | `GET /feriados-municipais` | Status 200; nenhum cartão de mês |
| P8 | 1 feriado em 31/12 do ano corrente | `GET /feriados-municipais` | O dia exibido é "31" |
| P9 | `configurations.layout` = `modelo3` | `GET /feriados-municipais` | Renderiza a view do `modelo3` |

### 3.2 CMS — listagem e permissões

| # | Arrange | Act | Assert |
|---|---|---|---|
| A1 | Papel sem permissões, logado | `GET /cms/feriados-municipais` | 403 (via `check.route.permission`) |
| A2 | Papel com `feriados-municipais.index` | `GET /cms/feriados-municipais` | 200 |
| A3 | Papel com `feriados-municipais.create` | `GET /cms/feriados-municipais` | O botão "Cadastrar Feriados Municipais" aparece |
| A4 | Papel **sem** `feriados-municipais.create` | `GET /cms/feriados-municipais` | O botão de cadastro **não** aparece |
| A5 | Papel sem `feriados-municipais.delete` | `GET /cms/feriados-municipais` | O botão "Excluir Registro(s)" **não** aparece |
| A6 | `super-admin` | `GET /cms/feriados-municipais` | Todos os botões aparecem |
| A7 | `modulos.status = false` | `GET /cms` | O item "Feriados Municipais" **não** aparece no menu |

### 3.3 CMS — criação

| # | Arrange | Act | Assert |
|---|---|---|---|
| C1 | — | `POST /cms/feriados-municipais` com `date=25/12/2026`, `description=Natal`, `icon=fas fa-tree` | 302; linha criada com `date = 2026-12-25`, `description = 'Natal'`, `icon = 'fas fa-tree'` |
| C2 | Payload sem `date` | mesmo POST | Erro de validação; nenhuma linha criada |
| C3 | Payload sem `description` | mesmo POST | Erro de validação com a mensagem "O campo Descrição do feriado é obrigatório." |
| C4 | Payload sem `icon` | mesmo POST | Erro de validação com "O campo ícone é obrigatório." |
| C5 | `icon` com 2 caracteres | mesmo POST | Erro de validação ("Ícone não deve ter menos que 3 caracteres.") |
| C6 | `icon` com 61 caracteres | mesmo POST | Erro de validação ("Ícone não deve ter mais que 60 caracteres.") |
| C7 | `description` com 201 caracteres | mesmo POST | Erro de validação ("Descrição do feriado não deve ter mais que 200 caracteres.") |
| C8 | `date` = `31/02/2026` | mesmo POST | Data inválida: registrar o comportamento (no legado `strtotime` normaliza para 03/03/2026) |
| C9 | — | `POST /cms/feriados-municipais` com falha de banco (mock) | **Comportamento legado:** `dd($th)` despeja a exceção. Na v7: resposta de erro controlada, sem stack trace |

### 3.4 CMS — edição

| # | Arrange | Act | Assert |
|---|---|---|---|
| E1 | Registro com `description = Natal` | `PUT /cms/feriados-municipais/{id}` com `description=Natal&q` | `description` atualizado; `updated_at` alterado; **nenhum** registro novo criado |
| E2 | Registro existente | `GET /cms/feriados-municipais/{id}/edit` | O formulário mostra a data formatada `dd/mm/aaaa` e a descrição atual |
| E3 | `GET /cms/feriados-municipais/999999/edit` (id inexistente) | idem | **Comportamento legado:** renderiza o formulário de criação. Na v7: 404 |
| E4 | Registro existente | `GET /cms/feriados-municipais/{id}` (rota `show`) | **Comportamento legado:** 200 com corpo vazio. Na v7: remover a rota |
| E5 | Registro com `date` em 2026 | `PUT` com `date=01/01/2027` | `date` passa a `2027-01-01`; o registro **deixa** de aparecer no portal |

### 3.5 CMS — exclusão

| # | Arrange | Act | Assert |
|---|---|---|---|
| X1 | Registro existente | `DELETE /cms/feriados-municipais/{id}` | JSON 200 com `success`; `deleted_at` preenchido; some da listagem |
| X2 | Id inexistente | `DELETE /cms/feriados-municipais/999999` | JSON 500 com `erro` (comportamento legado) |
| X3 | 3 registros | `POST /cms/feriados-municipais/destroyAll` com `toDelete[]` = 3 ids | JSON 200; os 3 com `deleted_at` preenchido |
| X4 | — | `POST /cms/feriados-municipais/destroyAll` **sem** `toDelete` | **Comportamento legado:** responde 200 "deletado com sucesso" sem apagar nada. Na v7: 422 |
| X5 | — | `POST` com `toDelete[]` contendo id inexistente | Nada é apagado; resposta coerente (na v7, erro de validação) |
| X6 | Registro já excluído | `DELETE /cms/feriados-municipais/{id}` | 200 com `success` (o soft delete de um já excluído não falha) |

### 3.6 DataTable

| # | Arrange | Act | Assert |
|---|---|---|---|
| D1 | 2 feriados (1 excluído) | `GET /cms/datatable/feriados_municipais` | JSON com 1 linha; `data` no formato `DD/MM/YYYY`; `descricao` presente; `id` presente |
| D2 | Papel com `feriados-municipais.edit` | idem | A linha traz `edit = true` |
| D3 | Papel sem `feriados-municipais.delete` | idem | A linha traz `delete = false` |

### 3.7 Regressão de bugs conhecidos

| # | Cenário | Assert esperado na v7 |
|---|---|---|
| R1 | `POST /cms/feriados-municipais/destroyAll` sem `toDelete` | 422, **não** 200 |
| R2 | Inserir com falha de banco | Resposta 500/422 sem stack trace no corpo |
| R3 | Carregar a view de listagem do CMS | O `<script>` de `deleteAll.js` deve ser resolvido (sem 404) |
| R4 | `GET /cms/feriados-municipais/{id}/edit` com id inexistente | 404 |
| R5 | Renderizar o portal no `modelo6` | Nenhum 404 de CSS |
| R6 | `DELETE` de um feriado | A mensagem deve citar "feriado", não "diário oficial" |
| R7 | `GET /cms/feriados-municipais/create` | O link "Voltar" aponta para a listagem de feriados |

## 4. Fixtures e factories necessárias

```php
// (ilustrativo) database/factories/FeriadoMunicipalFactory.php
class FeriadoMunicipalFactory extends Factory
{
    public function definition(): array
    {
        return [
            'date'        => now()->addMonths(fake()->numberBetween(0, 11))->startOfMonth(),
            'description' => fake()->sentence(3),
            'icon'        => 'fas fa-calendar-day',
        ];
    }

    public function doAnoPassado(): self
    {
        return $this->state(fn () => ['date' => now()->subYear()]);
    }
}
```

Factories auxiliares: nenhuma específica do módulo; reaproveitar os helpers de
papel/usuário de `v6/tests/DashboardTestCase.php` para os testes de permissão.

## 5. Fixtures de view e seed para ambiente de teste

Criar um seeder de ambiente de teste que:

1. Insira `modulos` id 51 com `status = true`.
2. Insira `resources` (`slug = 'feriados-municipais'`, `modulo_id = 51`) e
   `menu_orders` (`slug = 'feriados-municipais'`, `clicks = 0`, `modulo_id = 51`).
3. Insira ~15 feriados distribuídos entre janeiro e dezembro do ano corrente,
   incluindo **um** feriado em 31 de dezembro (teste de fim de ano) e **um**
   com `deleted_at` preenchido.
4. Garanta `configurations.layout` = `modelo1` (ou itere sobre os 6 temas em
   testes separados).

**Estado atual no banco de desenvolvimento:** `feriados_municipais` tem **0
linhas**; `modulos.status` = `false`; `menu_orders.clicks` = 64.

## 6. Como rodar

```bash
cd v6

./vendor/bin/phpunit
./vendor/bin/phpunit tests/Feature/Portal/FeriadosMunicipaisTest.php
./vendor/bin/phpunit tests/Feature/Cms/FeriadosMunicipaisAdminTest.php
./vendor/bin/phpunit --filter FeriadosMunicipais
```

Observações do `v6/phpunit.xml`:

- `DB_CONNECTION` não está definido — na v7, definir explicitamente
  (`pgsql` de teste ou `sqlite` em memória, se o schema for compatível).
- `CACHE_DRIVER=array` e `SESSION_DRIVER=array` evitam dependência de Redis.
- `QUEUE_DRIVER=sync` dispensa worker.

## 7. Testes manuais de aceitação (checklist do negócio)

1. Cadastrar "25/12/2026 — Natal — fas fa-tree" no CMS.
2. Abrir `/feriados-municipais`: o feriado aparece em "Dezembro", com o dia 25.
3. Editar a descrição para "Natal (ponto facultativo)" e recarregar: o portal
   mostra o novo texto.
4. Excluir o feriado: ele some do portal e do CMS.
5. Excluir em lote (2 registros): ambos somem; o contador do botão volta a `(0)`.
6. Digitar `25/12/2027` e salvar: o registro **não** aparece no portal de 2026.
