From 1551faaf0c9dd8c22f25e891ace92b34535d9c42 Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Fri, 3 Jul 2026 23:10:32 +0200 Subject: [PATCH] =?UTF-8?q?Normaliser=20les=20=C3=A9checs=20d'=C3=A9critur?= =?UTF-8?q?e=20set=5Fcell=20en=20ValueError?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - IntegrityError (ex: email déjà utilisé) est désormais capturée et reconvertie en ValueError, pour rester dans le funnel d'alerte existant du callback admin au lieu de faire planter le callback Dash. - Une UPDATE qui touche 0 ligne (ligne supprimée entre le chargement du tableau et la soumission de l'édition) lève désormais une ValueError au lieu d'être silencieusement traitée comme un succès (ce qui aurait créé un log d'audit trompeur). Co-Authored-By: Claude Sonnet 5 --- src/admin/tables.py | 17 ++++++++++++++--- tests/admin/test_tables.py | 22 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/src/admin/tables.py b/src/admin/tables.py index c49221c..d19d2cd 100644 --- a/src/admin/tables.py +++ b/src/admin/tables.py @@ -1,3 +1,4 @@ +import sqlite3 from dataclasses import dataclass from typing import Callable @@ -134,9 +135,19 @@ def set_cell(table: str, pk_value, column: str, value) -> None: raise ValueError(f"Table inconnue : {table}") cfg = TABLES[table] coerced = _coerce_value(table, column, value) - get_conn().execute( - f"UPDATE {table} SET {column} = ? WHERE {cfg.pk} = ?", (coerced, pk_value) - ) + try: + cursor = get_conn().execute( + f"UPDATE {table} SET {column} = ? WHERE {cfg.pk} = ?", (coerced, pk_value) + ) + except sqlite3.IntegrityError as exc: + raise ValueError( + f"Écriture refusée pour {column} (contrainte violée) : {value!r}" + ) from exc + if cursor.rowcount == 0: + raise ValueError( + f"Ligne introuvable (table={table}, {cfg.pk}={pk_value!r}) — " + "probablement supprimée entre-temps." + ) def find_changed_cell( diff --git a/tests/admin/test_tables.py b/tests/admin/test_tables.py index bf55ce7..3ca7382 100644 --- a/tests/admin/test_tables.py +++ b/tests/admin/test_tables.py @@ -68,6 +68,28 @@ def test_set_cell_writes_valid_value(users_db_path): assert rows[0]["siret"] == "12345678900011" +def test_set_cell_raises_valueerror_on_unique_constraint_violation(users_db_path): + from src.auth import db as auth_db + + auth_db.init_schema() + auth_db.create_user("first@ex.fr", "hash") + second_uid = auth_db.create_user("second@ex.fr", "hash") + + with pytest.raises(ValueError): + tables.set_cell("users", second_uid, "email", "first@ex.fr") + + +def test_set_cell_raises_valueerror_when_row_missing(users_db_path): + from src.auth import db as auth_db + + auth_db.init_schema() + uid = auth_db.create_user("a@ex.fr", "hash") + auth_db.delete_user(uid) + + with pytest.raises(ValueError): + tables.set_cell("users", uid, "email", "new@ex.fr") + + def test_set_cell_coerces_numeric_type(users_db_path): from src.auth import db as auth_db from src.subscriptions import db as sub_db