Problem
The transcription/audio_utils.py module (321 lines) lacks a dedicated test file. This module provides the core AudioSegmentExtractor class for efficient audio segment extraction, which is used throughout the pipeline but only tested indirectly.
Analysis
Current State
- Source file:
transcription/audio_utils.py (~321 LOC)
- Dedicated tests: None (
test_audio_utils.py does not exist)
- Indirect coverage: Partial via
test_pipeline.py, test_audio_enrichment.py
Key Functionality Requiring Tests
-
AudioSegmentExtractor class (lines 14-257):
-
Constructor (lines 28-57):
- File existence validation
- File type validation (must be a file, not directory)
- Audio metadata extraction (sample_rate, total_frames, channels, duration)
- RuntimeError on open failure
-
extract_segment() (lines 59-168):
- Time-to-frame conversion
- Start/end timestamp validation
- Negative timestamp handling (with/without clamp)
- Out-of-bounds timestamp handling (with/without clamp)
- Minimum duration enforcement
- Frame clamping to valid range
- Multi-channel → mono conversion
- Memory-efficient seeking
-
extract_segment_by_frames() (lines 170-223):
- Frame index validation
- Clamping behavior
- Multi-channel handling
-
get_duration() (line 225-232):
-
get_info() (lines 234-248):
- Metadata dictionary construction
-
repr() (lines 250-257):
-
load_full_audio() (lines 260-292):
- Full file loading
- File not found handling
- Multi-channel → mono conversion
- RuntimeError on read failure
-
validate_wav_file() (lines 295-320):
- Existence check
- File type check
- Readability validation
- Returns bool (no exceptions)
Critical Edge Cases Not Currently Tested
- Non-existent file → FileNotFoundError
- Directory path → ValueError
- Corrupted/invalid audio file → RuntimeError
- Negative timestamps with
clamp=True vs clamp=False
- Timestamps beyond file duration
min_duration enforcement
- Very short segments (< 1 frame)
- Zero-duration segments (
start == end)
- Reverse timestamps (
start > end)
- Multi-channel audio handling
- Frame-based extraction edge cases
- Large file seeking performance
Recommended Solution
Create tests/test_audio_utils.py with the following structure:
# Suggested test structure
import pytest
import numpy as np
from pathlib import Path
import soundfile as sf
from transcription.audio_utils import (
AudioSegmentExtractor,
load_full_audio,
validate_wav_file,
)
class TestAudioSegmentExtractorInit:
"""Tests for AudioSegmentExtractor initialization."""
def test_init_valid_wav(self, sample_wav): ...
def test_init_file_not_found(self, tmp_path): ...
def test_init_directory_path(self, tmp_path): ...
def test_init_invalid_audio(self, tmp_path): ...
def test_init_extracts_metadata(self, sample_wav): ...
class TestExtractSegment:
"""Tests for extract_segment() method."""
def test_extract_valid_segment(self, extractor): ...
def test_extract_full_duration(self, extractor): ...
def test_extract_start_equals_end(self, extractor): ...
def test_extract_reverse_timestamps_raises(self, extractor): ...
# Negative timestamp tests
def test_negative_start_clamped(self, extractor): ...
def test_negative_start_raises_without_clamp(self, extractor): ...
def test_negative_end_clamped(self, extractor): ...
def test_negative_end_raises_without_clamp(self, extractor): ...
# Out-of-bounds tests
def test_start_beyond_duration_clamped(self, extractor): ...
def test_start_beyond_duration_raises(self, extractor): ...
def test_end_beyond_duration_clamped(self, extractor): ...
def test_end_beyond_duration_raises(self, extractor): ...
# Minimum duration tests
def test_min_duration_satisfied(self, extractor): ...
def test_min_duration_violated_raises(self, extractor): ...
# Multi-channel tests
def test_stereo_to_mono(self, stereo_extractor): ...
# Frame boundary tests
def test_very_short_segment(self, extractor): ...
def test_returns_float32(self, extractor): ...
def test_returns_correct_sample_rate(self, extractor): ...
class TestExtractSegmentByFrames:
"""Tests for extract_segment_by_frames() method."""
def test_extract_valid_frame_range(self, extractor): ...
def test_reverse_frames_raises(self, extractor): ...
def test_negative_start_clamped(self, extractor): ...
def test_negative_start_raises(self, extractor): ...
def test_end_beyond_total_clamped(self, extractor): ...
def test_end_beyond_total_raises(self, extractor): ...
def test_invalid_range_raises(self, extractor): ...
class TestAccessors:
"""Tests for accessor methods."""
def test_get_duration(self, extractor): ...
def test_get_info_structure(self, extractor): ...
def test_get_info_values(self, extractor): ...
def test_repr(self, extractor): ...
class TestLoadFullAudio:
"""Tests for load_full_audio() function."""
def test_load_valid_wav(self, sample_wav): ...
def test_load_file_not_found(self, tmp_path): ...
def test_load_returns_float32(self, sample_wav): ...
def test_load_stereo_to_mono(self, stereo_wav): ...
class TestValidateWavFile:
"""Tests for validate_wav_file() function."""
def test_valid_wav_returns_true(self, sample_wav): ...
def test_nonexistent_returns_false(self, tmp_path): ...
def test_directory_returns_false(self, tmp_path): ...
def test_invalid_audio_returns_false(self, tmp_path): ...
def test_empty_file_returns_false(self, tmp_path): ...
# Fixtures
@pytest.fixture
def sample_wav(tmp_path):
"""Create a mono 16kHz WAV file."""
wav_path = tmp_path / "sample.wav"
audio = np.random.randn(16000).astype(np.float32) # 1 second
sf.write(str(wav_path), audio, 16000)
return wav_path
@pytest.fixture
def stereo_wav(tmp_path):
"""Create a stereo WAV file."""
wav_path = tmp_path / "stereo.wav"
audio = np.random.randn(16000, 2).astype(np.float32)
sf.write(str(wav_path), audio, 16000)
return wav_path
@pytest.fixture
def extractor(sample_wav):
"""Create extractor for sample WAV."""
return AudioSegmentExtractor(sample_wav)
@pytest.fixture
def stereo_extractor(stereo_wav):
"""Create extractor for stereo WAV."""
return AudioSegmentExtractor(stereo_wav)
Testing Strategy
- Create test WAV files using soundfile in fixtures
- Test boundary conditions exhaustively (clamp vs raise)
- Verify audio data with numpy assertions
- Test multi-channel handling with stereo fixtures
- Test metadata extraction accuracy
Acceptance Criteria
Labels
Problem
The
transcription/audio_utils.pymodule (321 lines) lacks a dedicated test file. This module provides the coreAudioSegmentExtractorclass for efficient audio segment extraction, which is used throughout the pipeline but only tested indirectly.Analysis
Current State
transcription/audio_utils.py(~321 LOC)test_audio_utils.pydoes not exist)test_pipeline.py,test_audio_enrichment.pyKey Functionality Requiring Tests
AudioSegmentExtractor class (lines 14-257):
Constructor (lines 28-57):
extract_segment() (lines 59-168):
extract_segment_by_frames() (lines 170-223):
get_duration() (line 225-232):
get_info() (lines 234-248):
repr() (lines 250-257):
load_full_audio() (lines 260-292):
validate_wav_file() (lines 295-320):
Critical Edge Cases Not Currently Tested
clamp=Truevsclamp=Falsemin_durationenforcementstart == end)start > end)Recommended Solution
Create
tests/test_audio_utils.pywith the following structure:Testing Strategy
Acceptance Criteria
tests/test_audio_utils.pyexists with 35+ test functionsLabels
testenhancementaudio