99 lines
3.3 KiB
Python
99 lines
3.3 KiB
Python
"""
|
|
Tests for Processor implementations — ChecksumProcessor, SimulatedDecodeProcessor, CompositeProcessor.
|
|
|
|
Demonstrates: TDD (Interview Topic 8) — ABC contract, parametrized tests.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from core.chunker.exceptions import ChunkChecksumError
|
|
from core.chunker.models import Chunk
|
|
from core.chunker.processor import (
|
|
ChecksumProcessor,
|
|
CompositeProcessor,
|
|
Processor,
|
|
SimulatedDecodeProcessor,
|
|
)
|
|
|
|
|
|
class TestChecksumProcessor:
|
|
def test_valid_time_range(self, sample_chunk):
|
|
"""Valid time range passes."""
|
|
proc = ChecksumProcessor()
|
|
result = proc.process(sample_chunk)
|
|
assert result.success is True
|
|
assert result.checksum_valid is True
|
|
assert result.processing_time > 0
|
|
|
|
def test_invalid_time_range(self):
|
|
"""Invalid time range raises ChunkChecksumError."""
|
|
chunk = Chunk(
|
|
sequence=0,
|
|
start_time=10.0,
|
|
end_time=10.0, # zero duration
|
|
source_path="/fake.mp4",
|
|
duration=0.0,
|
|
)
|
|
proc = ChecksumProcessor()
|
|
with pytest.raises(ChunkChecksumError) as exc_info:
|
|
proc.process(chunk)
|
|
assert exc_info.value.sequence == 0
|
|
|
|
def test_sequence_preserved(self, make_chunk):
|
|
"""Result carries the chunk's sequence number."""
|
|
chunk = make_chunk(42)
|
|
proc = ChecksumProcessor()
|
|
result = proc.process(chunk)
|
|
assert result.sequence == 42
|
|
|
|
|
|
class TestSimulatedDecodeProcessor:
|
|
def test_processes_successfully(self, sample_chunk):
|
|
"""Simulated decode always succeeds."""
|
|
proc = SimulatedDecodeProcessor(ms_per_second=1.0)
|
|
result = proc.process(sample_chunk)
|
|
assert result.success is True
|
|
assert result.processing_time > 0
|
|
|
|
def test_time_proportional_to_duration(self):
|
|
"""Longer chunks take longer."""
|
|
short = Chunk(0, 0.0, 1.0, "/fake.mp4", 1.0)
|
|
long = Chunk(1, 0.0, 10.0, "/fake.mp4", 10.0)
|
|
|
|
proc = SimulatedDecodeProcessor(ms_per_second=50.0)
|
|
r_short = proc.process(short)
|
|
r_long = proc.process(long)
|
|
|
|
assert r_long.processing_time > r_short.processing_time
|
|
|
|
|
|
class TestCompositeProcessor:
|
|
def test_chains_processors(self, sample_chunk):
|
|
"""Composite runs all processors in sequence."""
|
|
proc = CompositeProcessor([
|
|
ChecksumProcessor(),
|
|
SimulatedDecodeProcessor(ms_per_second=1.0),
|
|
])
|
|
result = proc.process(sample_chunk)
|
|
assert result.success is True
|
|
|
|
def test_stops_on_failure(self):
|
|
"""If first processor raises, composite propagates the error."""
|
|
bad_chunk = Chunk(0, 10.0, 10.0, "/fake.mp4", 0.0) # invalid range
|
|
proc = CompositeProcessor([
|
|
ChecksumProcessor(),
|
|
SimulatedDecodeProcessor(ms_per_second=1.0),
|
|
])
|
|
with pytest.raises(ChunkChecksumError):
|
|
proc.process(bad_chunk)
|
|
|
|
def test_requires_at_least_one(self):
|
|
"""Empty processor list raises ValueError."""
|
|
with pytest.raises(ValueError, match="at least one"):
|
|
CompositeProcessor([])
|
|
|
|
def test_is_processor(self):
|
|
"""CompositeProcessor is a Processor."""
|
|
proc = CompositeProcessor([ChecksumProcessor()])
|
|
assert isinstance(proc, Processor)
|