"""Éditeur de quêtes (voir db/quests/, routes/quests/, onglet "🗺️ Quêtes" de l'éditeur de scène 2D, templates/scene_edit.html) : id, titre, objectif, récompense (score), statut (nouvelle/en_cours/terminee), résultat (reussi/echoue), et un dialogue par statut (3 colonnes — voir static/js/quests/ quest-editor.js et sanitize_quest_dialogues). Une quête vit pour TOUT le jeu, jamais par écran (mêmes conventions que les événements personnalisés, db/custom_events/).""" import db def _make_game(tmp_game_slug_cleanup, name="pytest_quests"): return tmp_game_slug_cleanup(db.create_game(name)) def test_create_quest_has_sensible_defaults(tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) quest = db.create_quest(slug, "Trouver la clé") assert quest["title"] == "Trouver la clé" assert quest["objectif"] == "" assert quest["recompense_score"] == 0 assert quest["status"] == "nouvelle" assert quest["resultat"] is None assert quest["dialogues"] == {} def test_create_quest_falls_back_to_default_title_when_blank(tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) quest = db.create_quest(slug, " ") assert quest["title"] == "Nouvelle quête" def test_list_quests_orders_most_recent_first(tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) first = db.create_quest(slug, "Première") second = db.create_quest(slug, "Seconde") ids = [q["id"] for q in db.list_quests(slug)] assert ids[:2] == [second["id"], first["id"]] def test_update_quest_only_applies_known_valid_fields(tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) quest = db.create_quest(slug, "Trouver la clé") updated = db.update_quest(slug, quest["id"], { "title": "Trouver la clé dorée", "objectif": "Fouiller le donjon", "recompense_score": 50, "status": "en_cours", "resultat": "reussi", "unknown_field": "ignoré", "id": 999999, }) assert updated["title"] == "Trouver la clé dorée" assert updated["objectif"] == "Fouiller le donjon" assert updated["recompense_score"] == 50 assert updated["status"] == "en_cours" assert updated["resultat"] == "reussi" assert updated["id"] == quest["id"] # "id" dans fields est ignoré, jamais modifiable def test_update_quest_rejects_invalid_status_and_blank_title(tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) quest = db.create_quest(slug, "Trouver la clé") updated = db.update_quest(slug, quest["id"], {"status": "en_pause", "title": " "}) assert updated["status"] == "nouvelle" # inchangé, valeur invalide ignorée assert updated["title"] == "Trouver la clé" # inchangé, titre vide ignoré def test_update_quest_returns_none_for_missing_quest(tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) assert db.update_quest(slug, 999999, {"title": "x"}) is None def test_delete_quest_removes_it(tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) quest = db.create_quest(slug, "Trouver la clé") db.delete_quest(slug, quest["id"]) assert db.get_quest(slug, quest["id"]) is None def test_sanitize_quest_dialogues_keeps_only_valid_lines_per_status(): """speaker est un nom de PERSONNAGE libre (voir "ℹ️ Informations", screens/rendering/scene_object_names.py) — "Le Sorcier" est donc un nom valide comme n'importe quel autre, seul un speaker/texte VIDE ou un élément mal formé est rejeté.""" raw = { "nouvelle": [{"speaker": "Le Sorcier", "text": "Bonjour !"}, {"speaker": "Joueur", "text": " "}], "en_cours": [{"speaker": " ", "text": "sans nom"}, {"speaker": 42, "text": "nom non textuel"}, "pas un objet"], "terminee": "pas une liste", "statut_inconnu": [{"speaker": "Le Sorcier", "text": "ignoré"}], } cleaned = db.sanitize_quest_dialogues(raw) assert cleaned == { "nouvelle": [{"speaker": "Le Sorcier", "text": "Bonjour !"}], "en_cours": [], "terminee": [], } def test_set_quest_dialogues_persists_all_three_columns(tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) quest = db.create_quest(slug, "Trouver la clé") dialogues = { "nouvelle": [{"speaker": "pnj", "text": "Peux-tu m'aider ?"}, {"speaker": "joueur", "text": "Bien sûr !"}], "en_cours": [{"speaker": "pnj", "text": "Alors, cette clé ?"}], "terminee": [{"speaker": "pnj", "text": "Merci beaucoup !"}], } db.set_quest_dialogues(slug, quest["id"], dialogues) reloaded = db.get_quest(slug, quest["id"]) assert reloaded["dialogues"] == dialogues def test_set_quest_dialogues_returns_none_for_missing_quest(tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) assert db.set_quest_dialogues(slug, 999999, {}) is None # ---------- Routes ---------- def test_quest_new_route_creates_and_returns_json(client, tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) resp = client.post(f"/game/{slug}/quests/new", data='{"title": "Nouvelle quête du donjon"}', content_type="application/json") assert resp.status_code == 200 quest = resp.get_json()["quest"] assert quest["title"] == "Nouvelle quête du donjon" assert db.get_quest(slug, quest["id"]) is not None def test_quests_list_json_route_returns_all_quests(client, tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) db.create_quest(slug, "A") db.create_quest(slug, "B") resp = client.get(f"/game/{slug}/quests/list.json") titles = {q["title"] for q in resp.get_json()["quests"]} assert titles == {"A", "B"} def test_quest_update_route_persists_fields(client, tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) quest = db.create_quest(slug, "Trouver la clé") resp = client.post(f"/game/{slug}/quests/{quest['id']}", data='{"recompense_score": 100, "status": "terminee"}', content_type="application/json") assert resp.get_json()["quest"]["recompense_score"] == 100 assert resp.get_json()["quest"]["status"] == "terminee" def test_quest_update_route_404s_for_missing_quest(client, game): resp = client.post(f"/game/{game}/quests/999999", data="{}", content_type="application/json") assert resp.status_code == 404 def test_quest_delete_route_removes_it(client, tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) quest = db.create_quest(slug, "Trouver la clé") resp = client.post(f"/game/{slug}/quests/{quest['id']}/delete") assert resp.get_json() == {"ok": True} assert db.get_quest(slug, quest["id"]) is None def test_quest_dialogues_route_sanitizes_and_persists(client, tmp_game_slug_cleanup): slug = _make_game(tmp_game_slug_cleanup) quest = db.create_quest(slug, "Trouver la clé") resp = client.post( f"/game/{slug}/quests/{quest['id']}/dialogues", data='{"dialogues": {"nouvelle": [{"speaker": "pnj", "text": "Salut !"}], "en_cours": [], "terminee": []}}', content_type="application/json", ) assert resp.get_json()["dialogues"]["nouvelle"] == [{"speaker": "pnj", "text": "Salut !"}] assert db.get_quest(slug, quest["id"])["dialogues"]["nouvelle"] == [{"speaker": "pnj", "text": "Salut !"}] def test_quest_dialogues_route_404s_for_missing_quest(client, game): resp = client.post(f"/game/{game}/quests/999999/dialogues", data='{"dialogues": {}}', content_type="application/json") assert resp.status_code == 404