Files
jhonny-editor/code/engine/testes/test_aplicar_plano_de_edicao.py
T
João Henrique 2c9bdf2ff0 feat: criado monitoramento contínuo de trilhas com varredura recur
- criado monitoramento contínuo de trilhas com varredura recursiva e baixa prioridade
- gerado e validado plano editorial revisável do depoimento da Erika sem aplicação na timeline
- adicionada leitura e comparação estrutural da timeline após aplicar planos de edição
- limitadas as classificações de trilhas às duas maiores probabilidades de cada categoria
- limpo o catálogo musical para reiniciar as análises sem remover dados de vídeo

Resumo:
- 34 arquivos alterados
- 6 novos
- 28 modificados
- 0 removidos

 28 files changed, 833 insertions(+), 75 deletions(-)

Arquivos:
  - .jhonny/analises.db
  - code/cep-plugin/index.html
  - code/cep-plugin/main.js
  - code/engine/analisar_trilhas.py
  - code/engine/aplicar_plano_de_edicao.py
  - code/engine/consultar_detalhes_de_trilha.py
  - code/engine/editor/__init__.py
  - code/engine/editor/aplicador_de_plano_de_edicao.py
  - code/engine/editor/backup_de_sequencia.py
  - code/engine/editor/escrita/escrita_no_editor.py
  - code/engine/editor/modelos.py
  - code/engine/editor/validacao_semantica.py
  - code/engine/integracoes/audio/classificador_de_musica_essentia.py
  - code/engine/integracoes/audio/modelos_de_analise_musical.py
  - code/engine/integracoes/audio/provider_de_analise_musical_essentia.py
  - code/engine/persistencia/esquema.py
  - code/engine/persistencia/repositorio_de_trilhas.py
  - code/engine/testes/duplos_de_premiere.py
  - code/engine/testes/test_aplicador_de_plano_de_edicao.py
  - code/engine/testes/test_aplicar_plano_de_edicao.py
  - code/engine/testes/test_escrita_no_editor.py
  - code/engine/testes/test_leitor_de_plano_de_edicao.py
  - code/engine/testes/test_validador_semantico_de_plano.py
  - code/plugins/premiere-pro/skills/editar-por-voz/SKILL.md
  - code/plugins/premiere-pro/skills/editar-por-voz/criterios/08-formato-de-saida.md
  - code/plugins/premiere-pro/skills/editar-por-voz/criterios/10-revisao-humana.md
  - code/src/tools/timeline.ts
  - code/tests/tools/tool-modules.test.ts
  - .jhonny/planos/
  - code/.jhonny/analises.db-shm
  - code/.jhonny/analises.db-wal
  - code/engine/editor/verificador_da_aplicacao.py
  - code/engine/testes/test_verificador_da_aplicacao.py
  - code/plugins/premiere-pro/skills/editar-por-voz/criterios/11-gancho.md
2026-09-10 15:09:27 -04:00

105 lines
4.9 KiB
Python

