Files
ohm_streaming/tests/test_downloads.py
T
Roman 41566ab5fb v0.1.0 — Réécriture complète de OhmStreaming
Nouvelle version réécrite de zéro : recherche multi-sources (Vostfree,
French-Manga), extraction 2 niveaux, proxy vidéo intégré, streaming/téléchargement
HLS, métadonnées Kitsu, bibliothèque locale, comptes JWT + administration,
découverte fusionnée, indexeur Torznab (Sonarr/Prowlarr).
2026-09-22 10:05:47 +00:00

105 lines
3.6 KiB
Python

"""Tests du gestionnaire de téléchargements (serveur HTTP local avec Range)."""
import asyncio
import pytest
from aiohttp import web
from app.config import get_settings
from app.services.downloads import download_manager as dm
PAYLOAD = b"x" * 500_000
async def _start_file_server() -> tuple[web.AppRunner, int]:
"""Mini serveur HTTP supportant les requêtes Range."""
async def handle(request: web.Request) -> web.StreamResponse:
range_header = request.headers.get("Range")
if range_header:
start = int(range_header.removeprefix("bytes=").split("-")[0])
return web.Response(
body=PAYLOAD[start:],
status=206,
headers={"Content-Range": f"bytes {start}-{len(PAYLOAD) - 1}/{len(PAYLOAD)}"},
)
return web.Response(body=PAYLOAD, headers={"Content-Length": str(len(PAYLOAD))})
app = web.Application()
app.router.add_get("/video.mp4", handle)
runner = web.AppRunner(app)
await runner.setup()
site = web.TCPSite(runner, "127.0.0.1", 0)
await site.start()
port = site._server.sockets[0].getsockname()[1]
return runner, port
@pytest.fixture
async def file_server():
runner, port = await _start_file_server()
yield f"http://127.0.0.1:{port}"
await runner.cleanup()
@pytest.fixture
async def manager():
await dm.start()
yield dm
await dm.stop()
async def _wait_status(download_id: int, wanted: set[str], timeout: float = 10) -> dict:
for _ in range(int(timeout * 10)):
data = await dm.get(download_id)
if data["status"] in wanted:
return data
await asyncio.sleep(0.1)
raise AssertionError(f"statut {wanted} non atteint (actuel: {data['status']})")
async def test_full_download(file_server, manager):
d = await dm.enqueue(f"{file_server}/video.mp4", "http://page", "Episode Test 1")
data = await _wait_status(d["id"], {"done", "failed"})
assert data["status"] == "done", data.get("error")
assert data["percent"] == 100.0
path = get_settings().download_dir / data["file_path"]
assert path.read_bytes() == PAYLOAD
async def test_anti_duplicate(file_server, manager):
d1 = await dm.enqueue(f"{file_server}/video.mp4", "http://page", "Ep")
d2 = await dm.enqueue(f"{file_server}/video.mp4", "http://page", "Ep")
assert d1["id"] == d2["id"]
assert d2["duplicate"] is True
await _wait_status(d1["id"], {"done", "failed"})
async def test_pause_resume(file_server, manager):
d = await dm.enqueue(f"{file_server}/video.mp4", "http://page", "Ep Pause")
await asyncio.sleep(0.2)
await dm.pause(d["id"])
data = await _wait_status(d["id"], {"paused", "done"})
if data["status"] == "paused": # assez rapide pour être pausé
assert await dm.resume(d["id"]) is True
data = await _wait_status(d["id"], {"done", "failed"})
assert data["status"] == "done", data.get("error")
path = get_settings().download_dir / data["file_path"]
assert path.read_bytes() == PAYLOAD
async def test_failed_download_and_retry(manager):
d = await dm.enqueue("http://127.0.0.1:1/nope.mp4", "http://page", "Ep KO")
data = await _wait_status(d["id"], {"failed"})
assert data["error"]
assert await dm.retry(d["id"]) is True
data = await _wait_status(d["id"], {"failed"})
assert await dm.cancel(d["id"]) is True
async def test_cancel(file_server, manager):
d = await dm.enqueue(f"{file_server}/video.mp4", "http://page", "Ep Cancel")
await dm.cancel(d["id"])
data = await dm.get(d["id"])
assert data["status"] in ("cancelled", "done") # course possible si déjà fini