Un code-barre inconnu scanné propose désormais le même formulaire de création que /materiels au lieu d'un simple message d'erreur. La quantité peut être ajustée d'un delta choisi librement (pas figé à ±1) ou définie directement à une valeur absolue après un inventaire. La racine "/" sert directement la liste des matériels sans redirection. La page Matériels gagne un filtre par catégorie, un filtre stock bas, et un tri incluant un regroupement par catégorie.
178 lines
5.5 KiB
Python
178 lines
5.5 KiB
Python
"""
|
|
Tests du flux principal : créer une arborescence de catégories, créer un
|
|
matériel, le retrouver (ou le créer) par scan, ajuster son stock de deux
|
|
façons différentes, et vérifier que l'alerte de stock bas se déclenche
|
|
(elle est simplement affichée dans la console tant qu'aucun SMTP n'est
|
|
configuré, voir app/email_alerts.py).
|
|
"""
|
|
|
|
from sqlmodel import Session, select
|
|
|
|
from app.models import Categorie, DestinataireAlerte, Materiel
|
|
|
|
|
|
def _creer_sous_categorie(session: Session) -> Categorie:
|
|
"""Crée une petite arborescence à 2 niveaux (Ordinateurs > Portables)
|
|
pour les tests qui n'ont pas besoin de tester la profondeur illimitée
|
|
elle-même — voir test_categories.py pour ça."""
|
|
racine = Categorie(nom="Ordinateurs")
|
|
session.add(racine)
|
|
session.commit()
|
|
session.refresh(racine)
|
|
|
|
enfant = Categorie(nom="Portables", parent_id=racine.id)
|
|
session.add(enfant)
|
|
session.commit()
|
|
session.refresh(enfant)
|
|
return enfant
|
|
|
|
|
|
def test_creation_materiel(client, session):
|
|
sous_categorie = _creer_sous_categorie(session)
|
|
|
|
reponse = client.post(
|
|
"/materiels",
|
|
data={
|
|
"nom": "PC Dell XPS 13",
|
|
"categorie_id": sous_categorie.id,
|
|
"code_barre": "1234567890123",
|
|
"quantite": "5",
|
|
"seuil_alerte": "2",
|
|
},
|
|
follow_redirects=False,
|
|
)
|
|
|
|
assert reponse.status_code == 303
|
|
materiel = session.exec(select(Materiel)).first()
|
|
assert materiel is not None
|
|
assert materiel.quantite == 5
|
|
|
|
|
|
def test_racine_affiche_la_liste_des_materiels(client, session):
|
|
""" "/" doit afficher directement les matériels, pas rediriger."""
|
|
reponse = client.get("/", follow_redirects=False)
|
|
|
|
assert reponse.status_code == 200
|
|
assert "Matériels" in reponse.text
|
|
|
|
|
|
def test_scan_trouve_le_materiel_par_code_barre(client, session):
|
|
sous_categorie = _creer_sous_categorie(session)
|
|
session.add(
|
|
Materiel(
|
|
nom="Câble RJ45 2m",
|
|
categorie_id=sous_categorie.id,
|
|
code_barre="9998887776665",
|
|
quantite=10,
|
|
)
|
|
)
|
|
session.commit()
|
|
|
|
reponse = client.post("/scan", data={"code_barre": "9998887776665"})
|
|
|
|
assert reponse.status_code == 200
|
|
assert "Câble RJ45 2m" in reponse.text
|
|
|
|
|
|
def test_scan_code_inconnu_propose_la_creation(client, session):
|
|
"""Un code-barre sans matériel correspondant doit proposer le
|
|
formulaire de création rapide, pas juste un message d'erreur."""
|
|
_creer_sous_categorie(session)
|
|
|
|
reponse = client.post("/scan", data={"code_barre": "0000000000000"})
|
|
|
|
assert reponse.status_code == 200
|
|
assert "0000000000000" in reponse.text
|
|
assert 'name="categorie_id"' in reponse.text
|
|
|
|
|
|
def test_creation_depuis_le_scan(client, session):
|
|
sous_categorie = _creer_sous_categorie(session)
|
|
|
|
reponse = client.post(
|
|
"/scan/creer",
|
|
data={
|
|
"nom": "Clavier sans fil",
|
|
"code_barre": "5551112223334",
|
|
"categorie_id": sous_categorie.id,
|
|
"quantite": "4",
|
|
},
|
|
)
|
|
|
|
assert reponse.status_code == 200
|
|
assert "Clavier sans fil" in reponse.text
|
|
materiel = session.exec(select(Materiel).where(Materiel.code_barre == "5551112223334")).first()
|
|
assert materiel is not None
|
|
assert materiel.quantite == 4
|
|
|
|
|
|
def test_ajuster_quantite_avec_un_delta_choisi(client, session):
|
|
sous_categorie = _creer_sous_categorie(session)
|
|
materiel = Materiel(nom="Écran 24 pouces", categorie_id=sous_categorie.id, quantite=10)
|
|
session.add(materiel)
|
|
session.commit()
|
|
session.refresh(materiel)
|
|
|
|
reponse = client.post(f"/scan/{materiel.id}/ajuster", data={"quantite": "4", "sens": "retirer"})
|
|
|
|
assert reponse.status_code == 200
|
|
session.refresh(materiel)
|
|
assert materiel.quantite == 6
|
|
|
|
|
|
def test_definir_quantite_directement(client, session):
|
|
sous_categorie = _creer_sous_categorie(session)
|
|
materiel = Materiel(nom="Écran 24 pouces", categorie_id=sous_categorie.id, quantite=10)
|
|
session.add(materiel)
|
|
session.commit()
|
|
session.refresh(materiel)
|
|
|
|
reponse = client.post(f"/scan/{materiel.id}/definir", data={"quantite": "17"})
|
|
|
|
assert reponse.status_code == 200
|
|
session.refresh(materiel)
|
|
assert materiel.quantite == 17
|
|
|
|
|
|
def test_alerte_declenchee_sous_le_seuil(client, session, capsys):
|
|
sous_categorie = _creer_sous_categorie(session)
|
|
materiel = Materiel(
|
|
nom="Souris USB",
|
|
categorie_id=sous_categorie.id,
|
|
quantite=3,
|
|
seuil_alerte=5,
|
|
)
|
|
session.add(materiel)
|
|
session.commit()
|
|
session.refresh(materiel)
|
|
session.add(DestinataireAlerte(email="it-dept@clinique.local"))
|
|
session.commit()
|
|
|
|
reponse = client.post(f"/scan/{materiel.id}/ajuster", data={"quantite": "1", "sens": "retirer"})
|
|
|
|
assert reponse.status_code == 200
|
|
sortie = capsys.readouterr().out
|
|
assert "EMAIL SIMULÉ" in sortie
|
|
assert "Souris USB" in sortie
|
|
|
|
|
|
def test_pas_alerte_si_stock_suffisant(client, session, capsys):
|
|
sous_categorie = _creer_sous_categorie(session)
|
|
materiel = Materiel(
|
|
nom="Clavier USB",
|
|
categorie_id=sous_categorie.id,
|
|
quantite=10,
|
|
seuil_alerte=5,
|
|
)
|
|
session.add(materiel)
|
|
session.commit()
|
|
session.refresh(materiel)
|
|
session.add(DestinataireAlerte(email="it-dept@clinique.local"))
|
|
session.commit()
|
|
|
|
reponse = client.post(f"/scan/{materiel.id}/ajuster", data={"quantite": "1", "sens": "retirer"})
|
|
|
|
assert reponse.status_code == 200
|
|
sortie = capsys.readouterr().out
|
|
assert "EMAIL SIMULÉ" not in sortie
|