mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 21:07:03 +01:00
transcribe_helpers.py is 289 lines against the 250-line cap and was completely untested. Tests land first, in their own commit, so the split that follows is verified against tests that predate the seam. Left at its current path rather than moved under python_pkg/: transcribe.sh invokes it by path inside a dedicated ML venv, and transcribe_fw.py imports two siblings by bare name, so the closure is five files (1236 lines), not three. Tests go in linux_configuration/tests/ instead, whose conftest already puts standalone script directories on sys.path - the same arrangement the usage_report work used, and still collected by CI. The ML dependencies are never installed in CI and the module reaches them only through _try_import, so patching that one seam covers every probe in both directions. An AST scan at every nesting level confirmed there is no other path to them: the only direct numpy imports sit under TYPE_CHECKING. 41 tests, 100% line and branch. The last gap was the dispatch guard at line 284, unreachable through the CLI because argparse's choices list and the dispatch dict carry the same eight commands; covering it by admitting a ninth choice also documents why the guard is there. Split across four test files because a single module hit 291 lines - over the cap this work exists to clear. Verified: python-version prints 3.14, check-faster-whisper exits 7 with no ML deps present, generate-wav writes a 96044-byte WAV; 243 tests pass in the directory, up from 202 by exactly the 41 added. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
224 lines
7.3 KiB
Python
224 lines
7.3 KiB
Python
"""Tests for the transcribe_helpers command-line dispatch.
|
|
|
|
transcribe.sh calls this module as a subcommand runner and branches on the
|
|
exit status, so the codes are the contract: 7 means "faster_whisper missing",
|
|
2 means "required argument missing", 1 means "the operation failed".
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
import sys
|
|
from typing import TYPE_CHECKING
|
|
|
|
import pytest
|
|
import transcribe_helpers as helpers
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
|
|
_EXIT_MISSING_ARG = 2
|
|
_EXIT_NO_FASTER_WHISPER = 7
|
|
|
|
|
|
def _run(monkeypatch: pytest.MonkeyPatch, *argv: str) -> None:
|
|
"""Invoke main() with the given command line."""
|
|
monkeypatch.setattr(sys, "argv", ["transcribe_helpers.py", *argv])
|
|
helpers.main()
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# commands that report through the exit status
|
|
# --------------------------------------------------------------------------- #
|
|
def test_python_version_prints_and_exits_cleanly(
|
|
monkeypatch: pytest.MonkeyPatch, caplog: pytest.LogCaptureFixture
|
|
) -> None:
|
|
"""transcribe.sh parses this to decide which wheels to install."""
|
|
with caplog.at_level(logging.INFO):
|
|
_run(monkeypatch, "python-version")
|
|
|
|
assert helpers.get_python_version() in caplog.text
|
|
|
|
|
|
def test_check_faster_whisper_exits_7_when_absent(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""Exit 7 is the offline-mode signal transcribe.sh branches on."""
|
|
monkeypatch.setattr(helpers, "check_faster_whisper", lambda: False)
|
|
|
|
with pytest.raises(SystemExit) as excinfo:
|
|
_run(monkeypatch, "check-faster-whisper")
|
|
|
|
assert excinfo.value.code == _EXIT_NO_FASTER_WHISPER
|
|
|
|
|
|
def test_check_faster_whisper_is_silent_when_present(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""A satisfied dependency exits 0, so the shell continues."""
|
|
monkeypatch.setattr(helpers, "check_faster_whisper", lambda: True)
|
|
|
|
_run(monkeypatch, "check-faster-whisper")
|
|
|
|
|
|
def test_check_diarization_never_fails_the_run(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""Missing diarization deps only warn: speaker labels are optional."""
|
|
monkeypatch.setattr(helpers, "check_diarization_deps", lambda: False)
|
|
|
|
_run(monkeypatch, "check-diarization")
|
|
|
|
|
|
@pytest.mark.parametrize(("available", "raises"), [(True, False), (False, True)])
|
|
def test_check_ctranslate2_exits_1_when_absent(
|
|
monkeypatch: pytest.MonkeyPatch, *, available: bool, raises: bool
|
|
) -> None:
|
|
"""ctranslate2 is required, so its absence is a hard failure."""
|
|
monkeypatch.setattr(helpers, "check_ctranslate2", lambda: available)
|
|
|
|
if raises:
|
|
with pytest.raises(SystemExit) as excinfo:
|
|
_run(monkeypatch, "check-ctranslate2")
|
|
assert excinfo.value.code == 1
|
|
else:
|
|
_run(monkeypatch, "check-ctranslate2")
|
|
|
|
|
|
def test_deps_installed_reports_success(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""The confirmation command exits 0."""
|
|
_run(monkeypatch, "deps-installed")
|
|
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# commands taking arguments
|
|
# --------------------------------------------------------------------------- #
|
|
def test_generate_wav_writes_the_requested_file(
|
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
) -> None:
|
|
"""The --file argument is passed through to the generator."""
|
|
out = tmp_path / "tone.wav"
|
|
|
|
_run(monkeypatch, "generate-wav", "--file", str(out))
|
|
|
|
assert out.exists()
|
|
|
|
|
|
def test_generate_wav_requires_a_file(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""Omitting --file is a usage error, exit 2."""
|
|
with pytest.raises(SystemExit) as excinfo:
|
|
_run(monkeypatch, "generate-wav")
|
|
|
|
assert excinfo.value.code == _EXIT_MISSING_ARG
|
|
|
|
|
|
def test_generate_wav_exits_1_when_generation_fails(
|
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
) -> None:
|
|
"""A generator failure surfaces as exit 1."""
|
|
monkeypatch.setattr(helpers, "generate_sine_wav", lambda _f: False)
|
|
|
|
with pytest.raises(SystemExit) as excinfo:
|
|
_run(monkeypatch, "generate-wav", "--file", str(tmp_path / "x.wav"))
|
|
|
|
assert excinfo.value.code == 1
|
|
|
|
|
|
def test_prepare_model_passes_both_arguments(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""--model and --model-dir reach prepare_model unchanged."""
|
|
seen: list[tuple[str, str]] = []
|
|
|
|
def _record(model: str, model_dir: str) -> bool:
|
|
seen.append((model, model_dir))
|
|
return True
|
|
|
|
monkeypatch.setattr(helpers, "prepare_model", _record)
|
|
|
|
_run(monkeypatch, "prepare-model", "--model", "small", "--model-dir", "/m")
|
|
|
|
assert seen == [("small", "/m")]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"argv",
|
|
[
|
|
("prepare-model",),
|
|
("prepare-model", "--model", "small"),
|
|
("prepare-model", "--model-dir", "/m"),
|
|
],
|
|
)
|
|
def test_prepare_model_requires_both_arguments(
|
|
monkeypatch: pytest.MonkeyPatch, argv: tuple[str, ...]
|
|
) -> None:
|
|
"""Either argument missing is a usage error, exit 2."""
|
|
with pytest.raises(SystemExit) as excinfo:
|
|
_run(monkeypatch, *argv)
|
|
|
|
assert excinfo.value.code == _EXIT_MISSING_ARG
|
|
|
|
|
|
def test_prepare_model_exits_1_on_download_failure(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""A failed download surfaces as exit 1."""
|
|
monkeypatch.setattr(helpers, "prepare_model", lambda _m, _d: False)
|
|
|
|
with pytest.raises(SystemExit) as excinfo:
|
|
_run(monkeypatch, "prepare-model", "--model", "small", "--model-dir", "/m")
|
|
|
|
assert excinfo.value.code == 1
|
|
|
|
|
|
@pytest.mark.parametrize(("works", "raises"), [(True, False), (False, True)])
|
|
def test_test_cuda_reports_through_the_exit_status(
|
|
monkeypatch: pytest.MonkeyPatch, *, works: bool, raises: bool
|
|
) -> None:
|
|
"""transcribe.sh falls back to CPU when this exits non-zero."""
|
|
monkeypatch.setattr(helpers, "test_cuda", lambda: works)
|
|
|
|
if raises:
|
|
with pytest.raises(SystemExit) as excinfo:
|
|
_run(monkeypatch, "test-cuda")
|
|
assert excinfo.value.code == 1
|
|
else:
|
|
_run(monkeypatch, "test-cuda")
|
|
|
|
|
|
def test_an_unknown_command_is_rejected_by_argparse(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""The choices list means a typo cannot silently do nothing."""
|
|
with pytest.raises(SystemExit) as excinfo:
|
|
_run(monkeypatch, "not-a-command")
|
|
|
|
assert excinfo.value.code == _EXIT_MISSING_ARG
|
|
|
|
|
|
def test_a_command_with_no_handler_is_a_no_op(
|
|
monkeypatch: pytest.MonkeyPatch,
|
|
) -> None:
|
|
"""The dispatch guard tolerates a choice with no handler behind it.
|
|
|
|
Unreachable through the CLI today, because argparse's ``choices`` list and
|
|
the dispatch dict carry the same eight commands. It is exercised here by
|
|
accepting a ninth choice, so that adding a command to one list but not the
|
|
other degrades to doing nothing rather than raising.
|
|
"""
|
|
real_parse_args = helpers.argparse.ArgumentParser.parse_args
|
|
|
|
def _parse_with_extra_choice(
|
|
self: helpers.argparse.ArgumentParser, *args: object, **kwargs: object
|
|
) -> helpers.argparse.Namespace:
|
|
for action in self._actions:
|
|
if action.dest == "command" and action.choices is not None:
|
|
action.choices = [*action.choices, "unhandled"]
|
|
return real_parse_args(self, *args, **kwargs)
|
|
|
|
monkeypatch.setattr(
|
|
helpers.argparse.ArgumentParser, "parse_args", _parse_with_extra_choice
|
|
)
|
|
|
|
_run(monkeypatch, "unhandled")
|