""" Tests for ChunkQueue — backpressure, sentinel shutdown, timeout behavior. Demonstrates: TDD (Interview Topic 8) — concurrency testing. """ import queue import threading import pytest from core.chunker.queue import ChunkQueue class TestChunkQueue: def test_put_and_get(self, make_chunk): """Basic put/get cycle.""" q = ChunkQueue(maxsize=5) chunk = make_chunk(0) q.put(chunk) result = q.get(timeout=1.0) assert result.sequence == 0 def test_fifo_order(self, make_chunk): """Items come out in FIFO order.""" q = ChunkQueue(maxsize=5) for i in range(3): q.put(make_chunk(i)) for i in range(3): assert q.get(timeout=1.0).sequence == i def test_close_returns_none(self, make_chunk): """After close(), get() returns None (sentinel).""" q = ChunkQueue(maxsize=5) q.put(make_chunk(0)) q.close() result = q.get(timeout=1.0) assert result.sequence == 0 # Next get should hit sentinel result = q.get(timeout=1.0) assert result is None def test_close_propagates_to_multiple_consumers(self, make_chunk): """Sentinel propagates: multiple consumers all get None.""" q = ChunkQueue(maxsize=5) q.close() # Multiple consumers should all see None assert q.get(timeout=1.0) is None assert q.get(timeout=1.0) is None def test_is_closed(self): """is_closed reflects state.""" q = ChunkQueue() assert not q.is_closed q.close() assert q.is_closed def test_qsize(self, make_chunk): """qsize tracks approximate queue depth.""" q = ChunkQueue(maxsize=10) assert q.qsize() == 0 q.put(make_chunk(0)) q.put(make_chunk(1)) assert q.qsize() == 2 q.get(timeout=1.0) assert q.qsize() == 1 def test_backpressure_blocks(self, make_chunk): """Put blocks when queue is full (backpressure).""" q = ChunkQueue(maxsize=2) q.put(make_chunk(0)) q.put(make_chunk(1)) # Queue is full — put with short timeout should raise with pytest.raises(queue.Full): q.put(make_chunk(2), timeout=0.05) def test_get_timeout(self): """Get on empty queue with timeout raises Empty.""" q = ChunkQueue(maxsize=5) with pytest.raises(queue.Empty): q.get(timeout=0.05) def test_concurrent_put_get(self, make_chunk): """Producer/consumer threads work correctly.""" q = ChunkQueue(maxsize=3) results = [] def producer(): for i in range(10): q.put(make_chunk(i)) q.close() def consumer(): while True: item = q.get(timeout=2.0) if item is None: break results.append(item.sequence) t1 = threading.Thread(target=producer) t2 = threading.Thread(target=consumer) t1.start() t2.start() t1.join(timeout=5.0) t2.join(timeout=5.0) assert sorted(results) == list(range(10))