Wichtigster Fund: mein eigener CI-Check hat nur den zuletzt aktualisierten Task angeschaut statt beide Jobs (backend-tests/frontend-build) einzeln zu prüfen - dadurch ist die tatsächlich rote backend-tests-Pipeline seit mehreren Commits unbemerkt geblieben (auch schon deployed). Echter Bug (von mir eingeführt in SEARCH-002): /suche filterte auf Objektposition.status, das Feld heißt aber ist_status - AttributeError bei jeder Materialsuche. 5 veraltete Testerwartungen korrigiert (Logik in dashboard.py ist seit längerem korrekt und dokumentiert, Tests wurden nie nachgezogen): "noch_nie_kontrolliert" gilt laut _kategorie_und_gruende() IMMER zusätzlich zu jedem anderen Grund - test_dashboard.py/test_mangel.py/ test_fahrzeugdetails.py prüften teils das Gegenteil. 1 Typo-Bug in test_kontrolle.py (UUID != str-Vergleich, immer falsch). Offen/nicht geklärt: test_stammdaten.py::test_fach_verwendung_und_ersetzen_ fuehrt_zusammen (KeyError 'id' bei Fach-Merge-Response) - ohne lokale Testausführung nicht sicher diagnostizierbar, braucht echten pytest-Lauf. Nebenbei: FOUND-009/FOUND-011 im Backlog korrigiert (CI existiert bereits unter .gitea/, vorheriger Abgleich hatte nur .github/ geprüft). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVgbozhYmuEhiEJHffRXCV
188 lines
7.4 KiB
Python
188 lines
7.4 KiB
Python
import pytest
|
|
|
|
from tests.conftest import auth_header, login
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_mangel_melden_und_lesen(client, objekt_mit_position, mitarbeiter_user):
|
|
objekt, _material = objekt_mit_position
|
|
token = await login(client, "mitarbeiter1")
|
|
|
|
gemeldet = await client.post(
|
|
"/api/v1/maengel",
|
|
json={"objekt_id": objekt.id, "beschreibung": "Blaulicht vorne rechts defekt", "prioritaet": "hoch"},
|
|
headers=auth_header(token),
|
|
)
|
|
assert gemeldet.status_code == 201
|
|
assert gemeldet.json()["status"] == "neu"
|
|
|
|
liste = await client.get(f"/api/v1/maengel?objekt_id={objekt.id}", headers=auth_header(token))
|
|
assert len(liste.json()) == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_mitarbeiter_darf_status_nicht_aendern(client, objekt_mit_position, mitarbeiter_user):
|
|
objekt, _material = objekt_mit_position
|
|
token = await login(client, "mitarbeiter1")
|
|
gemeldet = await client.post(
|
|
"/api/v1/maengel",
|
|
json={"objekt_id": objekt.id, "beschreibung": "Reifen platt"},
|
|
headers=auth_header(token),
|
|
)
|
|
mangel_id = gemeldet.json()["id"]
|
|
|
|
response = await client.patch(
|
|
f"/api/v1/maengel/{mangel_id}", json={"status": "in_bearbeitung"}, headers=auth_header(token)
|
|
)
|
|
assert response.status_code == 403
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_materialverantwortlicher_kann_status_aendern_und_erledigen(
|
|
client, objekt_mit_position, mitarbeiter_user, materialverantwortlicher_user
|
|
):
|
|
objekt, _material = objekt_mit_position
|
|
mitarbeiter_token = await login(client, "mitarbeiter1")
|
|
gemeldet = await client.post(
|
|
"/api/v1/maengel",
|
|
json={"objekt_id": objekt.id, "beschreibung": "Sirene ausgefallen"},
|
|
headers=auth_header(mitarbeiter_token),
|
|
)
|
|
mangel_id = gemeldet.json()["id"]
|
|
|
|
verantwortlicher_token = await login(client, "materialverantwortlicher1")
|
|
bearbeitung = await client.patch(
|
|
f"/api/v1/maengel/{mangel_id}", json={"status": "ersatzteil_bestellt"}, headers=auth_header(verantwortlicher_token)
|
|
)
|
|
assert bearbeitung.json()["status"] == "ersatzteil_bestellt"
|
|
|
|
erledigt = await client.patch(
|
|
f"/api/v1/maengel/{mangel_id}", json={"status": "erledigt"}, headers=auth_header(verantwortlicher_token)
|
|
)
|
|
assert erledigt.json()["status"] == "erledigt"
|
|
assert erledigt.json()["erledigt_am"] is not None
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_entitaet_typ_und_id_muessen_zusammen_gesetzt_sein(client, objekt_mit_position, mitarbeiter_user):
|
|
objekt, _material = objekt_mit_position
|
|
token = await login(client, "mitarbeiter1")
|
|
response = await client.post(
|
|
"/api/v1/maengel",
|
|
json={"objekt_id": objekt.id, "beschreibung": "X", "entitaet_typ": "geraet_instanz"},
|
|
headers=auth_header(token),
|
|
)
|
|
assert response.status_code == 422
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_kritischer_offener_mangel_macht_objekt_nicht_einsatzbereit(
|
|
client, objekt_mit_position, mitarbeiter_user, materialverantwortlicher_user
|
|
):
|
|
objekt, _material = objekt_mit_position
|
|
mitarbeiter_token = await login(client, "mitarbeiter1")
|
|
await client.post(
|
|
"/api/v1/maengel",
|
|
json={"objekt_id": objekt.id, "beschreibung": "Bremsen defekt", "prioritaet": "hoch"},
|
|
headers=auth_header(mitarbeiter_token),
|
|
)
|
|
|
|
verantwortlicher_token = await login(client, "materialverantwortlicher1")
|
|
response = await client.get(
|
|
"/api/v1/dashboard/einsatzbereitschaft", headers=auth_header(verantwortlicher_token)
|
|
)
|
|
body = response.json()
|
|
assert body["nicht_einsatzbereit"] == 1
|
|
# Objekt wurde in diesem Test nie kontrolliert - "noch_nie_kontrolliert"
|
|
# gilt laut _kategorie_und_gruende() zusätzlich zu jedem anderen Grund.
|
|
assert body["details"][0]["gruende"] == ["mangel_kritisch_offen", "noch_nie_kontrolliert"]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_niedrige_prioritaet_blockiert_einsatzbereitschaft_nicht(
|
|
client, objekt_mit_position, mitarbeiter_user, materialverantwortlicher_user
|
|
):
|
|
objekt, material = objekt_mit_position
|
|
mitarbeiter_token = await login(client, "mitarbeiter1")
|
|
# kontrolliert (Ist=Soll), damit dieser Test unabhängig vom "nie kontrolliert"-Fall
|
|
# (siehe test_dashboard.py: test_nie_kontrolliertes_objekt_ist_unbekannt) nur die
|
|
# Priorität isoliert prüft.
|
|
start = await client.post(
|
|
f"/api/v1/objekte/{objekt.id}/kontrollen", json={"uebernehmen": False}, headers=auth_header(mitarbeiter_token)
|
|
)
|
|
kontrolle_id = start.json()["id"]
|
|
await client.put(
|
|
f"/api/v1/kontrollen/{kontrolle_id}/positionen/{material.id}",
|
|
json={"istmenge": "10"},
|
|
headers=auth_header(mitarbeiter_token),
|
|
)
|
|
await client.post(f"/api/v1/kontrollen/{kontrolle_id}/abschliessen", headers=auth_header(mitarbeiter_token))
|
|
|
|
await client.post(
|
|
"/api/v1/maengel",
|
|
json={"objekt_id": objekt.id, "beschreibung": "Kratzer im Lack", "prioritaet": "niedrig"},
|
|
headers=auth_header(mitarbeiter_token),
|
|
)
|
|
|
|
verantwortlicher_token = await login(client, "materialverantwortlicher1")
|
|
response = await client.get(
|
|
"/api/v1/dashboard/einsatzbereitschaft", headers=auth_header(verantwortlicher_token)
|
|
)
|
|
assert response.json()["einsatzbereit"] == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_leitungsverantwortlicher_kann_status_aendern(client, objekt_mit_position, mitarbeiter_user, db_session):
|
|
from app.core.security import hash_password
|
|
from app.models.auth import Benutzer, BenutzerRolle, RolleTyp
|
|
|
|
benutzer = Benutzer(
|
|
name="Test Leitung", login="leitung1", passwort_hash=hash_password("test-passwort-123"), aktiv=True
|
|
)
|
|
db_session.add(benutzer)
|
|
await db_session.flush()
|
|
db_session.add(BenutzerRolle(benutzer_id=benutzer.id, rolle=RolleTyp.leitungsverantwortlicher))
|
|
await db_session.flush()
|
|
|
|
objekt, _material = objekt_mit_position
|
|
mitarbeiter_token = await login(client, "mitarbeiter1")
|
|
gemeldet = await client.post(
|
|
"/api/v1/maengel",
|
|
json={"objekt_id": objekt.id, "beschreibung": "Scheinwerfer defekt"},
|
|
headers=auth_header(mitarbeiter_token),
|
|
)
|
|
mangel_id = gemeldet.json()["id"]
|
|
|
|
leitung_token = await login(client, "leitung1")
|
|
response = await client.patch(
|
|
f"/api/v1/maengel/{mangel_id}", json={"status": "erledigt"}, headers=auth_header(leitung_token)
|
|
)
|
|
assert response.status_code == 200
|
|
assert response.json()["status"] == "erledigt"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_mangel_meldung_und_status_aenderung_stehen_in_historie(
|
|
client, objekt_mit_position, mitarbeiter_user, materialverantwortlicher_user
|
|
):
|
|
objekt, _material = objekt_mit_position
|
|
mitarbeiter_token = await login(client, "mitarbeiter1")
|
|
gemeldet = await client.post(
|
|
"/api/v1/maengel",
|
|
json={"objekt_id": objekt.id, "beschreibung": "Funkgerät ausgefallen"},
|
|
headers=auth_header(mitarbeiter_token),
|
|
)
|
|
mangel_id = gemeldet.json()["id"]
|
|
|
|
verantwortlicher_token = await login(client, "materialverantwortlicher1")
|
|
await client.patch(
|
|
f"/api/v1/maengel/{mangel_id}", json={"status": "in_bearbeitung"}, headers=auth_header(verantwortlicher_token)
|
|
)
|
|
|
|
historie = await client.get(
|
|
f"/api/v1/historie?entitaet_typ=mangel&entitaet_id={mangel_id}", headers=auth_header(verantwortlicher_token)
|
|
)
|
|
ereignistypen = [e["ereignistyp"] for e in historie.json()]
|
|
assert "mangel_gemeldet" in ereignistypen
|
|
assert "mangel_status_geaendert" in ereignistypen
|