# Requerimento de Diárias — Estratégia de Testes

## 1. Estado atual

**Não existe nenhum teste** para este módulo em `v6/tests/`. A suíte existente
cobre apenas o Dashboard (`v6/tests/Feature/Dashboard*.php`,
`v6/tests/DashboardTestCase.php`).

Consequência: qualquer refatoração do módulo (especialmente a migração para a
v7) precisa de uma rede de segurança criada do zero.

## 2. Estratégia proposta

| Tipo | Escopo | Ferramenta |
|---|---|---|
| **Feature (CMS)** | Rotas `/cms/requerimento-diarias*`, validação, upload, permissões | PHPUnit + `RefreshDatabase` + `Sentinel` de teste |
| **Feature (Portal)** | Busca pública, filtros, paginação, mascaramento | PHPUnit + `RefreshDatabase` |
| **Unit** | Repositório (`structureUpInsert`, `generatePDF`), `SelectRule`, máscaras do PDF | PHPUnit puro |
| **Manual** | Fidelidade visual do PDF, do DataTable e do layout do Portal | Checklist em `v6/tests/Feature/...` marcado como não automatizado |

Ambiente: PostgreSQL de teste (o schema usa `CREATE VIEW`, `ILIKE` e
`translate()` — dependências do PostgreSQL; **SQLite não serve**).

