Files
JMR-devandClaude Sonnet 5 b23995f218 refactor: rename the package from ccn-transcribe to audio-scribe
The distribution, console script and import package are now audio-scribe /
audio_scribe (src/audio_scribe). Everything named for the old project follows:

- CcnError -> AudioScribeError, and its code "ccn_error" -> "audio_scribe_error"
  (the code is never persisted, so existing job state still loads)
- CCN_LIVE -> AUDIO_SCRIBE_LIVE for the live-GPU tests
- OpenVINO kernel cache moves to <cache>/audio-scribe/ov_cache; the first run
  after upgrading recompiles kernels, and the old directory is left in place
- README, build-binary.sh, hatch/coverage config and uv.lock updated to match

Breaking: the command is now `audio-scribe`; reinstall any tool install of the
old name with `uv tool uninstall ccn-transcribe && uv tool install .`.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-19 15:52:42 -05:00

86 lines
2.6 KiB
Python

from __future__ import annotations
import pytest
from audio_scribe import errors
def test_base_carries_message_and_hint() -> None:
err = errors.AudioScribeError("something broke", hint="try this")
assert str(err) == "something broke"
assert err.hint == "try this"
assert err.exit_code == errors.EXIT_JOB_FAILED
def test_hint_defaults_to_none() -> None:
assert errors.AudioScribeError("bare").hint is None
@pytest.mark.parametrize(
"cls",
[errors.ConfigError, errors.PreflightError, errors.DiskFullError, errors.SchemaTooNewError],
)
def test_fatal_errors_abort_the_run(cls: type[errors.AudioScribeError]) -> None:
err = cls("nope")
assert err.exit_code == errors.EXIT_FATAL
assert isinstance(err, errors.FatalError)
# A fatal error must never be mistaken for a per-job failure.
assert not isinstance(err, errors.JobError)
def test_job_error_carries_job_id() -> None:
err = errors.JobError("failed", job_id="youtube-abc123")
assert err.job_id == "youtube-abc123"
assert err.exit_code == errors.EXIT_JOB_FAILED
def test_job_error_job_id_optional() -> None:
assert errors.JobError("failed").job_id is None
@pytest.mark.parametrize(
"cls",
[
errors.GeoBlockedError,
errors.PrivateVideoError,
errors.AgeGatedError,
errors.MembersOnlyError,
errors.NotFoundError,
errors.BotCheckError,
errors.NetworkError,
],
)
def test_source_errors_are_job_errors(cls: type[errors.SourceError]) -> None:
assert issubclass(cls, errors.SourceError)
assert issubclass(cls, errors.JobError)
def test_only_network_errors_are_retryable() -> None:
assert errors.NetworkError("flaky").retryable is True
assert errors.PrivateVideoError("nope").retryable is False
assert errors.NotFoundError("gone").retryable is False
def test_every_error_class_declares_a_distinct_code() -> None:
classes = [
v
for v in vars(errors).values()
if isinstance(v, type) and issubclass(v, errors.AudioScribeError)
]
codes = [c.code for c in classes]
assert len(codes) == len(set(codes)), "duplicate .code values"
def test_aria2_error_carries_process_exit_code() -> None:
err = errors.Aria2Error(
"aria2c failed", aria2_exit_code=5, job_id="youtube-x", hint="slow link"
)
assert err.aria2_exit_code == 5
assert err.job_id == "youtube-x"
assert err.hint == "slow link"
assert isinstance(err, errors.DownloadError)
def test_aria2_error_exit_code_optional() -> None:
assert errors.Aria2Error("boom").aria2_exit_code is None