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