# Acesso Rápido — Estratégia de Testes

## 1. Estratégia

| Tipo | Escopo | Ferramenta sugerida (v7) |
|---|---|---|
| **Feature (portal)** | Rotas `/acesso-rapido/*` e `/acessos-rapido/*`: resolução de conteúdo, fallback para página estática, redirect de ouvidoria, clique automático. | `PHPUnit` + `Laravel\Dusk` (opcional, para o bloco da home). |
| **Feature (CMS)** | CRUD de atalhos, reordenação, ativação, DataTable JSON. | `PHPUnit` + `Laravel\Sanctum`/guard de sessão. |
| **Unit (model)** | Scopes, casts, serialização do `QuickLinkResource`. | `PHPUnit` (Pest opcional). |
| **Regressão (dados)** | Backfill do `contents` -> `quick_links` mantendo título, descrição, ícone, URL, ordem e status. | Script de console + asserção. |

> O legado **não possui nenhum teste** para este módulo. A suíte existente em
> `v6/tests/` cobre outras áreas; a v7 deve criar a cobertura do zero.

## 2. Fixtures necessárias

### 2.1 Factory

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

    public function definition(): array
    {
        return [
            'title'       => 'Atalho ' . $this->faker->word(),
            'description' => $this->faker->sentence(4),
            'icon'        => 'fas fa-star',
            'url'         => '/' . $this->faker->slug(),
            'is_blank'    => false,
            'is_active'   => true,
            'position'    => 1,
        ];
    }

    public function inactive(): static { return $this->state(fn () => ['is_active' => false]); }
}
```

### 2.2 Seed de view de teste

O bloco da home depende de três fatores que precisam estar presentes no ambiente
de teste:

| Fator | Origem | Como preparar |
|---|---|---|
| `modulos.status` do slug `acesso-rapido` | tabela `modulos` (id 49) | `Modulos::updateOrCreate(['slug' => 'acesso-rapido'], ['name' => 'Acesso Rápido', 'status' => true])` |
| `configurations.links_rapidos` | tabela `configurations` | `Configuration::updateOrCreate(['key' => 'links_rapidos'], ['value' => '1'])` |
| `configurations.layout` | tabela `configurations` | `Configuration::updateOrCreate(['key' => 'layout'], ['value' => 'modelo1'])` |

Além disso, o view composer `ConfigHeaderServiceProvider` roda em **todas** as
requisições: se a tabela `configurations` não tiver a chave `layout`, o
`getTemplateActived()` faz `->first()->value` sobre `null` e a aplicação quebra.
O seed de teste **precisa** criar `layout` e `pagina_em_manutencao`.

### 2.3 Páginas estáticas de teste

```php
StaticPage::create([
    'slug'   => 'manual-de-navegacao',
    'title'  => 'Manual de Navegação',
    'text'   => '<p>Conteúdo</p>',
    'layout' => 1,
    'clicks' => 0,
]);
```

> Atenção ao slug duplicado: o seed de teste deve garantir **uma** linha por slug.

## 3. Casos de teste — Portal

| # | Cenário | Arrange | Act | Assert |
|---|---|---|---|---|
| P1 | Slug com conteúdos | 2 `contents` na categoria `acesso-rapido` | `GET /acesso-rapido/perguntas-frequentes` | 200; corpo contém os títulos; view `portal.layouts.modelo1.informacoes-instituicao.acesso-rapido.perguntas-frequentes` |
| P2 | Slug sem conteúdo -> página estática | 1 `static_page` com slug `x`; nenhuma categoria `x` | `GET /acesso-rapido/x` | 200; corpo contém o texto da página estática |
| P3 | Slug inexistente | nenhum dado | `GET /acesso-rapido/nao-existe` | **404** (no legado: erro 500 — ver bug A1) |
| P4 | Redirect de ouvidoria | — | `GET /acesso-rapido/ouvidoria` | 302 para `/acessos-rapido/solicitar-informacao` |
| P5 | Página estática incrementa clique | `static_page` com `clicks = 0` | `GET /acessos-rapido/manual-de-navegacao` | `clicks` passa a 1 no banco |
| P6 | Slug `solicitar-informacao` usa o layout `newsbit` | `static_page` com slug `solicitar-informacao` | `GET /acessos-rapido/solicitar-informacao` | 200; view renderizada = `portal.layouts.newsbit.informacoes-instituicao.acesso-rapido.solicitar-informacao` |
| P7 | Bloco da home | `modulos.status = true`, `links_rapidos = '1'`, 2 atalhos ativos | `GET /` | corpo contém os títulos; não contém o título de um atalho inativo |
| P8 | Bloco da home com módulo desabilitado | `modulos.status = false` | `GET /` | corpo **não** contém a seção |
| P9 | Bloco da home com `links_rapidos = '0'` | `links_rapidos = '0'` | `GET /` | corpo **não** contém a seção |
| P10 | Ordem dos atalhos | 3 atalhos com `position` 1, 2, 3 | `GET /` | a ordem no HTML segue 1, 2, 3 |
| P11 | `is_blank` gera `target="_blank"` | atalho com `is_blank = true` | `GET /` | HTML contém `target="_blank"` |
| P12 | Link relativo | atalho com `url = '/licitacoes'` | `GET /` | `href="/licitacoes"` no HTML |
| P13 | Layout inexistente | `configurations.layout = 'inexistente'` | `GET /acesso-rapido/x` | 404 com `view_inexistente`; `configurations.pagina_em_manutencao` passa a `1` |
| P14 | Rota não aceita POST | — | `POST /acesso-rapido/x` | **405** (no legado: 200 — ver armadilha A2) |

## 4. Casos de teste — CMS

| # | Cenário | Arrange | Act | Assert |
|---|---|---|---|---|
| C1 | Listagem | usuário com `acesso-rapido.index` | `GET /cms/conteudos/acesso-rapido` | 200; view `prefecture.contents.acesso-rapido.index` |
| C2 | Listagem sem permissão | usuário sem a permissão | `GET /cms/conteudos/acesso-rapido` | redirect + flash "Você não tem autorização para acessar esta funcionalidade!" (`v6/app/Http/Middleware/CheckRoutePermission.php:26`) |
| C3 | Criar atalho | usuário com `acesso-rapido.create` | `POST /cms/conteudo` com `title`, `text`, `icon`, `link`, `category_contents_id` | linha criada em `contents` + `links`; redirect ao índice |
| C4 | Criar sem `title` | — | `POST` sem `title` | 422/redirect com erro "Por favor, informe um título." |
| C5 | Criar com `title` > 25 | — | `POST` com 26 caracteres | erro de validação |
| C6 | Editar | atalho existente | `PUT /cms/conteudo/{id}` | `contents` atualizado |
| C7 | Excluir | atalho existente | `DELETE /cms/conteudo/delete/{id}` | linha removida (sem soft delete no genérico) |
| C8 | Reordenar | 3 atalhos | `POST /cms/ajax/ajust-banners` com `sortable_data = [3,1,2]` | `order` = 1, 2, 3 conforme a ordem enviada |
| C9 | Ativar/desativar | atalho com `status = true` | `POST /cms/set-status` | `status` invertido; deixa de aparecer no portal |
| C10 | DataTable JSON | 3 atalhos | `GET /cms/datatable/acesso_rapido` | JSON com `data` ordenado por `value` e `titulo` correto |
| C11 | Permissões no JSON | usuário sem `acesso-rapido.edit` | `GET /cms/datatable/acesso_rapido` | `edit = false`, `delete = false` no JSON |
| C12 | `menuClicks` | `menu_orders` com slug `acesso-rapido` | `GET /cms/conteudos/acesso-rapido` | `clicks` incrementado |

## 5. Casos de teste — dados e migração

| # | Cenário | Assert |
|---|---|---|
| M1 | Backfill completo | 3 `quick_links` criados com título, descrição, ícone, URL, `is_blank`, `is_active` e `position` idênticos ao legado. |
| M2 | Backfill preserva a ordem | `position` = `contents.order` original. |
| M3 | Backfill de conteúdo sem `link` | linha criada com `url = ''` (ouNull, conforme decisão D6). |
| M4 | Conteúdo com múltiplos `links` | apenas 1 `quick_link` criada (o link mais recente). |
| M5 | Slug duplicado em `static_pages` | decisão explícita documentada; `manual-de-navegacao` deve ter 1 linha. |

## 6. Testes de equivalência funcional (porta a porta)

Comparar a saída do legado e da v7 para um conjunto fixo de seeds:

```bash
# Legado (v6)
php artisan tinker --execute='echo app(App\Http\Controllers\Portal\QuickAccessController::class);' # (não aplicável)
```

Procedimento prático:

1. Popular o banco v6 com o `AcessoRapidoSeeder` + `StaticPagesTableSeeder`.
2. Capturar o HTML da home e das 4 telas com `curl` (autenticado não é necessário
   para o portal).
3. Repetir na v7 com a mesma seed de `quick_links`.
4. Comparar: presença dos títulos, ordem, `href`, `target`, número de cards.

## 7. Como rodar

```bash
# suíte completa
php artisan test

# apenas o módulo
php artisan test --filter=QuickLink
php artisan test --filter=QuickAccess

# suíte legada (v6) — para comparar comportamento
cd v6 && ./vendor/bin/phpunit
```

Ambiente de teste:

```bash
cp .env.testing.example .env.testing
php artisan migrate:fresh --env=testing --seed
```

> O `migrate:fresh --seed` executa `DatabaseSeeder`, que **inclui**
> `AcessoRapidoSeeder` e `ManagementUnitTableSeeder`
> (`v6/database/seeds/DatabaseSeeder.php:43,50`). O seed de acesso rápido não é
> idempotente: rodar duas vezes duplica os dados — cuidado ao montar o ambiente
> de teste.

## 8. Cobertura mínima aceitável

- [ ] Todos os casos P1..P14.
- [ ] Todos os casos C1..C12.
- [ ] M1..M5.
- [ ] Teste de não regressão do redirect `/acesso-rapido/ouvidoria`.
- [ ] Teste de não regressão do redirect `/acesso-rapido/perguntas-frequentes`
      vindo de `Portal\PortalController@contentList`.
- [ ] Teste de 404 (não 500) para slug inexistente.
