- 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
88 lines
4.1 KiB
Python
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()
|