# Transmissões — Estratégia de Testes

> Versão analisada: `v6`. O módulo **não possui nenhum teste automatizado**
> na v6 (verificação: não há suíte de testes referenciando `transmissoes`).
> Esta pasta define a estratégia para a **v7**.

## 1. Diagnóstico da v6

| Aspecto | Situação |
|---|---|
| Testes unitários | **Inexistentes.** O parser de URL está inline no Blade, sem classe testável. |
| Testes de feature | **Inexistentes.** Não há `tests/Feature` referenciando `transmissoes`. |
| Cobertura da exclusividade de status | **Nula** — o bug A4 (`where('order', null)`) passaria despercebido. |
| Cobertura do ID cru do YouTube | **Nula** — o bug A2 passaria despercebido. |

## 2. Estratégia para a v7

| Camada | Tipo | Foco |
|---|---|---|
| Parser / normalização de URL | **Unit** | `VideoUrlResolver`: ID puro, `watch?v=`, `youtu.be`, `/embed/`, `/shorts/`, Vimeo, host arbitrário, string vazia, URL malformada. |
| Regras de domínio | **Unit (model)** | Exclusividade de status; normalização ao salvar. |
| CMS | **Feature** | Listagem, cadastro, edição, toggle, permissões, validação. |
| Portal | **Feature (view)** | Renderização do widget conforme flag do módulo e conteúdo ativo. |
| Endpoint público | **Feature** | `GET /api/transmission-atual` retorna a transmissão ativa ou `null`. |

## 3. Fixtures e factories necessárias

```php
// (ilustrativo) database/factories/TransmissionFactory.php
class TransmissionFactory extends Factory
{
    public function definition(): array
    {
        return [
            'title'     => 'Sessão Ordinária',
            'video_url' => 'https://www.youtube.com/watch?v=YbJOTdZBX1g',
            'video_id'  => 'YbJOTdZBX1g',
            'channel'   => VideoChannel::Youtube,
            'is_live'   => false,
            'status'    => false,
        ];
    }

    public function active(): static { return $this->state(fn () => ['status' => true]); }
    public function live(): static   { return $this->active()->state(fn () => ['is_live' => true]); }
}
```

Fixtures de ambiente (v6):

| Fixture | Origem | Uso |
|---|---|---|
| `modulos.status = true` para `transmissoes` | `v6/database/migrations/2024_11_01_150206_create_modulos_table.php` (id 44) | Necessário para o widget aparecer no portal. |
| `category_contents` id 36, slug `transmissoes` | `v6/database/seeds/CategoryContentTableSeeder.php:201-205` | Categoria dos conteúdos. |
| `menu_orders` slug `transmissoes`, `modulo_id = 44` | `v6/database/seeds/MenuOrderSeeder.php:267-273` | Telemetria. |
| `resources` slug `transmissoes`, `modulo_id = 44` | `v6/database/seeds/ResourcesTableSeeder.php:549-556` | Permissão. |
| View SQL `transmissoes` | `v6/database/migrations/2024_10_01_180234_create_view_transmissoes.php` | DataTable do CMS. |

## 4. Casos de teste — CMS

| # | Arrange | Act | Assert | Tipo |
|---|---|---|---|---|
| C1 | Usuário `super-admin` autenticado; módulo ligado | GET `/cms/conteudos/transmissoes` | HTTP 200; renderiza `Lista de Transmissões`; `menu_orders.clicks` do slug `transmissoes` aumenta em 1 | Feature |
| C2 | Usuário sem `transmissoes.create` | GET `/cms/conteudos/transmissoes` | Botão "Cadastrar Transmissão" **ausente** | Feature |
| C3 | Usuário sem `transmissoes.delete` | GET `/cms/conteudos/transmissoes` | Checkbox mestre e botão de exclusão em lote **ausentes** | Feature |
| C4 | Payload válido `{title, category_contents_id=36, link, status=1}` | POST `/cms/conteudo` | HTTP 302; `contents` com `category_contents_id = 36`; `links` criada com o link normalizado; atividade registrada | Feature |
| C5 | Payload sem `title` | POST `/cms/conteudo` | Redirect com erro em `title`; nada gravado | Feature |
| C6 | Payload com `link` de 201 caracteres | POST `/cms/conteudo` | Erro de validação em `link` | Feature |
| C7 | Transmissão A ativa; POST de B com `status=1` | POST `/cms/conteudo` | A.status = false; B.status = true; **exatamente 1 ativo** na categoria | Feature |
| C8 | Transmissão A ativa; edição de B (desativada) com `status` desmarcado | PUT `/cms/conteudo/{B}` | Comportamento atual: A é **desativada** (bug A4). Teste de caracterização na v6; na v7 o esperado passa a ser A permanecer ativa | Feature |
| C9 | Transmissão A ativa; B desativada; clique no toggle de B | POST `/cms/transmissoes/{B}/status` com `{contents_id: B}` | B.status = true. **Comportamento atual (bug A4): A continua ativa** → 2 ativos. Na v7, A deve ser desativada | Feature |
| C10 | Transmissão A ativa; clique no toggle de A | POST `/cms/transmissoes/{A}/status` | A.status = false; nenhum ativo na categoria | Feature |
| C11 | Qualquer estado | GET `/cms/datatable/transmissoes` | JSON com `data[]` contendo `value`, `titulo`, `status_name_transmissao` (`Ativado`/`Desativado`), `status`, `id` e as flags `edit`/`delete`/`active`/`inactivate` | Feature |
| C12 | `modulos.status = false` | GET `/cms/modulos` e POST de update | Flag alterna; após religar, o widget volta a aparecer | Feature |

