mirror of
https://github.com/safishamsi/graphify.git
synced 2026-07-13 10:57:13 +00:00
699e9960ce
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
148 lines
5.0 KiB
Python
148 lines
5.0 KiB
Python
"""Tests for graphify.transcribe — video/audio transcription support."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from graphify.transcribe import (
|
|
VIDEO_EXTENSIONS,
|
|
build_whisper_prompt,
|
|
transcribe,
|
|
transcribe_all,
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# VIDEO_EXTENSIONS
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_video_extensions_set():
|
|
assert ".mp4" in VIDEO_EXTENSIONS
|
|
assert ".mp3" in VIDEO_EXTENSIONS
|
|
assert ".wav" in VIDEO_EXTENSIONS
|
|
assert ".mov" in VIDEO_EXTENSIONS
|
|
assert ".py" not in VIDEO_EXTENSIONS
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# build_whisper_prompt
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_build_whisper_prompt_no_nodes():
|
|
"""Empty god_nodes returns fallback prompt."""
|
|
prompt = build_whisper_prompt([])
|
|
assert "punctuation" in prompt.lower() or len(prompt) > 0
|
|
|
|
|
|
def test_build_whisper_prompt_env_override(monkeypatch):
|
|
"""GRAPHIFY_WHISPER_PROMPT env var short-circuits LLM call."""
|
|
monkeypatch.setenv("GRAPHIFY_WHISPER_PROMPT", "Custom domain hint.")
|
|
prompt = build_whisper_prompt([{"label": "Python"}, {"label": "FastAPI"}])
|
|
assert prompt == "Custom domain hint."
|
|
|
|
|
|
def test_build_whisper_prompt_returns_topic_string():
|
|
"""Returns a topic-based prompt from god node labels — no LLM call."""
|
|
god_nodes = [{"label": "neural networks"}, {"label": "transformers"}, {"label": "attention"}]
|
|
with patch.dict(os.environ, {}, clear=False):
|
|
os.environ.pop("GRAPHIFY_WHISPER_PROMPT", None)
|
|
prompt = build_whisper_prompt(god_nodes)
|
|
assert "neural networks" in prompt.lower() or "transformers" in prompt.lower()
|
|
assert "punctuation" in prompt.lower()
|
|
|
|
|
|
def test_build_whisper_prompt_nodes_without_labels():
|
|
"""Nodes missing 'label' keys are safely skipped."""
|
|
god_nodes = [{"id": "1"}, {"id": "2", "label": ""}]
|
|
prompt = build_whisper_prompt(god_nodes)
|
|
assert len(prompt) > 0
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# transcribe
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_transcribe_uses_cache(tmp_path):
|
|
"""If transcript already exists, transcribe() returns cached path without running Whisper."""
|
|
video = tmp_path / "lecture.mp4"
|
|
video.write_bytes(b"fake")
|
|
out_dir = tmp_path / "transcripts"
|
|
out_dir.mkdir()
|
|
cached = out_dir / "lecture.txt"
|
|
cached.write_text("Cached transcript content.")
|
|
|
|
result = transcribe(video, output_dir=out_dir)
|
|
assert result == cached
|
|
|
|
|
|
def test_transcribe_force_reruns(tmp_path):
|
|
"""force=True re-transcribes even when cache exists."""
|
|
video = tmp_path / "talk.mp4"
|
|
video.write_bytes(b"fake")
|
|
out_dir = tmp_path / "transcripts"
|
|
out_dir.mkdir()
|
|
(out_dir / "talk.txt").write_text("Old transcript.")
|
|
|
|
fake_segment = MagicMock()
|
|
fake_segment.text = "New transcript segment."
|
|
fake_info = MagicMock()
|
|
fake_info.language = "en"
|
|
|
|
fake_model = MagicMock()
|
|
fake_model.transcribe.return_value = ([fake_segment], fake_info)
|
|
|
|
with patch("graphify.transcribe._get_whisper", return_value=lambda *a, **kw: fake_model):
|
|
result = transcribe(video, output_dir=out_dir, force=True)
|
|
|
|
assert result.read_text() == "New transcript segment."
|
|
|
|
|
|
def test_transcribe_missing_faster_whisper(tmp_path):
|
|
"""ImportError propagates when faster_whisper is not installed."""
|
|
video = tmp_path / "clip.mp4"
|
|
video.write_bytes(b"fake")
|
|
|
|
with patch("graphify.transcribe._get_whisper", side_effect=ImportError("faster-whisper not installed")):
|
|
with pytest.raises(ImportError):
|
|
transcribe(video, output_dir=tmp_path / "out")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# transcribe_all
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def test_transcribe_all_empty():
|
|
"""Empty input returns empty list without error."""
|
|
assert transcribe_all([]) == []
|
|
|
|
|
|
def test_transcribe_all_uses_cache(tmp_path):
|
|
"""transcribe_all() returns cached paths for already-transcribed files."""
|
|
video = tmp_path / "lecture.mp4"
|
|
video.write_bytes(b"fake")
|
|
out_dir = tmp_path / "transcripts"
|
|
out_dir.mkdir()
|
|
cached = out_dir / "lecture.txt"
|
|
cached.write_text("Cached.")
|
|
|
|
results = transcribe_all([str(video)], output_dir=out_dir)
|
|
assert len(results) == 1
|
|
assert str(cached) in results[0]
|
|
|
|
|
|
def test_transcribe_all_skips_failed(tmp_path):
|
|
"""transcribe_all() warns and skips files that fail to transcribe."""
|
|
video = tmp_path / "broken.mp4"
|
|
video.write_bytes(b"fake")
|
|
|
|
def raise_import(*args, **kwargs):
|
|
raise ImportError("faster_whisper not installed")
|
|
|
|
with patch("graphify.transcribe.transcribe", side_effect=RuntimeError("boom")):
|
|
results = transcribe_all([str(video)], output_dir=tmp_path / "out")
|
|
|
|
assert results == []
|