Add QueueRepositoryInterface with memory and file-backed adapters.
Provide a generic disk-backed FIFO queue with configurable path, retention, and dedup keys so consumers can buffer items across restarts without optional extras. Co-authored-by: Cursor <[email protected]>
This commit is contained in:
co-authored by
Cursor
parent
1babb52d09
commit
a768474892
@@ -0,0 +1,100 @@
|
||||
"""Unit tests for QueueRepositoryInterface."""
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
|
||||
import pytest
|
||||
|
||||
from python_repositories.interfaces.queue_repository_interface import (
|
||||
QueueRepositoryInterface,
|
||||
)
|
||||
|
||||
|
||||
class InMemoryQueueRepo:
|
||||
"""Plain class that satisfies QueueRepositoryInterface without inheritance."""
|
||||
|
||||
def enqueue(self, items: list[dict[str, Any]]) -> None:
|
||||
pass
|
||||
|
||||
def dequeue_batch(self, *, max_items: int = 1000) -> list[dict[str, Any]]:
|
||||
del max_items
|
||||
return []
|
||||
|
||||
def size(self) -> int:
|
||||
return 0
|
||||
|
||||
def evict_older_than(self, cutoff: datetime) -> int:
|
||||
del cutoff
|
||||
return 0
|
||||
|
||||
|
||||
def accepts_queue_repo(repo: QueueRepositoryInterface) -> None:
|
||||
"""Type-checking hook for QueueRepositoryInterface structural subtyping."""
|
||||
repo.size()
|
||||
|
||||
|
||||
def test_instantiation_fails_when_enqueue_not_implemented() -> None:
|
||||
class Incomplete(QueueRepositoryInterface):
|
||||
def dequeue_batch(self, *, max_items: int = 1000) -> list[dict[str, Any]]:
|
||||
return []
|
||||
|
||||
def size(self) -> int:
|
||||
return 0
|
||||
|
||||
def evict_older_than(self, cutoff: datetime) -> int:
|
||||
return 0
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
_ = Incomplete() # type: ignore
|
||||
|
||||
|
||||
def test_instantiation_fails_when_dequeue_batch_not_implemented() -> None:
|
||||
class Incomplete(QueueRepositoryInterface):
|
||||
def enqueue(self, items: list[dict[str, Any]]) -> None:
|
||||
pass
|
||||
|
||||
def size(self) -> int:
|
||||
return 0
|
||||
|
||||
def evict_older_than(self, cutoff: datetime) -> int:
|
||||
return 0
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
_ = Incomplete() # type: ignore
|
||||
|
||||
|
||||
def test_instantiation_fails_when_size_not_implemented() -> None:
|
||||
class Incomplete(QueueRepositoryInterface):
|
||||
def enqueue(self, items: list[dict[str, Any]]) -> None:
|
||||
pass
|
||||
|
||||
def dequeue_batch(self, *, max_items: int = 1000) -> list[dict[str, Any]]:
|
||||
return []
|
||||
|
||||
def evict_older_than(self, cutoff: datetime) -> int:
|
||||
return 0
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
_ = Incomplete() # type: ignore
|
||||
|
||||
|
||||
def test_instantiation_fails_when_evict_older_than_not_implemented() -> None:
|
||||
class Incomplete(QueueRepositoryInterface):
|
||||
def enqueue(self, items: list[dict[str, Any]]) -> None:
|
||||
pass
|
||||
|
||||
def dequeue_batch(self, *, max_items: int = 1000) -> list[dict[str, Any]]:
|
||||
return []
|
||||
|
||||
def size(self) -> int:
|
||||
return 0
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
_ = Incomplete() # type: ignore
|
||||
|
||||
|
||||
def test_structural_subtyping() -> None:
|
||||
repo: QueueRepositoryInterface = InMemoryQueueRepo()
|
||||
accepts_queue_repo(repo)
|
||||
assert isinstance(repo, QueueRepositoryInterface)
|
||||
assert repo.evict_older_than(datetime.now(UTC)) == 0
|
||||
Reference in New Issue
Block a user