"""Testes da resolução do executável Node usada pelo aplicador de planos."""
import os
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
from engine import aplicar_plano_de_edicao
from engine.editor import AcaoDeEdicao, ErroDePlanoInvalido, PlanoDeEdicao, TipoDeAcao
class TesteResolucaoDoNode(unittest.TestCase):
"""Garante que a engine não dependa do PATH herdado pelo painel CEP."""
def test_prioriza_executavel_configurado_no_ambiente(self):
"""Usa o caminho explícito quando o painel ou sistema o fornece."""
with patch.dict(os.environ, {"PREMIERE_NODE_BIN": "/custom/node"}, clear=False), patch(
"engine.aplicar_plano_de_edicao.os.path.isfile", return_value=True
), patch("engine.aplicar_plano_de_edicao.os.access", return_value=True):
self.assertEqual(aplicar_plano_de_edicao.resolver_caminho_do_node(), "/custom/node")
def test_usa_caminho_conhecido_quando_node_nao_esta_no_path(self):
"""Encontra a instalação Homebrew mesmo com PATH incompleto."""
with patch.dict(os.environ, {}, clear=True), patch(
"engine.aplicar_plano_de_edicao.shutil.which", return_value=None
), patch("engine.aplicar_plano_de_edicao.os.path.isfile", side_effect=lambda caminho: caminho == "/opt/homebrew/bin/node"), patch(
"engine.aplicar_plano_de_edicao.os.access", return_value=True
):
self.assertEqual(aplicar_plano_de_edicao.resolver_caminho_do_node(), "/opt/homebrew/bin/node")
def test_informa_instalacao_ausente(self):
"""Levanta um erro explicativo quando nenhum executável existe."""
with patch.dict(os.environ, {}, clear=True), patch(
"engine.aplicar_plano_de_edicao.shutil.which", return_value=None
), patch("engine.aplicar_plano_de_edicao.os.path.isfile", return_value=False):
with self.assertRaisesRegex(FileNotFoundError, "Node.js não encontrado"):
aplicar_plano_de_edicao.resolver_caminho_do_node()
class TestePreValidacaoDoPlano(unittest.TestCase):
"""Garante que planos semanticamente perigosos não alcancem o Premiere."""
def test_interrompe_antes_de_resolver_node_e_criar_backup(self) -> None:
"""A validação semântica ocorre antes de abrir a sessão do editor."""
with tempfile.TemporaryDirectory() as pasta:
caminho_do_plano = Path(pasta) / "plano.json"
caminho_do_plano.write_text(
'{"source":"video.mp4","actions":['
'{"kind":"cut","start":1.0,"end":2.0,"reason":"teste"}]}',
encoding="utf-8",
)
with patch(
"engine.aplicar_plano_de_edicao._validar_integridade_semantica",
side_effect=ErroDePlanoInvalido("frase interrompida"),
) as validar, patch(
"engine.aplicar_plano_de_edicao.resolver_caminho_do_node"
) as resolver_node:
with self.assertRaisesRegex(ErroDePlanoInvalido, "frase interrompida"):
aplicar_plano_de_edicao.executar(caminho_do_plano, plano_id=7)
validar.assert_called_once()
resolver_node.assert_not_called()
def test_gancho_exige_backup_confirmado(self) -> None:
"""A operação composta não começa quando a recuperação não foi criada."""
plano = PlanoDeEdicao("video.mp4", (
AcaoDeEdicao(TipoDeAcao.GANCHO, 10.0, 15.0, "gancho"),
))
with self.assertRaisesRegex(ErroDePlanoInvalido, "não confirmou o backup"):
aplicar_plano_de_edicao._garantir_backup_para_ganchos(plano, False)
def test_corte_preserva_comportamento_legado_quando_backup_falha(self) -> None:
"""A exigência adicional fica restrita ao gancho estrutural."""
plano = PlanoDeEdicao("video.mp4", (
AcaoDeEdicao(TipoDeAcao.CORTE, 10.0, 15.0, "retirada"),
))
aplicar_plano_de_edicao._garantir_backup_para_ganchos(plano, False)
def test_painel_rejeita_corte_sem_plano_associado_ao_banco(self) -> None:
"""Não permite contornar a validação colando um corte sem transcrição vinculada."""
with tempfile.TemporaryDirectory() as pasta:
caminho_do_plano = Path(pasta) / "plano.json"
caminho_do_plano.write_text(
'{"source":"video.mp4","actions":['
'{"kind":"cut","start":1.0,"end":2.0,"reason":"teste"}]}',
encoding="utf-8",
)
with patch(
"engine.aplicar_plano_de_edicao.resolver_caminho_do_node"
) as resolver_node:
with self.assertRaisesRegex(ErroDePlanoInvalido, "Salve ou carregue"):
aplicar_plano_de_edicao.executar(
caminho_do_plano,
exigir_validacao_semantica=True,
)
resolver_node.assert_not_called()
if __name__ == "__main__":
unittest.main()