# Unidades de Atendimento — Estratégia de Testes

> O legado (`v6`) **não possui testes** para este módulo (nenhum arquivo em
> `v6/tests/` referencia `ServiceUnits`, `unidades-de-atendimento` ou
> `ServiceUnit`). A estratégia abaixo é a proposta para a v7.

---

## 1. Estratégia

| Tipo | Escopo | Ferramenta |
|---|---|---|
| **Feature (portal)** | Rotas públicas: listagem, paginação, ordenação, 404, breadcrumbs, view por layout. | Pest ou PHPUnit + `RefreshDatabase`. |
| **Feature (CMS)** | Rotas `/cms`: listagem, cadastro, edição, exclusão individual e em lote, permissões. | Pest/PHPUnit. |
| **Unit** | Regras puras: geração de slug, desambiguação de slug, filtro da galeria, sanitização. | PHPUnit. |
| **Integração (view SQL)** | Se a view `unidades_de_atendimento` for mantida, validar suas colunas e ordenação. | Teste de banco (PostgreSQL). |

Regras de equivalência a testar (não podem regredir):

1. Página de 12 itens.
2. Ordenação `publication_date desc, id desc`.
3. 404 para slug inexistente e para slug de outra categoria.
4. Imagem de destaque **não** aparece na galeria.
5. Soft delete remove o item da listagem.
6. Slug duplicado recebe sufixo numérico.

## 2. Fixtures e factories necessárias

```php
// (ilustrativo) database/factories/CategoryContentFactory.php
CategoryContent::factory()->create(['slug' => 'unidades-de-atendimento', 'name' => 'Unidades de Atendimento', 'status_create' => true]);

// (ilustrativo) database/factories/ContentFactory.php
Content::factory()->for($category, 'categoryContents')->create([
    'title' => 'Unidade Central',
    'text' => '<p>Texto</p>',
    'slug' => 'unidade-central',
    'publication_date' => now(),
]);
```

| Fixture | Campos mínimos |
|---|---|
| `CategoryContent` | `slug`, `name`, `status_create = true`, `category_contents_id = null`. |
| `Content` | `title`, `slug`, `text`, `publication_date`, `category_contents_id`, `galery`. |
| `Archive` | `contents_id`, `path`, `name`, `extension`, `highlight` (true / null), `view_pdf`. |
| `Link` | `contents_id`, `link`, `title`. |
| `Configuration` | `key = 'layout'`, `value = 'modelo1'`. |
| Usuário Sentinel | papel `super-admin` e usuário com papel limitado (para permissões). |

**Ordem de criação obrigatória**: `Configuration(layout)` antes de qualquer
acesso ao portal; `CategoryContent` antes dos conteúdos.

## 3. Cenários de teste — Portal

| # | Cenário | Arrange | Act | Assert |
|---|---|---|---|---|
| T1 | Listagem retorna 200 | Categoria + 3 conteúdos | `GET /unidades-de-atendimento` | Status 200; view `portal.layouts.modelo1.unidades-de-atendimento.index`; 3 itens. |
| T2 | Paginação de 12 | 13 conteúdos | `GET /unidades-de-atendimento` | `assertCount(12, $response->viewData('contents')->items())`; total 13; 2 páginas. |
| T3 | Ordem por data desc | Conteúdos com datas distintas | `GET /unidades-de-atendimento` | Primeiro item = mais recente. |
| T4 | Desempate por id desc | Dois conteúdos com a mesma data | `GET /unidades-de-atendimento` | Ordem por id decrescente. |
| T5 | Soft delete oculta | 1 conteúdo `deleted_at` preenchido | `GET /unidades-de-atendimento` | Não aparece na listagem nem na view SQL. |
| T6 | Detalhe 200 | Conteúdo da categoria | `GET /unidades-de-atendimento/{slug}` | Status 200; `viewData('content')->slug` correto. |
| T7 | Slug inexistente → 404 | — | `GET /unidades-de-atendimento/nao-existe` | Status 404. |
| T8 | Slug de outra categoria → 404 | Conteúdo com `category_contents_id` != 44 | `GET /unidades-de-atendimento/{slug}` | Status 404. |
| T9 | Galeria sem a imagem de destaque | 2 archives: 1 `highlight = true`, 1 `highlight = null` | `GET /unidades-de-atendimento/{slug}` | `viewData('archives')` contém 1 item; o de destaque não está. |
| T10 | Galeria só com imagens | Archive `pdf` + Archive `png` | `GET /unidades-de-atendimento/{slug}` | O PDF não é renderizado no componente de galeria. |
| T11 | Link do breadcrumb | Conteúdo com `categoryContents` | `GET /unidades-de-atendimento/{slug}` | Breadcrumb "Unidades de Atendimento" com rota `conteudo.portal.list`. |
| T12 | Layout do portal | `Configuration.layout = modelo3` | `GET /unidades-de-atendimento` | Renderiza `portal.layouts.modelo3.unidades-de-atendimento.index`. |
| T13 | Conteúdo sem imagem | Conteúdo sem archives | `GET /unidades-de-atendimento` | Fallback `sem-imagem.png` presente no HTML. |

