# Gestão de Permissões — Testes

> Estratégia, casos existentes e lacunas. Todos os testes usam
> `Illuminate\Foundation\Testing\DatabaseTransactions`, portanto o banco de
> teste precisa estar migrado.

## 1. Estratégia

O módulo é coberto em três níveis:

| Nível | Objetivo | Arquivos |
|---|---|---|
| Unitário de serviço | resolução de chave, níveis, bypass, CRUD de perfil | `tests/Unit/V2/Core/Services/PermissionServiceTest.php` |
| Unitário de controller | validação, persistência, auditoria, respostas | `tests/Unit/V2/Modules/Infraestrutura/GestaoPermissoes/Controllers/` |
| Feature HTTP | stack completa de middleware e acesso real por rota | `tests/Feature/V2/Rbac/CommonUserModuleAccessE2ETest.php` |
| Feature de integração | fluxo de criação de usuário e CRUD de perfil via rotas | `tests/Feature/V2/PermissionsTest.php` |

Não há teste de browser (Playwright) para este módulo. A matriz de rádio e o
gerador de slug não são cobertos por automação.

## 2. Casos existentes — `PermissionServiceTest`

Arquivo com 412 linhas, 18 métodos.

| Caso | Arrange | Act | Assert |
|---|---|---|---|
| `test_hasPermission_returns_true_for_support_email` | mock com e-mail de suporte | `hasPermission` read/write | ambos `true` |
| `test_is_high_admin_reconhece_apenas_perfis_de_alta_permissao` | mocks super-admin, administrador, operador, null | `isHighAdmin` | `true/true/false/false` |
| `test_hasPermission_returns_false_for_unauthenticated` | `null` | `hasPermission` | `false` |
| `test_hasPermission_read_access_for_valid_role` | mock parcial com `Exemplo => read` | read | `true` |
| `test_hasPermission_write_access_for_valid_role` | `Exemplo => write` | read e write | `true` nos dois |
| `test_hasPermission_returns_false_for_none_access` | `Exemplo => none` | read e write | `false` nos dois |
| `test_resolvePermission_exact_canonical_key` | `Operators => write/read` | resolve chave exata | valor correto |
| `test_resolvePermission_slug_to_canonical` | `Operators => write` | resolve `operadores` | `write` |
| `test_resolvePermission_canonical_to_stored_slug` | `operadores => write` | resolve `Operators` | `write` |
| `test_resolvePermission_is_case_insensitive` | `Faq => write` | resolve `faq`, `FAQ` | `write` |
| `test_resolvePermission_ignores_separators` | chaves canônicas | resolve slugs hifenizados | `write`/`read` |
| `test_resolvePermission_explicit_aliases` | módulos com alias | resolve slug | `write` |
| `test_resolvePermission_chains_two_aliases_of_same_module` | `formularios`/`formulario-dinamico` | resolve o outro | `write` |
| `test_resolvePermission_unknown_and_invalid_values` | chave ausente/valor `super` | resolve | `none` |
| `test_createRole_creates_role_successfully` | dados válidos | `createRole` | persiste, UUID, auditoria `created` |
| `test_updateRole_updates_role_successfully` | perfil existente | `updateRole` | atualiza nome/slug/permissões, auditoria `updated` |
| `test_deleteRole_soft_deletes_role` | perfil sem usuários | `deleteRole` | `true`, `deleted_at` preenchido, auditoria `deleted` |
| `test_getAssignedUsersCount_returns_zero_when_no_users` | perfil sem usuários | `getAssignedUsersCount` | `0` |

Observação: o teste usa `Mockery` para `V2AuditLogger`, porque o construtor do
serviço exige a dependência.

## 3. Casos existentes — controllers

### `GestaoPermissoesRolesControllerTest` (5 métodos)

