chunker and ui
This commit is contained in:
127
tests/chunker/test_worker.py
Normal file
127
tests/chunker/test_worker.py
Normal file
@@ -0,0 +1,127 @@
|
||||
"""
|
||||
Tests for Worker — processing, retry with backoff, error handling.
|
||||
|
||||
Demonstrates: TDD (Interview Topic 8) — mocking processors, testing retry logic.
|
||||
"""
|
||||
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from core.chunker.models import Chunk, ChunkResult
|
||||
from core.chunker.processor import Processor
|
||||
from core.chunker.queue import ChunkQueue
|
||||
from core.chunker.worker import Worker
|
||||
|
||||
|
||||
class FailNTimesProcessor(Processor):
|
||||
"""Test processor that fails N times then succeeds."""
|
||||
|
||||
def __init__(self, fail_count: int):
|
||||
self.fail_count = fail_count
|
||||
self.call_count = 0
|
||||
|
||||
def process(self, chunk: Chunk) -> ChunkResult:
|
||||
self.call_count += 1
|
||||
if self.call_count <= self.fail_count:
|
||||
raise RuntimeError(f"Simulated failure #{self.call_count}")
|
||||
return ChunkResult(
|
||||
sequence=chunk.sequence,
|
||||
success=True,
|
||||
processing_time=0.001,
|
||||
)
|
||||
|
||||
|
||||
class AlwaysFailProcessor(Processor):
|
||||
"""Test processor that always fails."""
|
||||
|
||||
def process(self, chunk: Chunk) -> ChunkResult:
|
||||
raise RuntimeError("Always fails")
|
||||
|
||||
|
||||
class TestWorker:
|
||||
def test_processes_chunks(self, make_chunk):
|
||||
"""Worker processes all chunks from queue."""
|
||||
q = ChunkQueue(maxsize=5)
|
||||
for i in range(3):
|
||||
q.put(make_chunk(i))
|
||||
q.close()
|
||||
|
||||
from core.chunker.processor import ChecksumProcessor
|
||||
worker = Worker("w-0", q, ChecksumProcessor(), max_retries=0)
|
||||
results = worker.run()
|
||||
|
||||
assert len(results) == 3
|
||||
assert all(r.success for r in results)
|
||||
|
||||
def test_retry_on_failure(self, make_chunk):
|
||||
"""Worker retries on processor failure."""
|
||||
q = ChunkQueue(maxsize=5)
|
||||
q.put(make_chunk(0))
|
||||
q.close()
|
||||
|
||||
proc = FailNTimesProcessor(fail_count=2)
|
||||
worker = Worker("w-0", q, proc, max_retries=3)
|
||||
results = worker.run()
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0].success is True
|
||||
assert results[0].retries == 2
|
||||
assert proc.call_count == 3 # 2 failures + 1 success
|
||||
|
||||
def test_max_retries_exceeded(self, make_chunk):
|
||||
"""Worker gives up after max retries."""
|
||||
q = ChunkQueue(maxsize=5)
|
||||
q.put(make_chunk(0))
|
||||
q.close()
|
||||
|
||||
worker = Worker("w-0", q, AlwaysFailProcessor(), max_retries=2)
|
||||
results = worker.run()
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0].success is False
|
||||
assert results[0].error is not None
|
||||
assert worker.error_count == 1
|
||||
|
||||
def test_worker_id_on_results(self, make_chunk):
|
||||
"""Worker stamps its ID on results."""
|
||||
q = ChunkQueue(maxsize=5)
|
||||
q.put(make_chunk(0))
|
||||
q.close()
|
||||
|
||||
from core.chunker.processor import ChecksumProcessor
|
||||
worker = Worker("worker-7", q, ChecksumProcessor())
|
||||
results = worker.run()
|
||||
|
||||
assert results[0].worker_id == "worker-7"
|
||||
|
||||
def test_event_callback(self, make_chunk):
|
||||
"""Worker emits events via callback."""
|
||||
q = ChunkQueue(maxsize=5)
|
||||
q.put(make_chunk(0))
|
||||
q.close()
|
||||
|
||||
events = []
|
||||
callback = MagicMock(side_effect=lambda t, d: events.append((t, d)))
|
||||
|
||||
from core.chunker.processor import ChecksumProcessor
|
||||
worker = Worker("w-0", q, ChecksumProcessor(), event_callback=callback)
|
||||
worker.run()
|
||||
|
||||
event_types = [e[0] for e in events]
|
||||
assert "worker_status" in event_types
|
||||
assert "chunk_processing" in event_types
|
||||
assert "chunk_done" in event_types
|
||||
|
||||
def test_processed_count(self, make_chunk):
|
||||
"""Worker tracks processed count."""
|
||||
q = ChunkQueue(maxsize=10)
|
||||
for i in range(5):
|
||||
q.put(make_chunk(i))
|
||||
q.close()
|
||||
|
||||
from core.chunker.processor import ChecksumProcessor
|
||||
worker = Worker("w-0", q, ChecksumProcessor())
|
||||
worker.run()
|
||||
|
||||
assert worker.processed_count == 5
|
||||
Reference in New Issue
Block a user