"""Routes HTTP du support de formation (routes/document/) — création, édition directe (sans page intermédiaire), CRUD des éléments en AJAX, publication, et isolation par propriétaire (core/auth_guard.py).""" from typing import Any from flask.testing import FlaskClient import auth import db def test_documents_new_creates_a_support_not_a_game(client: FlaskClient) -> None: resp = client.post("/documents/new", data={"name": "Sécurité incendie"}, follow_redirects=False) assert resp.status_code == 302 assert "/document/" in resp.headers["Location"] assert "/edit" in resp.headers["Location"] slug = resp.headers["Location"].rstrip("/").split("/")[-2] try: assert db.support_meta(slug)["name"] == "Sécurité incendie" finally: db.delete_support(slug) def test_document_edit_renders_directly_no_intermediate_page(client: FlaskClient, support: str) -> None: resp = client.get(f"/document/{support}/edit") assert resp.status_code == 200 def test_document_element_add_returns_rendered_html(client: FlaskClient, support: str) -> None: resp = client.post(f"/document/{support}/elements/add", data={"kind": "titre"}) assert resp.status_code == 200 payload = resp.get_json() assert payload["kind"] == "titre" assert "docText" in payload["rendered_html"] def test_document_element_add_rejects_unknown_kind(client: FlaskClient, support: str) -> None: assert client.post(f"/document/{support}/elements/add", data={"kind": "n-importe-quoi"}).status_code == 400 def test_document_element_add_accepts_row_for_the_layout_engine(client: FlaskClient, support: str) -> None: """ "row" n'apparaît jamais dans la bibliothèque (voir document_engine.ELEMENT_LIBRARY) mais reste un kind valide pour cette route — c'est le moteur de layout côté client qui en crée une à la volée au moment d'un dépôt latéral (voir static/document/js/document-editor.js).""" resp = client.post(f"/document/{support}/elements/add", data={"kind": "row"}) assert resp.status_code == 200 assert resp.get_json()["kind"] == "row" def test_document_element_update_persists_attributes(client: FlaskClient, support: str) -> None: add_resp = client.post(f"/document/{support}/elements/add", data={"kind": "paragraphe"}) element_id = add_resp.get_json()["id"] resp = client.post( f"/document/{support}/elements/{element_id}/update", json={"content": "Texte mis à jour", "style": "legende"}, ) assert resp.status_code == 200 assert resp.get_json()["ok"] is True reread = client.post(f"/document/{support}/elements/add", data={"kind": "titre"}) assert reread.status_code == 200 def test_document_element_move_and_delete(client: FlaskClient, support: str) -> None: first_id = client.post(f"/document/{support}/elements/add", data={"kind": "titre"}).get_json()["id"] second_id = client.post(f"/document/{support}/elements/add", data={"kind": "paragraphe"}).get_json()["id"] move_resp = client.post( f"/document/{support}/elements/{second_id}/move", json={"new_parent_id": None, "new_index": 0}, ) assert move_resp.status_code == 200 assert move_resp.get_json()["ok"] is True delete_resp = client.post(f"/document/{support}/elements/{first_id}/delete") assert delete_resp.status_code == 200 assert delete_resp.get_json()["ok"] is True def test_restricted_user_can_have_one_game_and_one_support_at_once( user_client: FlaskClient, tmp_game_slug_cleanup: Any, tmp_support_slug_cleanup: Any ) -> None: with user_client.session_transaction() as sess: user_id = sess["user_id"] game_slug = db.create_game("Un environnement 2D", owner_folder=str(user_id)) tmp_game_slug_cleanup(game_slug) auth.set_project_slug(user_id, game_slug) resp = user_client.post("/documents/new", data={"name": "Un support"}, follow_redirects=False) assert resp.status_code == 302 support_slug = resp.headers["Location"].rstrip("/").split("/")[-2] tmp_support_slug_cleanup(support_slug) user = auth.get_user_by_id(user_id) assert user is not None assert user["project_slug"] == game_slug assert user["support_slug"] == support_slug def test_cannot_open_another_owners_support(client: FlaskClient, user_client: FlaskClient) -> None: """Isolation par propriétaire (core/auth_guard.py, split_slug déjà générique quel que soit le type de projet) — le compte admin (`client`) ne doit pas pouvoir ouvrir le support d'un autre compte.""" with user_client.session_transaction() as sess: victim_user_id = sess["user_id"] victim_slug = db.create_support("Support de la victime", owner_folder=str(victim_user_id)) try: assert client.get(f"/document/{victim_slug}/edit").status_code == 403 assert client.post(f"/document/{victim_slug}/delete").status_code == 403 finally: db.delete_support(victim_slug)