| Caso | Fluxo | Assert |
|---|---|---|
| `test_index_retorna_view_com_roles` | cria duas roles; mock de `Request` | view `v2-gestaopermissoes::roles.index`, dados `roles`/`search` |
| `test_store_cria_nova_role` | login Sentinel; POST de perfil | redirect 302, mensagem de sucesso, linha no banco |
| `test_update_atualiza_role` | perfil existente | nome/slug atualizados, mensagem de sucesso |
| `test_destroy_soft_deleta_role` | perfil sem usuários | soft delete, mensagem de sucesso |
| `test_destroy_role_nao_encontrada_retorna_erro` | UUID inexistente | mensagem “Perfil não encontrado” |

### `GestaoPermissoesPermissionsControllerTest` (4 métodos)

| Caso | Fluxo | Assert |
|---|---|---|
| `test_index_retorna_view_com_usuarios` | dois usuários com perfil | view e chaves `users`/`search` |
| `test_store_cria_novo_usuario_com_sentinel` | POST com `role_id` e `v2_role_id` | usuário criado, `v2_role_id` gravado |
| `test_update_atribui_role_ao_usuario` | POST de edição | `v2_role_id` atualizado |
| `test_update_usuario_nao_encontrado_retorna_erro` | id inválido | redirect com erro |

### `GestaoPermissoesPermissionsRegressionTest` (6 métodos)

| Caso | Foco |
|---|---|
| `it_updates_user_name_email_and_role` | atualização dos três campos |
| `it_updates_password_when_provided` | `Hash::check` da nova senha |
| `it_preserves_password_when_not_provided` | senha antiga continua válida |
| `it_rejects_duplicate_email` | `ValidationException` em `email` |
| `it_loads_roles_in_batch_for_index` | `v2_role_name` preenchido (sem N+1) |
| `it_returns_edit_view_with_user_data` | view de edição com `user` e `rolesV2` |

## 4. Casos existentes — feature HTTP (`CommonUserModuleAccessE2ETest`)

278 linhas. Provider com **21 módulos**:

| Grupo | Módulos |
|---|---|
| DadosAbertos | `Operators`, `TransparenciaFiscal` |
| Infraestrutura | `CertificadoDigital`, `FormulariosDinamicos`, `MailAudit`, `GestaoPermissoes` |
| Institucional | `AInstituicao`, `BasicPage`, `EmpresasAssociadas`, `Faq`, `PerguntasFrequentes` |
| Publicacoes | `AgendaEventos`, `Legislacao`, `Licitacoes`, `Noticias`, `SorteSua` |
| Servicos | `AutoTesteJogoResponsavel`, `Ouvidoria`, `SeloOperador`, `Webhook` |
| Utilitarios | `Roadmap` |

Métodos:

| Método | O que prova |
|---|---|
| `test_usuario_comum_com_perfil_canonico_acessa_modulo` | usuário não-suporte/não-super-admin com chave canônica recebe 200 |
| `test_usuario_comum_com_perfil_legado_slug_acessa_modulo` | perfil antigo com slug recebe 200 |
| `test_usuario_comum_sem_permissao_e_barrado` | perfil vazio recebe 302 para o dashboard |
| `test_perfil_canonico_com_todas_as_permissoes_acessa_todos_os_modulos` | 21 rotas em sequência |
| `test_perfil_legado_slug_com_todas_as_permissoes_acessa_todos_os_modulos` | idem com slugs |

Infraestrutura dos testes:

- cria `V2Role` com `permissions_v2`;
- cria `User` com `v2_role_id` por atribuição direta;
- anexa o papel Sentinel `operador` (senão `CmsAuthenticate` barra);
- cria e completa `Activation`;
- faz `Sentinel::login`;
- desativa `VerifyCsrfToken`.

Esse arquivo é a rede de segurança contra a regressão de aliases: a cada
módulo novo com slug divergente, ele precisa ser adicionado ao provider.

## 5. Casos existentes — `tests/Feature/V2/PermissionsTest.php`

| Método | Cobertura |
|---|---|
| `test_permission_service_basic_logic` | leitura/escrita herdada de `V2Role` e atualização do perfil |
| `test_support_user_has_implicit_access` | bypass do e-mail de suporte |
| `test_global_helper_v2_can` | helper global com usuário logado |
| `test_user_creation_via_controller_endpoint` | POST real de criação de usuário com `v2_role_id` |
| `test_v2_roles_crud_flow` | index, store, update e destroy via rotas |

