145 lines
4.5 KiB
Python
145 lines
4.5 KiB
Python
"""
|
|
Tests for Pipeline — end-to-end orchestration, stats, error handling.
|
|
|
|
Demonstrates: TDD (Interview Topic 8) — integration testing with mocked FFmpeg probe.
|
|
"""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from core.chunker import Pipeline
|
|
from core.chunker.exceptions import PipelineError
|
|
|
|
|
|
def mock_probe(duration):
|
|
"""Create a mock ProbeResult with the given duration."""
|
|
result = MagicMock()
|
|
result.duration = duration
|
|
return result
|
|
|
|
|
|
class TestPipeline:
|
|
@patch("core.chunker.chunker.probe_file")
|
|
def test_end_to_end(self, mock_pf, temp_file):
|
|
"""Full pipeline processes a file successfully."""
|
|
path = temp_file(b"x" * 4096)
|
|
mock_pf.return_value = mock_probe(40.0)
|
|
|
|
result = Pipeline(
|
|
source=path,
|
|
chunk_duration=10.0,
|
|
num_workers=2,
|
|
processor_type="checksum",
|
|
).run()
|
|
|
|
assert result.total_chunks == 4
|
|
assert result.processed == 4
|
|
assert result.failed == 0
|
|
assert result.elapsed_time > 0
|
|
assert result.chunks_in_order is True
|
|
|
|
@patch("core.chunker.chunker.probe_file")
|
|
def test_throughput_calculated(self, mock_pf, temp_file):
|
|
"""Pipeline calculates throughput."""
|
|
path = temp_file(b"x" * 10000)
|
|
mock_pf.return_value = mock_probe(30.0)
|
|
|
|
result = Pipeline(source=path, chunk_duration=10.0, num_workers=2).run()
|
|
|
|
assert result.throughput_mbps > 0
|
|
|
|
@patch("core.chunker.chunker.probe_file")
|
|
def test_worker_stats(self, mock_pf, temp_file):
|
|
"""Pipeline reports per-worker stats."""
|
|
path = temp_file(b"x" * 4000)
|
|
mock_pf.return_value = mock_probe(40.0)
|
|
|
|
result = Pipeline(
|
|
source=path, chunk_duration=10.0, num_workers=2
|
|
).run()
|
|
|
|
assert len(result.worker_stats) == 2
|
|
for worker_id, stats in result.worker_stats.items():
|
|
assert "processed" in stats
|
|
assert "errors" in stats
|
|
|
|
def test_nonexistent_file(self):
|
|
"""Non-existent file raises PipelineError."""
|
|
with pytest.raises(PipelineError):
|
|
Pipeline(source="/nonexistent/file.mp4").run()
|
|
|
|
@patch("core.chunker.chunker.probe_file")
|
|
def test_event_callback(self, mock_pf, temp_file):
|
|
"""Pipeline emits events through callback."""
|
|
path = temp_file(b"x" * 2048)
|
|
mock_pf.return_value = mock_probe(20.0)
|
|
events = []
|
|
|
|
def capture(event_type, data):
|
|
events.append(event_type)
|
|
|
|
Pipeline(
|
|
source=path,
|
|
chunk_duration=10.0,
|
|
num_workers=1,
|
|
event_callback=capture,
|
|
).run()
|
|
|
|
assert "pipeline_start" in events
|
|
assert "pipeline_complete" in events
|
|
assert "chunk_queued" in events
|
|
|
|
@patch("core.chunker.chunker.probe_file")
|
|
def test_simulated_decode_processor(self, mock_pf, temp_file):
|
|
"""Pipeline works with simulated_decode processor."""
|
|
path = temp_file(b"x" * 2048)
|
|
mock_pf.return_value = mock_probe(20.0)
|
|
|
|
result = Pipeline(
|
|
source=path,
|
|
chunk_duration=10.0,
|
|
num_workers=2,
|
|
processor_type="simulated_decode",
|
|
).run()
|
|
|
|
assert result.total_chunks == 2
|
|
assert result.failed == 0
|
|
|
|
@patch("core.chunker.chunker.probe_file")
|
|
def test_single_chunk_file(self, mock_pf, temp_file):
|
|
"""Duration shorter than chunk_duration produces one chunk."""
|
|
path = temp_file(b"x" * 100)
|
|
mock_pf.return_value = mock_probe(5.0)
|
|
|
|
result = Pipeline(source=path, chunk_duration=10.0).run()
|
|
|
|
assert result.total_chunks == 1
|
|
assert result.processed == 1
|
|
|
|
@patch("core.chunker.chunker.probe_file")
|
|
def test_retries_tracked(self, mock_pf, temp_file):
|
|
"""Pipeline result tracks total retries."""
|
|
path = temp_file(b"x" * 2048)
|
|
mock_pf.return_value = mock_probe(20.0)
|
|
|
|
result = Pipeline(source=path, chunk_duration=10.0).run()
|
|
|
|
assert result.retries >= 0 # Might be 0 if no failures
|
|
|
|
@patch("core.chunker.chunker.probe_file")
|
|
def test_output_dir_and_chunk_files(self, mock_pf, temp_file):
|
|
"""Pipeline tracks output_dir and chunk_files when set."""
|
|
path = temp_file(b"x" * 1024)
|
|
mock_pf.return_value = mock_probe(10.0)
|
|
|
|
result = Pipeline(
|
|
source=path,
|
|
chunk_duration=10.0,
|
|
processor_type="checksum",
|
|
).run()
|
|
|
|
# No output_dir set, so chunk_files should be empty
|
|
assert result.output_dir is None
|
|
assert result.chunk_files == []
|