"""Onglet "Générer avec l'IA" — plusieurs CONVERSATIONS par écran (CRUD, comme claude.ai, voir plan) : routes/ia/ia_chat.py + screens/ia/. ANTHROPIC_API_KEY est toujours absente en test (voir tests/conftest.py) : ces tests exercent donc le chemin de DÉGRADATION GRACIEUSE (message d'erreur clair, jamais une 500) pour l'envoi de message — pas un appel réel, quel que soit le .env local du poste.""" import screens def _create_jeu2d_game(client, tmp_game_slug_cleanup, name="pytest_ia_chat"): resp = client.post("/games/new", data={"name": name}, follow_redirects=False) slug = tmp_game_slug_cleanup(resp.headers["Location"].rstrip("/").split("/")[-1]) screen_id = screens.create_screen(slug, "Scène 1", kind="jeu_2d") return slug, screen_id def test_create_and_list_conversations(client, tmp_game_slug_cleanup): slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup) resp = client.post(f"/game/{slug}/screens/{screen_id}/ia-conversations") assert resp.status_code == 200 conversation = resp.get_json() assert conversation["title"] == "Nouvelle conversation" resp2 = client.get(f"/game/{slug}/screens/{screen_id}/ia-conversations") assert resp2.status_code == 200 ids = [c["id"] for c in resp2.get_json()] assert conversation["id"] in ids def test_conversations_are_isolated_per_screen(client, tmp_game_slug_cleanup): slug, screen_id_1 = _create_jeu2d_game(client, tmp_game_slug_cleanup) screen_id_2 = screens.create_screen(slug, "Scène 2", kind="jeu_2d") client.post(f"/game/{slug}/screens/{screen_id_1}/ia-conversations") resp = client.get(f"/game/{slug}/screens/{screen_id_2}/ia-conversations") assert resp.get_json() == [] def test_delete_conversation_removes_it_from_the_list(client, tmp_game_slug_cleanup): slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup) conversation = client.post(f"/game/{slug}/screens/{screen_id}/ia-conversations").get_json() resp = client.delete(f"/game/{slug}/ia-conversations/{conversation['id']}") assert resp.status_code == 200 remaining = client.get(f"/game/{slug}/screens/{screen_id}/ia-conversations").get_json() assert all(c["id"] != conversation["id"] for c in remaining) def test_delete_unknown_conversation_404s(client, game): resp = client.delete(f"/game/{game}/ia-conversations/999999") assert resp.status_code == 404 def test_send_message_persists_it_and_a_graceful_error_reply(client, tmp_game_slug_cleanup): slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup) conversation = client.post(f"/game/{slug}/screens/{screen_id}/ia-conversations").get_json() resp = client.post( f"/game/{slug}/ia-conversations/{conversation['id']}/messages", json={"message": "Génère un dialogue"} ) assert resp.status_code == 200 body = resp.get_json() assert body["user_message"]["content"] == "Génère un dialogue" assert body["user_message"]["role"] == "user" assert body["assistant_message"]["role"] == "assistant" assert "Configuration IA incomplète" in body["assistant_message"]["content"] def test_send_message_rejects_an_empty_message(client, tmp_game_slug_cleanup): slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup) conversation = client.post(f"/game/{slug}/screens/{screen_id}/ia-conversations").get_json() resp = client.post(f"/game/{slug}/ia-conversations/{conversation['id']}/messages", json={"message": " "}) assert resp.status_code == 400 def test_send_message_404s_for_an_unknown_conversation(client, game): resp = client.post(f"/game/{game}/ia-conversations/999999/messages", json={"message": "salut"}) assert resp.status_code == 404 def test_messages_history_is_persisted_and_ordered(client, tmp_game_slug_cleanup): slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup) conversation = client.post(f"/game/{slug}/screens/{screen_id}/ia-conversations").get_json() client.post(f"/game/{slug}/ia-conversations/{conversation['id']}/messages", json={"message": "premier"}) client.post(f"/game/{slug}/ia-conversations/{conversation['id']}/messages", json={"message": "second"}) resp = client.get(f"/game/{slug}/ia-conversations/{conversation['id']}/messages") assert resp.status_code == 200 messages = resp.get_json() user_contents = [m["content"] for m in messages if m["role"] == "user"] assert user_contents == ["premier", "second"] def test_messages_are_isolated_per_conversation(client, tmp_game_slug_cleanup): slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup) conversation_1 = client.post(f"/game/{slug}/screens/{screen_id}/ia-conversations").get_json() conversation_2 = client.post(f"/game/{slug}/screens/{screen_id}/ia-conversations").get_json() client.post(f"/game/{slug}/ia-conversations/{conversation_1['id']}/messages", json={"message": "pour la 1ère"}) resp = client.get(f"/game/{slug}/ia-conversations/{conversation_2['id']}/messages") assert resp.get_json() == [] def test_first_user_message_renames_the_conversation(client, tmp_game_slug_cleanup): slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup) conversation = client.post(f"/game/{slug}/screens/{screen_id}/ia-conversations").get_json() client.post(f"/game/{slug}/ia-conversations/{conversation['id']}/messages", json={"message": "Crée un fond de bureau"}) conversations = client.get(f"/game/{slug}/screens/{screen_id}/ia-conversations").get_json() updated = next(c for c in conversations if c["id"] == conversation["id"]) assert updated["title"] == "Crée un fond de bureau" # Un deuxième message ne renomme plus (titre déjà dérivé du premier). client.post(f"/game/{slug}/ia-conversations/{conversation['id']}/messages", json={"message": "et une variable"}) conversations2 = client.get(f"/game/{slug}/screens/{screen_id}/ia-conversations").get_json() updated2 = next(c for c in conversations2 if c["id"] == conversation["id"]) assert updated2["title"] == "Crée un fond de bureau"