Ponto frágil: dois métodos usam `dd()` em caso de erro de sessão
(`:183`, `:252` do arquivo), o que interrompe a suíte em falha em vez de
produzir um assert legível.

## 6. Fixtures e factories necessárias

Não há factories dedicadas a `V2Role`. Os testes criam registros manualmente:

```php
$role = V2Role::create([
    'name' => 'Comum RBAC E2E',
    'slug' => 'comum-rbac-e2e',
    'permissions_v2' => ['Noticias' => 'write'],
]);
```

(`CommonUserModuleAccessE2ETest.php:84-88`)

Para o V7, criar:

| Fixture | Conteúdo mínimo |
|---|---|
| `RoleFactory` | nome, slug, `permissoes` com tokens válidos |
| `CmsUserFactory` | nome, e-mail, senha, `role_id` |
| `SupportUserSeeder` | conta reservada + perfil `suporte` |
| `ModuleManifestFake` | dois módulos: um com `aprovar`, outro só com `ver/editar` |

## 7. Comandos

```bash
# suíte do serviço
vendor/bin/phpunit tests/Unit/V2/Core/Services/PermissionServiceTest.php

# controllers do módulo
vendor/bin/phpunit tests/Unit/V2/Modules/Infraestrutura/GestaoPermissoes/

# acesso HTTP por módulo (65 casos: 21 × 3 + 2)
vendor/bin/phpunit tests/Feature/V2/Rbac/CommonUserModuleAccessE2ETest.php

# integração de criação de usuário e CRUD de perfil
vendor/bin/phpunit tests/Feature/V2/PermissionsTest.php

# suítes completas
vendor/bin/phpunit --testsuite Unit
vendor/bin/phpunit --testsuite Feature
```

O projeto usa PHPUnit `~6.0` (`composer.json:75`). Os testes declaram
`protected function setUp()` sem tipo de retorno, compatível com PHP 7.4.

## 8. Lacunas de cobertura

| Lacuna | Por que importa | Teste sugerido |
|---|---|---|
| GET de mutação com perfil `read` | o middleware libera qualquer GET | rota de download que gera/atualiza arquivo deve ser bloqueada |
| Parâmetro `v2.permission:Operators,write` | hoje é ignorado; um teste que espera bloqueio por parâmetro falharia | teste de contrato documentando o comportamento real |
| Rota fora de `active_pattern` | fail-open | usuário sem perfil acessa rota autenticada não mapeada |
| `update()` de usuário alvo suporte/super-admin | não há proteção de servidor | POST direto deve retornar 403 |
| Perfil `super-admin` existente em `v2_roles` | update/destroy não bloqueiam o alvo | criar linha com slug `super-admin` e tentar editar |
| Exclusão de perfil com usuários | regra existe no serviço | teste HTTP de `destroy` com usuário vinculado |
| Valor inválido na matriz | sanitização silenciosa | POST com `permissions[X]=super` não grava |
| Matriz com todos os módulos | nenhum teste verifica a lista renderizada | assert de contagem/ordem e ausência de `restricted` |
| Alias novo esquecido | regressão de 403 | provider do E2E é a barreira, mas não cobre módulo futuro |
| `permissions_v2` como string JSON | compatibilidade | teste de `getUserPermissions()` com string |
| Auditoria de perfil | chamadas testadas por mock, não o conteúdo | assert em `v2_audit_logs` |

## 9. Como validar manualmente

1. Criar um perfil com `Noticias = read` e outro com `Noticias = write`.
2. Associar um usuário comum ao perfil `read`.
3. Abrir `/cms/permissoes/roles` — deve listar (GET).
4. Abrir `/cms/permissoes/roles/create` — deve exibir o cartão de bloqueio.
5. Fazer POST direto para `/cms/permissoes/roles` — deve redirecionar com erro.
6. Trocar o perfil para `write` e repetir — deve criar.
7. Remover o perfil do usuário — todas as rotas de módulo devem redirecionar
   ao dashboard, exceto as de fail-open (`/cms`, `/cms/search`).
