Files
Vorrania/backend/tests/test_artikel_loeschen.py
ScarriffleandClaude Opus 5 264d3886f3 Artikelsuche findet die Marke - und Loeschen fragt erst nach dem Bestand
Die Suche verglich bisher einen einzigen Textschnipsel gegen den Namen. Wer
"Vogel Jack and Jones" eintippte, bekam nichts: Dieser Ausdruck steht in
keinem Artikelnamen, und die Marke wurde ueberhaupt nie durchsucht. Jetzt
wird der Suchtext in Woerter zerlegt, jedes muss in Name oder Marke
vorkommen, die Reihenfolge ist egal.

Gefiltert wird in Python und nicht per ILIKE, weil normalisieren() Umlaute
aufloest ("Muehle" findet "Mühle") - auf der Spalte faende ein ILIKE den
gespeicherten Wert nicht wieder. Dieselbe Form nutzt recipes.zuordnung_ziele
schon laenger. Alle Artikellisten haengen an dieser einen Route, die
Verbesserung wirkt also auch bei Gegenstaenden und beim Ein-/Auslagern.

Die Artikelzeile in der App zeigt nun die Marke, sonst waere ein Treffer
darauf nicht zu erklaeren.

Dazu die zweite Haelfte: delete_product hatte keinerlei Schutz und riss
Chargen, Einzelstuecke, Barcodes, Bilder und den ganzen Bewegungsverlauf per
Kaskade mit. Jetzt lehnt der Server mit 409 ab, solange Einzelstuecke oder
Chargen dranhaengen. Bewegungen allein halten bewusst nicht auf - sonst waere
jeder je benutzte Artikel fuer immer unloeschbar. Der Bestaetigungstext im
Web versprach den Verlust des Bestands und stimmte damit nicht mehr.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-15 20:06:07 +02:00

96 lines
2.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Artikel löschen – und wann der Server sich weigert.
Ohne Sperre reisst ein Klick Chargen, Einzelstücke, Barcodes, Bilder und den
gesamten Bewegungsverlauf per Kaskade mit. Geprüft wird deshalb der heutige
Bestand. Bewegungen allein halten bewusst NICHT auf, sonst waere jeder je
benutzte Artikel fuer immer unloeschbar.
Geprüft wird nur die Sperre selbst – die Kaskade nicht: Die Tests laufen auf
SQLite ohne ``PRAGMA foreign_keys=ON``, eine Kaskaden-Zusicherung waere dort
ohne Aussagekraft.
"""
import pytest
from fastapi import HTTPException
from app.models import BaseUnit, Item, Lot, Product, Role, User
from app.routers.products import delete_product
@pytest.fixture()
def admin(db):
person = User(username="chef", password_hash="x", role=Role.admin)
db.add(person)
db.commit()
db.refresh(person)
return person
def _artikel(db, name="Hammer"):
product = Product(name=name, base_unit=BaseUnit.piece, package_size=1,
source="manual")
db.add(product)
db.commit()
db.refresh(product)
return product
def test_loeschen_ohne_bestand_geht_durch(db, admin):
product = _artikel(db)
delete_product(product_id=product.id, db=db, _=admin)
assert db.get(Product, product.id) is None
def test_loeschen_mit_einzelstuecken_wird_abgelehnt(db, admin):
product = _artikel(db)
db.add(Item(uid="ABC12345", product_id=product.id))
db.commit()
with pytest.raises(HTTPException) as fehler:
delete_product(product_id=product.id, db=db, _=admin)
assert fehler.value.status_code == 409
assert "Einzelstücke" in fehler.value.detail
assert db.get(Product, product.id) is not None
def test_loeschen_mit_chargen_wird_abgelehnt(db, admin):
product = _artikel(db)
db.add(Lot(product_id=product.id, quantity=5))
db.commit()
with pytest.raises(HTTPException) as fehler:
delete_product(product_id=product.id, db=db, _=admin)
assert fehler.value.status_code == 409
assert "Bestand" in fehler.value.detail
assert db.get(Product, product.id) is not None
def test_loeschen_nach_leergeraeumter_charge_geht_durch(db, admin):
"""Wer alles auslagert, muss den Artikel loswerden können.
Haelt fest, dass die Sperre am heutigen Bestand haengt und nicht zu einer
Verlaufspruefung ausgebaut wird – sonst waere ein einmal benutzter Artikel
nie wieder loeschbar.
"""
product = _artikel(db)
lot = Lot(product_id=product.id, quantity=5)
db.add(lot)
db.commit()
db.delete(lot) # wie beim vollstaendigen Auslagern
db.commit()
delete_product(product_id=product.id, db=db, _=admin)
assert db.get(Product, product.id) is None
def test_unbekannter_artikel_bleibt_ein_404(db, admin):
with pytest.raises(HTTPException) as fehler:
delete_product(product_id=9999, db=db, _=admin)
assert fehler.value.status_code == 404