From 7476ed229e34f9fb34be4bb63347f1d13d6f1faa Mon Sep 17 00:00:00 2001 From: william Date: Sun, 30 Aug 2026 16:47:59 +0200 Subject: [PATCH] =?UTF-8?q?Phase=202=20:=20hasard=20+=20op=C3=A9rations=20?= =?UTF-8?q?math=C3=A9matiques?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit screens/labels/data_operations.py : 6 nouvelles opérations pour "Modifier une donnée"/"Modifier une variable" — multiplier, diviser, modulo (garde-fou division par zéro : valeur inchangée plutôt qu'une ZeroDivisionError qui interromprait le graphe), minimum/maximum (borne la valeur ACTUELLE — utile pour une variable globale, qui n'a pas de min_value/max_value comme un champ d'objet), et alea (tire un nombre aléatoire entre deux bornes). screens/data_actions/compute_operation.py (déjà factorisé en Phase 0, donc une seule implémentation pour apply_data_action.py/ apply_variable_action.py) : implémente les 6. "alea" est la seule à deux opérandes — réutilise data_value au format "min,max" plutôt qu'une nouvelle colonne de nœud (bornes remises dans l'ordre si inversées). random.uniform pour un résultat décimal, random.randint pour un entier. static/js/screen_edit/flow-editor.js + templates/screen_edit.html : petit indice visuel — le champ "Valeur / montant" du formulaire de nœud affiche "min,max (ex. 1,6)" quand "alea" est choisi, pour ne pas laisser deviner ce format à deux nombres, différent de toutes les autres opérations. Sinon aucun nouveau champ/changement de schéma nécessaire, le - {% for key, label in data_operations %} {% endfor %} @@ -437,7 +437,7 @@ {% endfor %} - {% for key, label in data_operations %} {% endfor %} diff --git a/tests/test_compute_operation.py b/tests/test_compute_operation.py index df60e2c6..7e6a8b2f 100644 --- a/tests/test_compute_operation.py +++ b/tests/test_compute_operation.py @@ -1,9 +1,9 @@ """Tests unitaires de compute_new_value (screens/data_actions/compute_operation.py) — logique partagée par apply_data_action.py (champ d'objet) et apply_variable_action.py (variable globale), factorisée pour ne plus être -dupliquée dans les deux fichiers avant que la Phase 2 (hasard/opérations -mathématiques) n'y ajoute 6 nouvelles opérations. Verrouille le -comportement des 7 opérations existantes.""" +dupliquée dans les deux fichiers. Verrouille le comportement des 7 +opérations historiques ET des 6 opérations ajoutées en Phase 2 (hasard + +opérations mathématiques).""" import pytest from screens.data_actions.compute_operation import compute_new_value @@ -51,4 +51,66 @@ def test_incrementer_valeur_invalide_ne_change_rien(): def test_operation_inconnue_leve_value_error(): with pytest.raises(ValueError): - compute_new_value("multiplier", 10, "2", False) + compute_new_value("teleporter", 10, "2", False) + + +# ---------- Phase 2 — hasard + opérations mathématiques ---------- + +def test_multiplier(): + assert compute_new_value("multiplier", 10, "3", False) == 30 + assert compute_new_value("multiplier", 4.0, "2.5", True) == 10.0 + + +def test_diviser(): + assert compute_new_value("diviser", 10, "2", False) == 5 + assert compute_new_value("diviser", 5.0, "2", True) == 2.5 + + +def test_diviser_par_zero_ne_change_rien(): + assert compute_new_value("diviser", 10, "0", False) == 10 + + +def test_modulo(): + assert compute_new_value("modulo", 10, "3", False) == 1 + assert compute_new_value("modulo", 10, "0", False) == 10 # garde-fou division par zéro + + +def test_minimum_releve_la_valeur_si_en_dessous(): + assert compute_new_value("minimum", 3, "10", False) == 10 + assert compute_new_value("minimum", 15, "10", False) == 15 # déjà au-dessus : inchangé + + +def test_maximum_plafonne_la_valeur_si_au_dessus(): + assert compute_new_value("maximum", 15, "10", False) == 10 + assert compute_new_value("maximum", 3, "10", False) == 3 # déjà en dessous : inchangé + + +def test_minimum_maximum_valeur_invalide_ne_change_rien(): + assert compute_new_value("minimum", 5, "pas un nombre", False) == 5 + assert compute_new_value("maximum", 5, "", False) == 5 + + +def test_alea_entier_dans_les_bornes(): + for _ in range(50): + result = compute_new_value("alea", 0, "1,6", False) + assert isinstance(result, int) + assert 1 <= result <= 6 + + +def test_alea_decimal_dans_les_bornes(): + for _ in range(50): + result = compute_new_value("alea", 0, "1.5,2.5", True) + assert isinstance(result, float) + assert 1.5 <= result <= 2.5 + + +def test_alea_bornes_inversees_sont_remises_dans_lordre(): + for _ in range(50): + result = compute_new_value("alea", 0, "6,1", False) + assert 1 <= result <= 6 + + +def test_alea_valeur_invalide_retombe_sur_zero(): + assert compute_new_value("alea", 0, "pas un nombre", False) == 0 + assert compute_new_value("alea", 0, "", False) == 0 + assert compute_new_value("alea", 0, "5", False) == 0 # une seule borne : incomplet