## 5. Casos de teste — Portal

| # | Arrange | Act | Assert | Tipo |
|---|---|---|---|---|
| P1 | Módulo `transmissoes` **desligado**; conteúdo ativo existe | GET `/` (qualquer tema) | Nenhum `<iframe>` nem texto "Transmissão" na resposta | Feature (view) |
| P2 | Módulo ligado; **nenhum** conteúdo na categoria 36 | GET `/` | Nenhum card de transmissão | Feature (view) |
| P3 | Módulo ligado; conteúdo ativo com link `https://www.youtube.com/watch?v=YbJOTdZBX1g` | GET `/` | Resposta contém `https://www.youtube.com/embed/YbJOTdZBX1g`, `embed-responsive-16by9` e o rótulo "Transmissão" | Feature (view) |
| P4 | Módulo ligado; conteúdo ativo com link `https://youtu.be/YbJOTdZBX1g` | GET `/` | `embed/YbJOTdZBX1g` presente | Feature (view) |
| P5 | Módulo ligado; conteúdo ativo com link de host não-YouTube (`https://exemplo.gov.br/video`) | GET `/` | `iframe src="https://exemplo.gov.br/video"` presente | Feature (view) |
| P6 | Módulo ligado; conteúdo ativo com link **não reconhecível** | GET `/` | Resposta contém `Transmissão não disponível` e **não** contém `<iframe>` | Feature (view) |
| P7 | Módulo ligado; **dois** conteúdos ativos | GET `/` | Exatamente **um** `<iframe>` (garantia de unicidade do widget) — na v7 a ordem deve ser determinística | Feature (view) |
| P8 | Conteúdo ativo **sem** registro em `links` | GET `/` | Página **não** quebra com erro fatal (bug A3); na v7 exibe o aviso de indisponibilidade | Feature (view) |
| P9 | Módulo ligado; conteúdo ativo; tema `modelo1` e `modelo2` | GET `/` | Player presente em ambos (paridade entre temas) | Feature (view) |
| P10 | Idem, temas `modelo3`..`modelo6` | GET `/` | Player presente em todos | Feature (view) |

## 6. Casos de teste — parser (unit, v7)

| # | Entrada | Saída esperada |
|---|---|---|
| U1 | `YbJOTdZBX1g` | `https://www.youtube.com/embed/YbJOTdZBX1g` |
| U2 | `https://www.youtube.com/watch?v=YbJOTdZBX1g` | `https://www.youtube.com/embed/YbJOTdZBX1g` |
| U3 | `https://youtu.be/YbJOTdZBX1g` | `https://www.youtube.com/embed/YbJOTdZBX1g` |
| U4 | `https://www.youtube.com/embed/YbJOTdZBX1g` | `https://www.youtube.com/embed/YbJOTdZBX1g` |
| U5 | `https://www.youtube.com/shorts/YbJOTdZBX1g` | `https://www.youtube.com/embed/YbJOTdZBX1g` |
| U6 | `https://vimeo.com/123456789` | `https://player.vimeo.com/video/123456789` |
| U7 | `https://exemplo.gov.br/video` | URL original (canal `custom`) |
| U8 | `""` (vazio) | Exception de validação / `null` |
| U9 | `não é uma url` | Exception de validação |

## 7. Comandos exatos

Ambiente da **v6** (para conferência manual e caracterização):

```bash
cd /home/one/projects/maxima/v7/v6

# Verificar a view SQL
docker exec v6_6f22e1bd-postgres-1 psql -U postgres -d prefeituras_v5 \
  -c "select pg_get_viewdef('transmissoes'::regclass, true)"

# Conferir as colunas da view
docker exec v6_6f22e1bd-postgres-1 psql -U postgres -d prefeituras_v5 \
  -c "\d transmissoes"

# Listar as transmissões cadastradas
docker exec v6_6f22e1bd-postgres-1 psql -U postgres -d prefeituras_v5 \
  -c "select * from transmissoes"

# Conferir a flag do módulo
docker exec v6_6f22e1bd-postgres-1 psql -U postgres -d prefeituras_v5 \
  -c "select id, name, slug, status from modulos where slug = 'transmissoes'"

# Conferir quantas transmissões estão ativas (deve ser <= 1)
docker exec v6_6f22e1bd-postgres-1 psql -U postgres -d prefeituras_v5 \
  -c "select count(*) as ativos from contents where category_contents_id = 36 and status is true and deleted_at is null"
```

Ambiente da **v7** (testes automatizados previstos):

```bash
php artisan test --filter=Transmission
php artisan test --filter=VideoUrlResolver
php artisan test --testsuite=Feature --filter=TransmissionWidget
```

## 8. Checklist manual de aceite (v6)

1. `/cms/modulos` → ligar **Transmissões**.
2. `/cms/conteudos/transmissoes` → "Cadastrar Transmissão".
3. Preencher `Título` = "Sessão de Testemunho" e `Video (Youtube)` =
   `https://www.youtube.com/watch?v=YbJOTdZBX1g`, marcar **Ativar/Desativar**.
4. Salvar → a listagem mostra **Ativado**.
5. Abrir o portal → o card "Transmissão" aparece na lateral com o player.
6. Cadastrar uma segunda transmissão **Ativada** → a primeira passa a
   **Desativado** na listagem.
7. Clicar no toggle da segunda → verificar no banco se há **um** ativo
   (espera-se hoje **dois**, bug A4).
8. Cadastrar com o ID cru `YbJOTdZBX1g` → no portal aparece
   "Transmissão não disponível" (bug A2).