## 3. Fixtures necessárias

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

    public function definition(): array
    {
        return [
            'id_servidor'          => Servidor::factory(),
            'codigo_diarias'       => fake()->unique()->numberBetween(1, 99999),
            'dados_objetivo'       => fake()->sentence(),
            'cidade_destino'       => fake()->city(),
            'estado_destino'       => State::factory(),
            'placa_transporte'     => strtoupper(fake()->bothify('???-####')),
            'data_saida'            => '2026-03-01',
            'data_entrada'          => '2026-03-05',
            'duracao_viagem'        => '4 dias',
            'numero_diarias'        => '4',
            'valor_diarias'         => '250,00',
            'fundamentos_legais'    => 'Lei Municipal 1.234/2020',
        ];
    }
}
```

Factories exigidas:

| Factory | Campos mínimos |
|---|---|
| `ServidorFactory` | `cpf` (14 dígitos válidos), `name`, `cargo`, `matricula`, `identidade` |
| `StateFactory` | `uf`, `name`, `uf_code`, `countries_id` |
| `RequerimentoDiariasFactory` | ver acima |
| `RequerimentoDiariasConfigFactory` | `lei_padrao`, `archive`, `path` |
| `ArchiveFactory` | `title`, `name`, `path`, `requerimento_id` |

> **Dependência externa:** `Servidor` e `State` são modelos de outras
> unidades; se não existirem factories, criar helpers locais no setup do
> teste (`Servidor::create([...])`).

## 4. Fixtures de view/seed para o ambiente de teste

A view `req_diarias` é criada por migration — em ambiente de teste ela existe
após `migrate`. Mas a listagem do CMS depende dela, então:

```php
protected function setUp(): void
{
    parent::setUp();
    // Garante uma configuração singleton
    RequerimentoDiariasConfig::firstOrCreate([], [
        'lei_padrao' => 'Lei Padrão Municipal',
        'archive'    => 'lei-padrao.pdf',
        'path'       => 'storage/app/public/leis_de_requerimentos/lei-padrao.pdf',
    ]);
}
```

`configurations` precisa ter as chaves lidas pelo PDF:
`favicon`, `estado`, `titulo_site`, `cidade` e `layout` (com
`layout = modelo1` e a view `portal.layouts.modelo1.home.index` presente).

## 5. Casos de teste

### 5.1 CMS — CRUD

| # | Arrange | Act | Assert |
|---|---|---|---|
| T01 | Usuário `super-admin` autenticado | `GET /cms/requerimento-diarias` | 200; view `prefecture.contents.requerimento-diarias.index` |
| T02 | Usuário sem a permissão `requerimento-diarias.index` | `GET /cms/requerimento-diarias` | Redirect (403/redirect do `check.route.permission`) |
| T03 | Usuário não autenticado | `GET /cms/requerimento-diarias` | Redirect para `login` |
| T04 | Formulário válido, sem arquivo | `POST /cms/requerimento-diarias` | Registro criado; redirect para `requerimento-diarias.index`; flash de sucesso |
| T05 | Formulário sem `codigo_diarias` | `POST /cms/requerimento-diarias` | 422 / redirect com erro em `codigo_diarias` |
| T06 | Formulário com `id_servidor = 0` | `POST` | Erro (regra `SelectRule`) |
| T07 | Formulário com `archive` `.exe` | `POST` | Erro de mime |
| T08 | Registro existente | `GET /cms/requerimento-diarias/{id}/edit` | 200; formulário preenchido |
| T09 | Registro existente + **novo** arquivo | `PUT /cms/requerimento-diarias/{id}` | Registro atualizado; `archives` atualizado |
| T10 | Registro existente **sem** novo arquivo | `PUT /cms/requerimento-diarias/{id}` | **Comportamento legado:** erro inesperado capturado pelo controller (ver `05-REGRAS-E-PORTABILIDADE.md` §3.4). Na v7: deve **passar** |
| T11 | Registro existente | `DELETE /cms/requerimento-diarias/{id}` | JSON `{"success": ...}`; `deleted_at` preenchido; linha ausente de `req_diarias` |
| T12 | 3 registros | `POST /cms/requerimento-diarias/destroyAll` com `toDelete[]` | JSON de sucesso; 3 linhas com `deleted_at` |
| T13 | `toDelete` vazio | `POST .../destroyAll` | **Legado:** retorna sucesso mesmo sem deletar (bug). Na v7: erro 422 |
| T14 | Registro com `codigo_diarias = 10` | `GET /cms/requerimento-diarias/create` | `codigo_sugestao = 11` |

### 5.2 CMS — configurações

| # | Arrange | Act | Assert |
|---|---|---|---|
| T15 | Sem configuração | `GET /requerimento-diarias/configuracoes` | 200; `configuracoes` vazio |
| T16 | Sem configuração | `PUT /requerimento-diarias/configuracoes/update` com `lei_padrao` + arquivo | Registro criado com `id = 1`; arquivo em `leis_de_requerimentos` |
| T17 | Com configuração | `PUT .../update` sem arquivo | `lei_padrao` atualizada; `archive`/`path` preservados |
| T18 | Com configuração | `GET /deletar-arquivo-padrao` | `archive` e `path` = null |
| T19 | Requerimento com `archives` | `GET /deletar-arquivo/{id}` | `archives` removido; redirect com flash |
| T20 | Requerimento com `archives` | `GET /deletar-arquivo/{id}` | **Legado:** `padrao_arquivo_config` não persiste (coluna inexistente). Na v7: `false` persistido |

### 5.3 CMS — DataTable

| # | Arrange | Act | Assert |
|---|---|---|---|
| T21 | 2 requerimentos | `GET /cms/datatable/requerimento_diarias` | JSON com 2 registros; campos `value`, `requerente`, `objetivo_viagem`, `fundamentos_legais`, `download_link`, `id`; `edit`/`delete` booleanos |
| T22 | Requerimento com `deleted_at` | idem | Registro **ausente** (filtro `deleted_at IS NULL` da view) |
| T23 | Usuário sem `requerimento-diarias.delete` | idem | `delete = false` no JSON |

### 5.4 Portal

| # | Arrange | Act | Assert |
|---|---|---|---|
| T24 | 3 requerimentos | `GET /requerimento-de-diarias` | 200; view `portal.layouts.busca-diarias.show`; paginação com 10 por página |
| T25 | Servidor "João da Silva" | `GET /requerimento-de-diarias?req_name=joao` | 1 registro |
| T26 | Servidor "João da Silva" | `GET /requerimento-de-diarias?req_name=joao` (com acento) | Busca sem acento encontra o registro (normalização por `translate`) |
| T27 | CPF `12345678901234` | `GET /requerimento-de-diarias?cpf=12345678901234` | 1 registro |
| T28 | CPF `123` | `GET /requerimento-de-diarias?cpf=123` | Erro de validação ("Digite o CPF completo.") |
| T29 | `codigo_diarias = 42` | `GET /requerimento-de-diarias?num_diaria=42` | 1 registro |
| T30 | `data_saida = 2026-03-01`, `data_entrada = 2026-03-05` | `GET /requerimento-de-diarias?data_retorno=2026-03-03` | **Legado:** filtra por `data_saida <= 2026-03-03`. Na v7: filtra por `data_entrada` |
| T31 | Requerimento | `GET /requerimento-de-diarias/{id}` | PDF (status 200, `Content-Type: application/pdf`) |
| T32 | Requerimento | `GET /requerimento-de-diarias/{id}` | Corpo do PDF **contém** `xxx` no CPF e no RG (mascaramento) |
| T33 | Requerimento | `GET /cms/requerimento-diarias/{id}` | Corpo do PDF **não** contém `xxx` (CPF completo) |
| T34 | Requerimento sem `archives`, com config de lei padrão | `GET /requerimento-de-diarias` | Link "Visualizar Regulamentação" aponta para `leis_de_requerimentos/{archive}` |

### 5.5 Unit — repositório e regras

| # | Arrange | Act | Assert |
|---|---|---|---|
| T35 | Request com `data_saida = '01/03/2026'` | `RequerimentoRepository::structureUpInsert()` | `data_saida = '2026-03-01'` |
| T36 | Request com `data_entrada = '05/03/2026'` | idem | `data_entrada = '2026-03-05'` |
| T37 | Request com campo vazio | `filter_request()` (AppTrait) | Campo vira `null` |
| T38 | `SelectRule` | `passes('x', '0')` / `passes('x', '1')` | `false` / `true` |

### 5.6 Regressão de segurança (v7)

| # | Cenário | Assert |
|---|---|---|
| T39 | `GET /requerimento-de-diarias?req_name=' OR 1=1 --` | Nenhum erro SQL; resultado vazio; query lograda com binding |
| T40 | Usuário com apenas `requerimento-diarias.index` | `POST /cms/requerimento-diarias` → 403 (legado: passa) |
| T41 | `GET /deletar-arquivo-padrao` | 405 (rota não deve aceitar GET destrutivo) |