## 4. Cenários de teste — CMS

| # | Cenário | Arrange | Act | Assert |
|---|---|---|---|---|
| C1 | Acesso exige login | Usuário anônimo | `GET /cms/conteudos/unidades-de-atendimento` | Redirect para login / 403. |
| C2 | Listagem do CMS | `super-admin` logado | `GET /cms/conteudos/unidades-de-atendimento` | Status 200; view `prefecture.contents.unidades-de-atendimento.index`. |
| C3 | DataTable JSON | `super-admin` | `GET /cms/datatable/unidades-de-atendimento` | JSON com `data[]` contendo `titulo`, `id`, `galeria`, `edit`, `delete`. |
| C4 | Cadastro válido | `super-admin` | `POST /cms/conteudo` com `title`, `category_contents_id = 44`, `text` | Registro criado; slug gerado; redirect. |
| C5 | Título obrigatório | sem `title` | `POST /cms/conteudo` | Erro de validação em `title`. |
| C6 | Categoria obrigatória | sem `category_contents_id` | `POST /cms/conteudo` | Erro em `category_contents_id`. |
| C7 | Slug duplicado | 2 conteúdos com o mesmo título | `POST /cms/conteudo` | Segundo slug = `titulo-<max(id)+1>`. |
| C8 | Slug único no banco | — | após T7 | `contents.slug` continua sem duplicidade. |
| C9 | Imagem de destaque | Upload `archive_highlight` | `POST /cms/conteudo` | `archives.highlight = true` e `contents.galery` conforme enviado. |
| C10 | Galeria marcada | `galery = 1` | `GET /cms/conteudos/editar/unidades-de-atendimento/{id}` | Aba `#galeria` presente no HTML. |
| C11 | Exclusão individual | Conteúdo | `DELETE /cms/conteudo/{id}` | Registro removido (soft delete) e arquivos tratados. |
| C12 | Exclusão em lote | 3 conteúdos | `POST /cms/conteudo/deleteAll` com `toDelete[]` | 3 removidos. |
| C13 | Permissão de criação | Usuário sem `unidades-de-atendimento.create` | `GET /cms/conteudos/unidades-de-atendimento` | Botão "Cadastrar Unidade" ausente. |
| C14 | Slug inválido no CMS | `GET /cms/conteudos/nao-existe` | — | Redirect para `dashboard.index`. |
| C15 | Título de "Atas e Pontos" | `GET /cms/conteudos/atas-e-pontos` | — | (bug A5) — na v7 o título deve ser "Atas e Pontos de Frequência". |

## 5. Testes unitários

| # | Alvo | Assert |
|---|---|---|
| U1 | Geração de slug (`slugfy`/`generateSlug`) | "Unidade de Atendimento" → `unidade-de-atendimento`. |
| U2 | Sufixo de desambiguação | Slug repetido recebe `-N`. |
| U3 | Filtro da galeria | Somente extensões de imagem e `highlight` nulo. |
| U4 | Normalização do link de vídeo | `normalizeVideoLink()` aceita URL do YouTube e ID puro. |
| U5 | Sanitização do HTML (v7) | `<script>` removido; `<p>` preservado. |

## 6. Teste da view SQL (se mantida)

```sql
-- asserção estrutural (não executada como teste, apenas referência)
SELECT value, titulo, data_e_hora, id, galeria, slug FROM unidades_de_atendimento;
```

Verificar: (a) `data_e_hora` no formato `DD/MM/YYYY às HH24:MI`; (b) `galeria`
espelha `contents.galery`; (c) conteúdos com `deleted_at` não aparecem;
(d) `value` é sequencial por consulta (comportamento instável conhecido — A5).

## 7. Como rodar

```bash
# Laravel moderno (v7)
php artisan test --filter=ServiceUnit
php artisan test --filter=ServiceUnitsController
php artisan test tests/Feature/Portal/ServiceUnitsTest.php

# Cobertura do módulo
php artisan test --coverage --min=80
```

## 8. Ambiente de teste

- `APP_ENV=testing`, `DB_CONNECTION=pgsql` apontando para banco de teste
  separado (a view SQL exige PostgreSQL).
- Seed mínima: `Configuration(layout=modelo1)`, `CategoryContent(slug=unidades-de-atendimento)`.
- Storage: `Storage::fake('public')` para os uploads de destaque/galeria.
- Usuários: factory de usuário Sentinel com papel `super-admin` e um usuário
  sem permissões, para testar as policies.

## 9. Checklist de regressão funcional

- [ ] Portal lista no máximo 12 unidades por página.
- [ ] Ordem: mais recente primeiro; empate resolvido pelo id.
- [ ] Slug inexistente e slug de outra categoria devolvem 404.
- [ ] Imagem de destaque não aparece na galeria.
- [ ] Itens excluídos não aparecem no portal nem na DataTable.
- [ ] Exportações Excel/PDF/CSV do CMS disponíveis.
- [ ] Botões do CMS respeitam as permissões `unidades-de-atendimento.*`.
