Files
jhonny-editor/code/engine/testes/test_aplicar_plano_de_edicao.py
T
João Henrique 9edde2df37 feat: aprimorada a edição por voz com validação de frases, margens
- aprimorada a edição por voz com validação de frases, margens seguras e backup único antes da timeline.
- adicionada consulta expansível dos detalhes de cada trilha diretamente do banco

Resumo:
- 23 arquivos alterados
- 5 novos
- 18 modificados
- 0 removidos

 18 files changed, 321 insertions(+), 216 deletions(-)

Arquivos:
  - .gitignore
  - .jhonny/analises.db
  - code/cep-plugin/index.html
  - code/cep-plugin/main.js
  - code/cep-plugin/styles.css
  - code/engine/analisar_trilhas.py
  - code/engine/aplicar_plano_de_edicao.py
  - code/engine/editor/__init__.py
  - code/engine/editor/backup_de_sequencia.py
  - code/engine/persistencia/__init__.py
  - code/engine/testes/test_aplicar_plano_de_edicao.py
  - code/plugins/premiere-pro/skills/edit-video-by-voice/SKILL.md
  - code/plugins/premiere-pro/skills/editar-por-voz/SKILL.md
  - code/plugins/premiere-pro/skills/editar-por-voz/criterios/05-zoom.md
  - code/plugins/premiere-pro/skills/editar-por-voz/criterios/06-texto-corte-marcador.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/plugins/premiere-pro/skills/transcript-to-edit-actions/SKILL.md
  - code/engine/consultar_detalhes_de_trilha.py
  - code/engine/editor/validacao_semantica.py
  - code/engine/persistencia/leitura_semantica_do_plano.py
  - code/engine/testes/test_leitura_semantica_do_plano.py
  - code/engine/testes/test_validador_semantico_de_plano.py
2026-09-10 14:38:42 -04:00

88 lines
4.1 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 ErroDePlanoInvalido
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_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()