feat: add asset pool with severity tagging and rotation
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
from pathlib import Path
|
||||
|
||||
from server.asset_pool import AssetPool
|
||||
|
||||
|
||||
def _make_pool(tmp_path: Path, max_images: int = 10, max_audio: int = 5) -> AssetPool:
|
||||
return AssetPool(
|
||||
base_dir=str(tmp_path),
|
||||
max_images=max_images,
|
||||
max_audio=max_audio,
|
||||
)
|
||||
|
||||
|
||||
def _fake_image(tmp_path: Path, pool: AssetPool, severity: float) -> str:
|
||||
content = b"fake png data"
|
||||
return pool.add_image(content, severity=severity)
|
||||
|
||||
|
||||
def _fake_audio(tmp_path: Path, pool: AssetPool, severity: float) -> str:
|
||||
content = b"fake wav data"
|
||||
return pool.add_audio(content, severity=severity)
|
||||
|
||||
|
||||
class TestAssetPoolInit:
|
||||
def test_creates_directories(self, tmp_path):
|
||||
"""Pool creates img/ and audio/ subdirectories."""
|
||||
pool = _make_pool(tmp_path)
|
||||
assert (tmp_path / "img").is_dir()
|
||||
assert (tmp_path / "audio").is_dir()
|
||||
|
||||
def test_empty_pool(self, tmp_path):
|
||||
"""New pool has no assets."""
|
||||
pool = _make_pool(tmp_path)
|
||||
assert pool.image_count == 0
|
||||
assert pool.audio_count == 0
|
||||
|
||||
|
||||
class TestAddAssets:
|
||||
def test_add_image(self, tmp_path):
|
||||
"""Adding an image increments count and returns a URL path."""
|
||||
pool = _make_pool(tmp_path)
|
||||
url = _fake_image(tmp_path, pool, severity=1.0)
|
||||
assert pool.image_count == 1
|
||||
assert url.startswith("/assets/img/")
|
||||
assert url.endswith(".png")
|
||||
|
||||
def test_add_audio(self, tmp_path):
|
||||
"""Adding audio increments count and returns a URL path."""
|
||||
pool = _make_pool(tmp_path)
|
||||
url = _fake_audio(tmp_path, pool, severity=1.0)
|
||||
assert pool.audio_count == 1
|
||||
assert url.startswith("/assets/audio/")
|
||||
assert url.endswith(".wav")
|
||||
|
||||
def test_file_exists_on_disk(self, tmp_path):
|
||||
"""Added assets exist as real files."""
|
||||
pool = _make_pool(tmp_path)
|
||||
url = _fake_image(tmp_path, pool, severity=1.0)
|
||||
filename = url.split("/")[-1]
|
||||
assert (tmp_path / "img" / filename).exists()
|
||||
|
||||
|
||||
class TestSelectAssets:
|
||||
def test_select_image_by_severity(self, tmp_path):
|
||||
"""Selects an image closest to target severity."""
|
||||
pool = _make_pool(tmp_path)
|
||||
_fake_image(tmp_path, pool, severity=0.5)
|
||||
_fake_image(tmp_path, pool, severity=2.0)
|
||||
_fake_image(tmp_path, pool, severity=4.0)
|
||||
url = pool.select_image(target_severity=1.8)
|
||||
assert url is not None
|
||||
|
||||
def test_select_audio_by_severity(self, tmp_path):
|
||||
"""Selects an audio clip closest to target severity."""
|
||||
pool = _make_pool(tmp_path)
|
||||
_fake_audio(tmp_path, pool, severity=0.5)
|
||||
_fake_audio(tmp_path, pool, severity=3.0)
|
||||
url = pool.select_audio(target_severity=2.5)
|
||||
assert url is not None
|
||||
|
||||
def test_select_from_empty_returns_none(self, tmp_path):
|
||||
"""Selecting from empty pool returns None."""
|
||||
pool = _make_pool(tmp_path)
|
||||
assert pool.select_image(target_severity=1.0) is None
|
||||
assert pool.select_audio(target_severity=1.0) is None
|
||||
|
||||
|
||||
class TestRotation:
|
||||
def test_image_rotation(self, tmp_path):
|
||||
"""Oldest images are removed when pool exceeds max."""
|
||||
pool = _make_pool(tmp_path, max_images=3)
|
||||
for i in range(5):
|
||||
_fake_image(tmp_path, pool, severity=float(i))
|
||||
assert pool.image_count == 3
|
||||
|
||||
def test_audio_rotation(self, tmp_path):
|
||||
"""Oldest audio clips are removed when pool exceeds max."""
|
||||
pool = _make_pool(tmp_path, max_audio=2)
|
||||
for i in range(4):
|
||||
_fake_audio(tmp_path, pool, severity=float(i))
|
||||
assert pool.audio_count == 2
|
||||
|
||||
|
||||
class TestStatus:
|
||||
def test_status_dict(self, tmp_path):
|
||||
"""Status returns pool sizes."""
|
||||
pool = _make_pool(tmp_path)
|
||||
_fake_image(tmp_path, pool, severity=1.0)
|
||||
_fake_audio(tmp_path, pool, severity=1.0)
|
||||
status = pool.get_status()
|
||||
assert status["image_pool_size"] == 1
|
||||
assert status["audio_pool_size"] == 1
|
||||
Reference in New Issue
Block a user