"""Tests E2E de l'endpoint AJAX ajax/load-more-videos.php. L'endpoint exige trois protections cumulées (dans l'ordre) : 1. l'en-tête X-Requested-With: XMLHttpRequest ; 2. une origine (Origin, ou Referer à défaut) strictement identique au site ; 3. un token CSRF stateless valide (champ POST csrf_token). """ import pytest ENDPOINT = "/ajax/load-more-videos.php" @pytest.fixture() def csrf_token(page, base_url): """Récupère un token CSRF frais depuis la balise meta de l'accueil.""" page.goto(base_url + "/index.php", wait_until="domcontentloaded") token = page.locator('meta[name="csrf-token"]').get_attribute("content") assert token, "La balise meta csrf-token est absente de l'accueil" return token def _post(page, base_url, query="", token=None, ajax=True, origin=None): """POST sur l'endpoint avec les protections activables une à une.""" headers = {} if ajax: headers["X-Requested-With"] = "XMLHttpRequest" if origin is not None: headers["Origin"] = origin form = {} if token is not None: form["csrf_token"] = token return page.request.post( base_url + ENDPOINT + query, form=form, headers=headers ) def test_load_more_rejects_non_ajax(page, base_url, csrf_token): """Sans X-Requested-With, la requête est refusée (403).""" rep = _post(page, base_url, "?type=recent&page=1", token=csrf_token, ajax=False, origin=base_url) assert rep.status == 403 assert rep.json() == {"error": "Accès non autorisé"} def test_load_more_rejects_bad_origin(page, base_url, csrf_token): """Une origine étrangère est refusée (403).""" rep = _post(page, base_url, "?type=recent&page=1", token=csrf_token, origin="https://example.com") assert rep.status == 403 assert rep.json() == {"error": "Origine non autorisée"} def test_load_more_rejects_bad_csrf(page, base_url): """Un token CSRF forgé est refusé (403).""" rep = _post(page, base_url, "?type=recent&page=1", token="1234567890:tokenforge", origin=base_url) assert rep.status == 403 assert rep.json() == {"error": "Token CSRF invalide"} def test_load_more_rejects_invalid_type(page, base_url, csrf_token): """Un type de vidéos inconnu est refusé (400).""" rep = _post(page, base_url, "?type=inconnu&page=1", token=csrf_token, origin=base_url) assert rep.status == 400 assert rep.json() == {"error": "Type de vidéos non valide"} def test_load_more_category_requires_id(page, base_url, csrf_token): """Le type 'category' sans ID de catégorie est refusé (400).""" rep = _post(page, base_url, "?type=category&page=1", token=csrf_token, origin=base_url) assert rep.status == 400 assert rep.json() == {"error": "ID de catégorie manquant ou invalide"} def test_load_more_recent_structure(page, base_url, csrf_token): """Une requête valide renvoie la structure JSON attendue.""" rep = _post(page, base_url, "?type=recent&page=1", token=csrf_token, origin=base_url) assert rep.status == 200 payload = rep.json() assert payload["success"] is True assert payload["page"] == 1 assert isinstance(payload["hasMore"], bool) assert isinstance(payload["html"], str) def test_load_more_recent_returns_videos(page, base_url, csrf_token): """Le HTML retourné contient des cartes vidéo (nécessite le réseau).""" rep = _post(page, base_url, "?type=recent&page=1", token=csrf_token, origin=base_url) assert rep.status == 200 payload = rep.json() if not payload["html"]: pytest.skip("Aucune vidéo reçue de l'API PeerTube (réseau indisponible ?)") assert 'class="video-card"' in payload["html"] assert "data-video-id=" in payload["html"] # L'endpoint n'a plus rien à servir bien au-delà du catalogue rep_fin = _post(page, base_url, "?type=recent&page=10000", token=csrf_token, origin=base_url) assert rep_fin.status == 200 assert rep_fin.json()["hasMore"] is False