test: socle de tests executables et fabrique d'application parametrable

Il n'existait aucun test, et le code n'offrait aucune prise pour en ecrire :
create_app() exigeait SECRET_KEY et DATABASE_URL dans l'environnement,
creait les tables et demarrait un bot Discord. C'etait la cause, pas le
symptome.

create_app(config=None)
  Les valeurs par defaut viennent toujours de l'environnement, les
  surcharges de l'appelant sont appliquees ensuite, et la validation
  vient en dernier pour qu'un test puisse fournir les siennes. Deux
  effets de bord passent sous drapeau, actifs par defaut pour que la
  production et le developpement se comportent a l'identique :
    AUTO_CREATE_TABLES   controle db.create_all()
    ENABLE_DISCORD_BOT   controle start_bot()
  FORCE_HTTPS passe egalement en configuration : lu via os.getenv a
  chaque requete, il renvoyait un 301 sur tout appel de test.

Suite de tests : 47 tests, 3 xfail, 32 % de couverture.
  tests/conftest.py            fabriques par role, connexion par le vrai
                               formulaire, base SQLite temporaire
  test_auth_session.py         expiration de session, desactivation de
                               compte, deconnexion
  test_security_headers.py     en-tetes, non-divulgation sur /health,
                               echappement de nl2br
  test_authorization.py        acces anonyme, vertical, horizontal,
                               validation des entrees, CSRF

Les tests marques xfail(strict=True) decrivent des constats non encore
corriges. Ils echouent par construction ; le mode strict transforme une
reussite inattendue en echec, ce qui signale qu'il faut retirer le
marqueur. Trois subsistent : enumeration de comptes (SEC-AUTH-006), CSP
unsafe-inline (SEC-WEB-001), auto-retrogradation du dernier administrateur
(SEC-AUTHZ-007).

pyproject.toml
  Configuration pytest et ruff. Ruff n'avait aucune configuration : la CI
  l'executait avec le jeu de regles par defaut. Les 33 F401 de
  app/models/__init__.py sont ignores par fichier, c'est une facade de
  re-export intentionnelle.

requirements-dev.txt separe l'outillage de test des dependances de
production.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
GGThed
2026-08-07 19:46:34 -04:00
co-authored by Claude Opus 5
parent de9448a9aa
commit 1b990a84d9
7 changed files with 821 additions and 16 deletions
+42
View File
@@ -0,0 +1,42 @@
# Tooling configuration.
#
# Deliberately limited to tool settings: the project is run from wsgi.py,
# not installed as a distribution, so there is no [project] table yet.
# Consolidating requirements.txt / requirements-dev.txt into dependency
# groups here is tracked as QUA-001.
[tool.pytest.ini_options]
testpaths = ["tests"]
pythonpath = ["."]
addopts = "-q --strict-markers --strict-config"
filterwarnings = [
"default",
# discord.py imports audioop, removed from the stdlib in 3.13.
"ignore:'audioop' is deprecated:DeprecationWarning",
# Every model uses datetime.utcnow as a column default. Tracked as
# DB-009; the warning would otherwise drown the run.
"ignore:datetime.datetime.utcnow:DeprecationWarning",
]
[tool.ruff]
line-length = 100
target-version = "py312"
exclude = [".venv", "venv", "migrations", "docs"]
[tool.ruff.lint]
# Starting from ruff's default rule set (pyflakes + a slice of pycodestyle).
# Widening it — bugbear, isort, pyupgrade — is deliberately deferred until
# the codebase has been formatted once, so that the first enforcement is
# about real defects rather than churn. Tracked as QUA-002.
select = ["E4", "E7", "E9", "F"]
[tool.ruff.lint.per-file-ignores]
# Intentional re-export facade: `from app.models import User, Tryout, ...`
# is the documented entry point, and importing the modules is what
# registers every model with SQLAlchemy.
"app/models/__init__.py" = ["F401"]
"app/models/user_model/__init__.py" = ["F401"]
"tests/conftest.py" = ["E402"]
[tool.ruff.format]
quote-style = "preserve"