## 6. Como rodar

```bash
# Dentro de v6/ (ou da raiz da v7)
php artisan test --filter=RequerimentoDiarias

# Arquivo específico
php artisan test tests/Feature/RequerimentoDiarias/CmsCrudTest.php

# Suíte de integração do módulo
php artisan test --testsuite=RequerimentoDiarias

# Views (se usar Dusk — não há Dusk no projeto)
php artisan view:clear && php artisan config:clear
```

Configuração do banco de teste: `.env.testing` com
`DB_CONNECTION=pgsql`, `DB_DATABASE=prefeituras_test` e
`DB_HOST` apontando para o container PostgreSQL. Executar
`php artisan migrate --database=pgsql` antes da suíte (as views SQL são
criadas pelas migrations — **não** são recriadas por `migrate:fresh` sem as
migrations presentes).

## 7. Checklist manual (não automatizável)

- [ ] Layout do PDF idêntico ao atual (cabeçalho, tabelas, brasão).
- [ ] Exportação Excel/PDF do DataTable mantém larguras e cabeçalho.
- [ ] Máscara de CPF no formulário do Portal (`000.000.000-00`).
- [ ] O "Limpar campos" limpa `#cpf`, `#num_diaria` e `#req_name`.
- [ ] O botão "Excluir Arquivo" do requerimento remove o anexo.
- [ ] O botão "Excluir Arquivo" da configuração remove o arquivo padrão.
- [ ] Sugestão de `codigo_diarias` = `max + 1`.
- [ ] "Usar Arquivo de Lei Padrão" faz o PDF/ícone aparecer no Portal.
- [ ] Exportar Excel da listagem do CMS respeita `stripHtml`.

## 8. Dados de exemplo (seed de desenvolvimento)

Ambiente de homologação sem registros (verificado: `requerimento_diarias` = 0
linhas). Para testes manuais é útil um seed:

```php
// database/seeders/RequerimentoDiariasDemoSeeder.php
RequerimentoDiariasConfig::updateOrCreate(['id' => 1], [
    'lei_padrao' => 'Lei Municipal nº 1.234/2020',
    'archive'    => 'lei-municipal-1234-2020.pdf',
    'path'       => 'storage/app/public/leis_de_requerimentos/lei-municipal-1234-2020.pdf',
]);

RequerimentoDiarias::factory()->count(12)->create();
```

> Não criar esse seed na v6 (o escopo desta tarefa é apenas documentação).
> `[NÃO VERIFICADO]` — não existe seed de demonstração equivalente no legado.
