Dev #3

Merged
w-vandal merged 7 commits from dev into main 2026-08-27 17:46:02 +00:00
3 changed files with 71 additions and 5 deletions
Showing only changes of commit c14f7e9ba5 - Show all commits
+9 -3
View File
@@ -8,11 +8,17 @@ from core.flask_app import app
@app.route("/game/<slug>/objects/<int:definition_id>/fields/add", methods=["POST"])
def object_field_add(slug, definition_id):
name = request.form.get("field_name", "").strip()
if name:
field_type = request.form.get("field_type", "texte")
relation_definition_id = request.form.get("field_relation")
# Voir parse_field_rows.py (même garde) : "Relation" choisi sans objet
# cible valide ferait planter add_field_to_definition (int("") lève),
# ce qui annulait l'ALTER TABLE avant son commit() -- le champ
# n'était alors ajouté du tout, sans le moindre message d'erreur.
if name and not (field_type == "relation" and not (relation_definition_id or "").strip().isdigit()):
db.add_field_to_definition(slug, definition_id, {
"name": name,
"type": request.form.get("field_type", "texte"),
"relation_definition_id": request.form.get("field_relation"),
"type": field_type,
"relation_definition_id": relation_definition_id,
"required": request.form.get("field_required") == "1",
"min_value": request.form.get("field_min") or None,
"max_value": request.form.get("field_max") or None,
+18 -2
View File
@@ -9,10 +9,26 @@ def _parse_field_rows(form):
for i, fname in enumerate(field_names):
if not fname.strip():
continue
ftype = field_types[i]
relation_definition_id = field_relations[i] if i < len(field_relations) else None
if ftype == "relation" and not (relation_definition_id or "").strip().isdigit():
# Type "Relation vers un autre objet" choisi sans (ou avant
# d'avoir pu) sélectionner d'objet cible réel — ex. le tout
# premier objet créé dans un jeu, quand ce sélecteur est encore
# vide faute d'un autre objet à pointer. create_definition()
# fait un int(relation_definition_id) sans filet : le laisser
# passer plantait TOUTE la requête (500), et comme la ligne
# _definitions n'était alors jamais validée (pas de commit()
# atteint), l'objet n'était pas créé du tout — pas de champ
# "Relation" non plus, mais un panneau qui semble "ne rien
# faire" plutôt qu'un message d'erreur clair. On ignore donc ce
# champ précis (comme une ligne sans nom, ci-dessus) plutôt que
# de faire échouer la création de TOUT l'objet à cause de lui.
continue
fields.append({
"name": fname.strip(),
"type": field_types[i],
"relation_definition_id": field_relations[i] if i < len(field_relations) else None,
"type": ftype,
"relation_definition_id": relation_definition_id,
"required": field_required[i] == "1" if i < len(field_required) else False,
"min_value": (field_min[i].strip() or None) if i < len(field_min) else None,
"max_value": (field_max[i].strip() or None) if i < len(field_max) else None,
+44
View File
@@ -25,3 +25,47 @@ def test_object_not_created_without_a_name(client, game):
resp = client.post(f"/game/{game}/objects/new", data={"object_name": " "}, follow_redirects=False)
assert resp.status_code == 302
assert db.list_definitions(game) == []
def test_relation_field_without_a_chosen_target_does_not_crash_the_whole_request(client, game):
"""Régression : "Relation vers un autre objet" est TOUJOURS une option
du sélecteur de type (db/constants.py), même quand il n'existe encore
AUCUN autre objet à pointer (ex. le tout premier objet créé dans un
jeu) — le sélecteur "Objet lié" est alors vide, et
create_definition() faisait int("") sans filet, plantant TOUTE la
requête (500) avant même d'atteindre son commit() : l'objet n'était
donc PAS créé du tout, pas seulement son champ "Relation" — vécu comme
"le panneau recharge la page sans créer d'objet" alors que des champs
avaient bien été renseignés. Le champ "Relation" invalide doit être
ignoré (comme une ligne sans nom), pas faire échouer tout le reste."""
resp = client.post(f"/game/{game}/objects/new", data={
"object_name": "Personnage",
"field_name[]": ["nom", "arme"],
"field_type[]": ["texte", "relation"],
"field_relation[]": ["", ""],
"field_required[]": ["0", "0"],
"field_min[]": ["", ""],
"field_max[]": ["", ""],
}, follow_redirects=False)
assert resp.status_code == 302
definitions = db.list_definitions(game)
assert len(definitions) == 1
fields = db.get_definition(game, definitions[0]["id"])["fields"]
assert [f["name"] for f in fields] == ["nom"]
def test_add_relation_field_without_a_chosen_target_is_ignored_not_a_crash(client, game):
"""Même garde, côté "+ Ajouter un champ" du panneau d'un objet déjà
créé (object_field_add.py / add_field_to_definition.py)."""
resp = client.post(f"/game/{game}/objects/new", data={
"object_name": "Personnage", "field_name[]": ["nom"], "field_type[]": ["texte"],
"field_relation[]": [""], "field_required[]": ["0"], "field_min[]": [""], "field_max[]": [""],
}, follow_redirects=False)
def_id = int(resp.headers["Location"].rstrip("/").split("/")[-1])
resp = client.post(f"/game/{game}/objects/{def_id}/fields/add", data={
"field_name": "arme", "field_type": "relation", "field_relation": "",
}, follow_redirects=False)
assert resp.status_code == 302
fields = db.get_definition(game, def_id)["fields"]
assert [f["name"] for f in fields] == ["nom"]