Michael Hu
Create unit tests for domain layer
48f8a08
"""Unit tests for AudioChunk value object."""
import pytest
from src.domain.models.audio_chunk import AudioChunk
class TestAudioChunk:
"""Test cases for AudioChunk value object."""
def test_valid_audio_chunk_creation(self):
"""Test creating valid AudioChunk instance."""
chunk = AudioChunk(
data=b"fake_audio_chunk_data",
format="wav",
sample_rate=22050,
chunk_index=0,
is_final=False,
timestamp=1.5
)
assert chunk.data == b"fake_audio_chunk_data"
assert chunk.format == "wav"
assert chunk.sample_rate == 22050
assert chunk.chunk_index == 0
assert chunk.is_final is False
assert chunk.timestamp == 1.5
assert chunk.size_bytes == len(b"fake_audio_chunk_data")
def test_audio_chunk_with_defaults(self):
"""Test creating AudioChunk with default values."""
chunk = AudioChunk(
data=b"fake_audio_chunk_data",
format="wav",
sample_rate=22050,
chunk_index=0
)
assert chunk.is_final is False
assert chunk.timestamp is None
def test_final_chunk_creation(self):
"""Test creating final AudioChunk."""
chunk = AudioChunk(
data=b"final_chunk_data",
format="wav",
sample_rate=22050,
chunk_index=5,
is_final=True
)
assert chunk.is_final is True
assert chunk.chunk_index == 5
def test_non_bytes_data_raises_error(self):
"""Test that non-bytes data raises TypeError."""
with pytest.raises(TypeError, match="Audio data must be bytes"):
AudioChunk(
data="not_bytes", # type: ignore
format="wav",
sample_rate=22050,
chunk_index=0
)
def test_empty_data_raises_error(self):
"""Test that empty data raises ValueError."""
with pytest.raises(ValueError, match="Audio data cannot be empty"):
AudioChunk(
data=b"",
format="wav",
sample_rate=22050,
chunk_index=0
)
def test_unsupported_format_raises_error(self):
"""Test that unsupported format raises ValueError."""
with pytest.raises(ValueError, match="Unsupported audio format: xyz"):
AudioChunk(
data=b"fake_data",
format="xyz",
sample_rate=22050,
chunk_index=0
)
def test_supported_formats(self):
"""Test all supported audio formats."""
supported_formats = ['wav', 'mp3', 'flac', 'ogg', 'raw']
for fmt in supported_formats:
chunk = AudioChunk(
data=b"fake_data",
format=fmt,
sample_rate=22050,
chunk_index=0
)
assert chunk.format == fmt
def test_non_integer_sample_rate_raises_error(self):
"""Test that non-integer sample rate raises ValueError."""
with pytest.raises(ValueError, match="Sample rate must be a positive integer"):
AudioChunk(
data=b"fake_data",
format="wav",
sample_rate=22050.5, # type: ignore
chunk_index=0
)
def test_negative_sample_rate_raises_error(self):
"""Test that negative sample rate raises ValueError."""
with pytest.raises(ValueError, match="Sample rate must be a positive integer"):
AudioChunk(
data=b"fake_data",
format="wav",
sample_rate=-1,
chunk_index=0
)
def test_zero_sample_rate_raises_error(self):
"""Test that zero sample rate raises ValueError."""
with pytest.raises(ValueError, match="Sample rate must be a positive integer"):
AudioChunk(
data=b"fake_data",
format="wav",
sample_rate=0,
chunk_index=0
)
def test_non_integer_chunk_index_raises_error(self):
"""Test that non-integer chunk index raises ValueError."""
with pytest.raises(ValueError, match="Chunk index must be a non-negative integer"):
AudioChunk(
data=b"fake_data",
format="wav",
sample_rate=22050,
chunk_index=1.5 # type: ignore
)
def test_negative_chunk_index_raises_error(self):
"""Test that negative chunk index raises ValueError."""
with pytest.raises(ValueError, match="Chunk index must be a non-negative integer"):
AudioChunk(
data=b"fake_data",
format="wav",
sample_rate=22050,
chunk_index=-1
)
def test_valid_chunk_index_zero(self):
"""Test that chunk index of zero is valid."""
chunk = AudioChunk(
data=b"fake_data",
format="wav",
sample_rate=22050,
chunk_index=0
)
assert chunk.chunk_index == 0
def test_non_boolean_is_final_raises_error(self):
"""Test that non-boolean is_final raises TypeError."""
with pytest.raises(TypeError, match="is_final must be a boolean"):
AudioChunk(
data=b"fake_data",
format="wav",
sample_rate=22050,
chunk_index=0,
is_final="true" # type: ignore
)
def test_non_numeric_timestamp_raises_error(self):
"""Test that non-numeric timestamp raises ValueError."""
with pytest.raises(ValueError, match="Timestamp must be a non-negative number"):
AudioChunk(
data=b"fake_data",
format="wav",
sample_rate=22050,
chunk_index=0,
timestamp="1.5" # type: ignore
)
def test_negative_timestamp_raises_error(self):
"""Test that negative timestamp raises ValueError."""
with pytest.raises(ValueError, match="Timestamp must be a non-negative number"):
AudioChunk(
data=b"fake_data",
format="wav",
sample_rate=22050,
chunk_index=0,
timestamp=-1.0
)
def test_valid_timestamp_zero(self):
"""Test that timestamp of zero is valid."""
chunk = AudioChunk(
data=b"fake_data",
format="wav",
sample_rate=22050,
chunk_index=0,
timestamp=0.0
)
assert chunk.timestamp == 0.0
def test_valid_timestamp_values(self):
"""Test valid timestamp values."""
valid_timestamps = [0.0, 1.5, 10, 100.123]
for timestamp in valid_timestamps:
chunk = AudioChunk(
data=b"fake_data",
format="wav",
sample_rate=22050,
chunk_index=0,
timestamp=timestamp
)
assert chunk.timestamp == timestamp
def test_size_bytes_property(self):
"""Test size_bytes property returns correct value."""
test_data = b"test_audio_chunk_data_123"
chunk = AudioChunk(
data=test_data,
format="wav",
sample_rate=22050,
chunk_index=0
)
assert chunk.size_bytes == len(test_data)
def test_duration_estimate_property(self):
"""Test duration_estimate property calculation."""
# Create chunk with known data size
test_data = b"x" * 44100 # 44100 bytes
chunk = AudioChunk(
data=test_data,
format="wav",
sample_rate=22050, # 22050 samples per second
chunk_index=0
)
# Expected duration: 44100 bytes / (22050 samples/sec * 2 bytes/sample) = 1.0 second
expected_duration = 44100 / (22050 * 2)
assert abs(chunk.duration_estimate - expected_duration) < 0.01
def test_duration_estimate_with_zero_sample_rate(self):
"""Test duration_estimate with edge case of zero calculation."""
# This shouldn't happen due to validation, but test the property logic
chunk = AudioChunk(
data=b"test_data",
format="wav",
sample_rate=22050,
chunk_index=0
)
# Should return a reasonable estimate
assert chunk.duration_estimate >= 0
def test_audio_chunk_is_immutable(self):
"""Test that AudioChunk is immutable (frozen dataclass)."""
chunk = AudioChunk(
data=b"fake_data",
format="wav",
sample_rate=22050,
chunk_index=0
)
with pytest.raises(AttributeError):
chunk.format = "mp3" # type: ignore
def test_chunk_sequence_ordering(self):
"""Test that chunks can be ordered by chunk_index."""
chunks = [
AudioChunk(data=b"chunk2", format="wav", sample_rate=22050, chunk_index=2),
AudioChunk(data=b"chunk0", format="wav", sample_rate=22050, chunk_index=0),
AudioChunk(data=b"chunk1", format="wav", sample_rate=22050, chunk_index=1),
]
# Sort by chunk_index
sorted_chunks = sorted(chunks, key=lambda c: c.chunk_index)
assert sorted_chunks[0].chunk_index == 0
assert sorted_chunks[1].chunk_index == 1
assert sorted_chunks[2].chunk_index == 2
def test_streaming_scenario(self):
"""Test typical streaming scenario with multiple chunks."""
# First chunk
chunk1 = AudioChunk(
data=b"first_chunk_data",
format="wav",
sample_rate=22050,
chunk_index=0,
is_final=False,
timestamp=0.0
)
# Middle chunk
chunk2 = AudioChunk(
data=b"middle_chunk_data",
format="wav",
sample_rate=22050,
chunk_index=1,
is_final=False,
timestamp=1.0
)
# Final chunk
chunk3 = AudioChunk(
data=b"final_chunk_data",
format="wav",
sample_rate=22050,
chunk_index=2,
is_final=True,
timestamp=2.0
)
assert not chunk1.is_final
assert not chunk2.is_final
assert chunk3.is_final
# Verify ordering
chunks = [chunk1, chunk2, chunk3]
for i, chunk in enumerate(chunks):
assert chunk.chunk_index == i
assert chunk.timestamp == float(i)